Comparing version 0.0.1-alpha7.0 to 0.0.1-alpha8.3
@@ -0,1 +1,3 @@ | ||
// @flow | ||
// This script detects if you're running on Node v8 or above; if so it runs the | ||
@@ -2,0 +4,0 @@ // code directly, otherwise it falls back to the babel-compiled version |
145
lib/index.js
"use strict"; | ||
// Full credit: https://raw.githubusercontent.com/postgraphql/postgraphql/master/src/postgres/utils/sql.ts | ||
var isString = require("lodash/isString"); | ||
var isNumber = require("lodash/isNumber"); | ||
var isBoolean = require("lodash/isBoolean"); | ||
var isSymbol = require("lodash/isSymbol"); | ||
@@ -15,19 +12,51 @@ var isNil = require("lodash/isNil"); | ||
var debugError = function debugError(err) { | ||
function debugError(err) { | ||
debug(err); | ||
return err; | ||
}; | ||
} | ||
var $$type = Symbol("type"); | ||
function makeNode(type, obj) { | ||
var newObj = Object.assign({}, obj); | ||
Object.defineProperty(newObj, $$type, { | ||
var $$trusted = Symbol("trusted"); | ||
/*:: | ||
type SQLRawNode = { | ||
text: string, | ||
type: 'RAW', | ||
[Symbol]: true, | ||
} | ||
type SQLIdentifierNode = { | ||
names: Array<mixed>, | ||
type: 'IDENTIFIER', | ||
[Symbol]: true, | ||
} | ||
type SQLValueNode = { | ||
value: mixed, | ||
type: 'VALUE', | ||
[Symbol]: true, | ||
} | ||
type SQLNode = SQLRawNode | SQLValueNode | SQLIdentifierNode | ||
*/ | ||
function makeTrustedNode /*:: <Node>*/(node /*: Node */) /*: Node */{ | ||
Object.defineProperty(node, $$trusted, { | ||
enumerable: false, | ||
configurable: false, | ||
value: type | ||
value: true | ||
}); | ||
return newObj; | ||
return node; | ||
} | ||
var ensureNonEmptyArray = function ensureNonEmptyArray(array) { | ||
function makeRawNode(text /*: string */) /*: SQLRawNode */{ | ||
return makeTrustedNode({ type: "RAW", text }); | ||
} | ||
function makeIdentifierNode(names /*: Array<mixed> */ | ||
) /*: SQLIdentifierNode */{ | ||
return makeTrustedNode({ type: "IDENTIFIER", names }); | ||
} | ||
function makeValueNode(value /*: mixed */) /*: SQLValueNode */{ | ||
return makeTrustedNode({ type: "VALUE", value }); | ||
} | ||
function ensureNonEmptyArray(array) { | ||
var allowZeroLength = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
@@ -43,9 +72,9 @@ | ||
if (entry == null) { | ||
throw debugError(new Error(`Array index ${idx} is ${entry}`)); | ||
throw debugError(new Error(`Array index ${idx} is ${String(entry)}`)); | ||
} | ||
}); | ||
return array; | ||
}; | ||
} | ||
function compile(sql) { | ||
function compile(sql /*: Array<SQLNode> */) { | ||
// Join this to generate the SQL query | ||
@@ -75,3 +104,6 @@ var sqlFragments = []; | ||
switch (item[$$type]) { | ||
if (!item[$$trusted]) { | ||
throw new Error(`Expecte sql item, instead got '${String(item)}'.`); | ||
} | ||
switch (item.type) { | ||
case "RAW": | ||
@@ -83,7 +115,11 @@ sqlFragments.push(item.text); | ||
sqlFragments.push(item.names.map(function (name) { | ||
if (typeof name === "string") return escapeSqlIdentifier(name); | ||
if (!isSymbol(name)) { | ||
throw debugError(new Error(`Expected string or symbol, received '${name}'`)); | ||
sqlFragments.push(item.names.map(function (rawName) { | ||
if (typeof rawName === "string") { | ||
var _name /*: string */ = rawName; | ||
return escapeSqlIdentifier(_name); | ||
} | ||
if (!isSymbol(rawName)) { | ||
throw debugError(new Error(`Expected string or symbol, received '${String(rawName)}'`)); | ||
} | ||
var name /*: Symbol */ = /*:: (*/rawName /*: any) */; | ||
@@ -109,3 +145,2 @@ // Get the correct identifier string for this symbol. | ||
default: | ||
throw new Error(`Unexpected Sql item type '${item[$$type]}' (full item: '${item}').`); | ||
} | ||
@@ -143,3 +178,3 @@ } | ||
*/ | ||
var query = function query(strings) { | ||
function query(strings /*: mixed */) /*: Array<mixed> */{ | ||
for (var _len = arguments.length, values = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
@@ -149,5 +184,11 @@ values[_key - 1] = arguments[_key]; | ||
if (!Array.isArray(strings)) { | ||
throw new Error("sql.query should be used as a template literal, not a function call!"); | ||
} | ||
return strings.reduce(function (items, text, i) { | ||
if (typeof text !== "string") { | ||
throw new Error("sql.query should be used as a template literal, not a function call."); | ||
} | ||
if (!values[i]) { | ||
return items.concat(makeNode("RAW", { text })); | ||
return items.concat(makeRawNode(text)); | ||
} else { | ||
@@ -160,15 +201,15 @@ var _value = values[i]; | ||
if (!Array.isArray(_value) && !isPlainObject(_value)) { | ||
if (isString(_value)) { | ||
throw new Error(`Raw string passed into SQL query: '${_value}'.`); | ||
} else if (isNumber(_value)) { | ||
throw new Error(`Raw number passed into SQL query: '${_value}'.`); | ||
if (typeof _value === "string") { | ||
throw new Error(`Raw string passed into SQL query: '${String(_value)}'.`); | ||
} else if (typeof _value === "number") { | ||
throw new Error(`Raw number passed into SQL query: '${String(_value)}'.`); | ||
} else { | ||
throw new Error(`Invalid raw value passed into SQL query: '${_value}'.`); | ||
throw new Error(`Invalid raw value passed into SQL query: '${String(_value)}'.`); | ||
} | ||
} | ||
} | ||
return items.concat(makeNode("RAW", { text }), _value); | ||
return items.concat(makeRawNode(text), _value); | ||
} | ||
}, []); | ||
}; | ||
} | ||
@@ -180,4 +221,4 @@ /** | ||
*/ | ||
var raw = function raw(text) { | ||
return makeNode("RAW", { text }); | ||
var raw = function raw(text /*: mixed */) { | ||
return makeRawNode(String(text)); | ||
}; | ||
@@ -195,5 +236,4 @@ | ||
return makeNode("IDENTIFIER", { | ||
names: ensureNonEmptyArray(names) | ||
}); | ||
return (/*: Array<mixed> */makeIdentifierNode(ensureNonEmptyArray(names)) | ||
); | ||
}; | ||
@@ -205,4 +245,4 @@ | ||
*/ | ||
var value = function value(val) { | ||
return makeNode("VALUE", { value: val }); | ||
var value = function value(val /*: mixed */) { | ||
return makeValueNode(val); | ||
}; | ||
@@ -214,6 +254,6 @@ | ||
*/ | ||
var literal = function literal(val) { | ||
if (isString(val) && val.match(/^[a-zA-Z0-9_-]*$/)) { | ||
var literal = function literal(val /*: mixed */) { | ||
if (typeof val === "string" && val.match(/^[a-zA-Z0-9_-]*$/)) { | ||
return raw(`'${val}'`); | ||
} else if (lodashIsFinite(val)) { | ||
} else if (typeof val === "number" && lodashIsFinite(val)) { | ||
if (Number.isInteger(val)) { | ||
@@ -224,3 +264,3 @@ return raw(String(val)); | ||
} | ||
} else if (isBoolean(val)) { | ||
} else if (typeof val === "boolean") { | ||
if (val) { | ||
@@ -234,3 +274,3 @@ return raw(`TRUE`); | ||
} else { | ||
return makeNode("VALUE", { value: val }); | ||
return makeValueNode(val); | ||
} | ||
@@ -243,9 +283,18 @@ }; | ||
*/ | ||
var join = function join(items) { | ||
var seperator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ""; | ||
var join = function join(rawItems /*: mixed */) { | ||
var rawSeparator /*: mixed */ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ""; | ||
if (!Array.isArray(rawItems)) { | ||
throw new Error("Items to join must be an array"); | ||
} | ||
var items = rawItems; | ||
if (typeof rawSeparator !== "string") { | ||
throw new Error("Invalid separator - must be a string"); | ||
} | ||
var separator = rawSeparator; | ||
return ensureNonEmptyArray(items, true).reduce(function (currentItems, item, i) { | ||
if (i === 0 || !seperator) { | ||
if (i === 0 || !separator) { | ||
return currentItems.concat(item); | ||
} else { | ||
return currentItems.concat(makeNode("RAW", { text: seperator }), item); | ||
return currentItems.concat(makeRawNode(separator), item); | ||
} | ||
@@ -257,3 +306,3 @@ }, []); | ||
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c | ||
var escapeSqlIdentifier = function escapeSqlIdentifier(str) { | ||
function escapeSqlIdentifier(str) { | ||
var escaped = '"'; | ||
@@ -273,3 +322,3 @@ | ||
return escaped; | ||
}; | ||
} | ||
@@ -276,0 +325,0 @@ exports.query = query; |
{ | ||
"name": "pg-sql2", | ||
"version": "0.0.1-alpha7.0", | ||
"version": "0.0.1-alpha8.3", | ||
"description": "Generate safe SQL with tagged template literals", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
158
src/index.js
@@ -0,5 +1,4 @@ | ||
// @flow | ||
// Full credit: https://raw.githubusercontent.com/postgraphql/postgraphql/master/src/postgres/utils/sql.ts | ||
const isString = require("lodash/isString"); | ||
const isNumber = require("lodash/isNumber"); | ||
const isBoolean = require("lodash/isBoolean"); | ||
const isSymbol = require("lodash/isSymbol"); | ||
@@ -13,19 +12,52 @@ const isNil = require("lodash/isNil"); | ||
const debugError = err => { | ||
function debugError(err) { | ||
debug(err); | ||
return err; | ||
}; | ||
} | ||
const $$type = Symbol("type"); | ||
function makeNode(type, obj) { | ||
const newObj = Object.assign({}, obj); | ||
Object.defineProperty(newObj, $$type, { | ||
const $$trusted = Symbol("trusted"); | ||
/*:: | ||
type SQLRawNode = { | ||
text: string, | ||
type: 'RAW', | ||
[Symbol]: true, | ||
} | ||
type SQLIdentifierNode = { | ||
names: Array<mixed>, | ||
type: 'IDENTIFIER', | ||
[Symbol]: true, | ||
} | ||
type SQLValueNode = { | ||
value: mixed, | ||
type: 'VALUE', | ||
[Symbol]: true, | ||
} | ||
type SQLNode = SQLRawNode | SQLValueNode | SQLIdentifierNode | ||
*/ | ||
function makeTrustedNode /*:: <Node>*/(node /*: Node */) /*: Node */ { | ||
Object.defineProperty(node, $$trusted, { | ||
enumerable: false, | ||
configurable: false, | ||
value: type, | ||
value: true, | ||
}); | ||
return newObj; | ||
return node; | ||
} | ||
const ensureNonEmptyArray = (array, allowZeroLength = false) => { | ||
function makeRawNode(text /*: string */) /*: SQLRawNode */ { | ||
return makeTrustedNode({ type: "RAW", text }); | ||
} | ||
function makeIdentifierNode( | ||
names /*: Array<mixed> */ | ||
) /*: SQLIdentifierNode */ { | ||
return makeTrustedNode({ type: "IDENTIFIER", names }); | ||
} | ||
function makeValueNode(value /*: mixed */) /*: SQLValueNode */ { | ||
return makeTrustedNode({ type: "VALUE", value }); | ||
} | ||
function ensureNonEmptyArray(array, allowZeroLength = false) { | ||
if (!Array.isArray(array)) { | ||
@@ -39,9 +71,9 @@ throw debugError(new Error("Expected array")); | ||
if (entry == null) { | ||
throw debugError(new Error(`Array index ${idx} is ${entry}`)); | ||
throw debugError(new Error(`Array index ${idx} is ${String(entry)}`)); | ||
} | ||
}); | ||
return array; | ||
}; | ||
} | ||
function compile(sql) { | ||
function compile(sql /*: Array<SQLNode> */) { | ||
// Join this to generate the SQL query | ||
@@ -64,3 +96,6 @@ const sqlFragments = []; | ||
for (const item of items) { | ||
switch (item[$$type]) { | ||
if (!item[$$trusted]) { | ||
throw new Error(`Expecte sql item, instead got '${String(item)}'.`); | ||
} | ||
switch (item.type) { | ||
case "RAW": | ||
@@ -75,9 +110,15 @@ sqlFragments.push(item.text); | ||
item.names | ||
.map(name => { | ||
if (typeof name === "string") return escapeSqlIdentifier(name); | ||
if (!isSymbol(name)) { | ||
.map(rawName => { | ||
if (typeof rawName === "string") { | ||
const name /*: string */ = rawName; | ||
return escapeSqlIdentifier(name); | ||
} | ||
if (!isSymbol(rawName)) { | ||
throw debugError( | ||
new Error(`Expected string or symbol, received '${name}'`) | ||
new Error( | ||
`Expected string or symbol, received '${String(rawName)}'` | ||
) | ||
); | ||
} | ||
const name /*: Symbol */ = /*:: (*/ rawName /*: any) */; | ||
@@ -105,5 +146,2 @@ // Get the correct identifier string for this symbol. | ||
default: | ||
throw new Error( | ||
`Unexpected Sql item type '${item[$$type]}' (full item: '${item}').` | ||
); | ||
} | ||
@@ -127,6 +165,16 @@ } | ||
*/ | ||
const query = (strings, ...values) => | ||
strings.reduce((items, text, i) => { | ||
function query(strings /*: mixed */, ...values /*: Array<mixed> */) { | ||
if (!Array.isArray(strings)) { | ||
throw new Error( | ||
"sql.query should be used as a template literal, not a function call!" | ||
); | ||
} | ||
return strings.reduce((items, text, i) => { | ||
if (typeof text !== "string") { | ||
throw new Error( | ||
"sql.query should be used as a template literal, not a function call." | ||
); | ||
} | ||
if (!values[i]) { | ||
return items.concat(makeNode("RAW", { text })); | ||
return items.concat(makeRawNode(text)); | ||
} else { | ||
@@ -139,9 +187,13 @@ const value = values[i]; | ||
if (!Array.isArray(value) && !isPlainObject(value)) { | ||
if (isString(value)) { | ||
throw new Error(`Raw string passed into SQL query: '${value}'.`); | ||
} else if (isNumber(value)) { | ||
throw new Error(`Raw number passed into SQL query: '${value}'.`); | ||
if (typeof value === "string") { | ||
throw new Error( | ||
`Raw string passed into SQL query: '${String(value)}'.` | ||
); | ||
} else if (typeof value === "number") { | ||
throw new Error( | ||
`Raw number passed into SQL query: '${String(value)}'.` | ||
); | ||
} else { | ||
throw new Error( | ||
`Invalid raw value passed into SQL query: '${value}'.` | ||
`Invalid raw value passed into SQL query: '${String(value)}'.` | ||
); | ||
@@ -151,5 +203,6 @@ } | ||
} | ||
return items.concat(makeNode("RAW", { text }), value); | ||
return items.concat(makeRawNode(text), value); | ||
} | ||
}, []); | ||
} | ||
@@ -161,3 +214,3 @@ /** | ||
*/ | ||
const raw = text => makeNode("RAW", { text }); | ||
const raw = (text /*: mixed */) => makeRawNode(String(text)); | ||
@@ -169,6 +222,4 @@ /** | ||
*/ | ||
const identifier = (...names) => | ||
makeNode("IDENTIFIER", { | ||
names: ensureNonEmptyArray(names), | ||
}); | ||
const identifier = (...names /*: Array<mixed> */) => | ||
makeIdentifierNode(ensureNonEmptyArray(names)); | ||
@@ -179,3 +230,3 @@ /** | ||
*/ | ||
const value = val => makeNode("VALUE", { value: val }); | ||
const value = (val /*: mixed */) => makeValueNode(val); | ||
@@ -186,6 +237,6 @@ /** | ||
*/ | ||
const literal = val => { | ||
if (isString(val) && val.match(/^[a-zA-Z0-9_-]*$/)) { | ||
const literal = (val /*: mixed */) => { | ||
if (typeof val === "string" && val.match(/^[a-zA-Z0-9_-]*$/)) { | ||
return raw(`'${val}'`); | ||
} else if (lodashIsFinite(val)) { | ||
} else if (typeof val === "number" && lodashIsFinite(val)) { | ||
if (Number.isInteger(val)) { | ||
@@ -196,3 +247,3 @@ return raw(String(val)); | ||
} | ||
} else if (isBoolean(val)) { | ||
} else if (typeof val === "boolean") { | ||
if (val) { | ||
@@ -206,3 +257,3 @@ return raw(`TRUE`); | ||
} else { | ||
return makeNode("VALUE", { value: val }); | ||
return makeValueNode(val); | ||
} | ||
@@ -215,14 +266,23 @@ }; | ||
*/ | ||
const join = (items, seperator = "") => | ||
ensureNonEmptyArray(items, true).reduce((currentItems, item, i) => { | ||
if (i === 0 || !seperator) { | ||
const join = (rawItems /*: mixed */, rawSeparator /*: mixed */ = "") => { | ||
if (!Array.isArray(rawItems)) { | ||
throw new Error("Items to join must be an array"); | ||
} | ||
const items = rawItems; | ||
if (typeof rawSeparator !== "string") { | ||
throw new Error("Invalid separator - must be a string"); | ||
} | ||
const separator = rawSeparator; | ||
return ensureNonEmptyArray(items, true).reduce((currentItems, item, i) => { | ||
if (i === 0 || !separator) { | ||
return currentItems.concat(item); | ||
} else { | ||
return currentItems.concat(makeNode("RAW", { text: seperator }), item); | ||
return currentItems.concat(makeRawNode(separator), item); | ||
} | ||
}, []); | ||
}; | ||
// Copied from https://github.com/brianc/node-postgres/blob/860cccd53105f7bc32fed8b1de69805f0ecd12eb/lib/client.js#L285-L302 | ||
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c | ||
const escapeSqlIdentifier = function(str) { | ||
function escapeSqlIdentifier(str) { | ||
var escaped = '"'; | ||
@@ -242,3 +302,3 @@ | ||
return escaped; | ||
}; | ||
} | ||
@@ -245,0 +305,0 @@ exports.query = query; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
26370
557