objection
Advanced tools
Comparing version 0.8.0-rc.1 to 0.8.0-rc.2
@@ -7,4 +7,4 @@ 'use strict'; | ||
const idLengthLimit = 63; | ||
const relationRecursionLimit = 64; | ||
const ID_LENGTH_LIMIT = 63; | ||
const RELATION_RECURSION_LIMIT = 64; | ||
@@ -80,3 +80,3 @@ class RelationJoinBuilder { | ||
expr: this.expression, | ||
builder: builder, | ||
builder, | ||
modelClass: builder.modelClass(), | ||
@@ -99,3 +99,3 @@ joinOperation: this.opt.joinOperation || 'leftJoin', | ||
expr: this.expression, | ||
builder: builder, | ||
builder, | ||
modelClass: builder.modelClass(), | ||
@@ -130,2 +130,3 @@ joinOperation: this.opt.joinOperation || 'leftJoin', | ||
const id = pInfo.idGetter(row); | ||
let model; | ||
@@ -146,3 +147,3 @@ if (!id) { | ||
let model = pInfo.getModelFromBranch(curBranch, id); | ||
model = pInfo.getModelFromBranch(curBranch, id); | ||
@@ -174,7 +175,3 @@ if (!model) { | ||
if (!pInfo.omitCols[col]) { | ||
keyInfo.push({ | ||
pInfo: pInfo, | ||
key: key, | ||
col: col | ||
}); | ||
keyInfo.push({pInfo, key, col}); | ||
} | ||
@@ -188,7 +185,3 @@ } else { | ||
if (!pInfo.omitCols[col]) { | ||
keyInfo.push({ | ||
pInfo, | ||
key, | ||
col | ||
}); | ||
keyInfo.push({pInfo, key, col}); | ||
} | ||
@@ -268,4 +261,4 @@ } | ||
const filterQuery = createFilterQuery({ | ||
builder: builder, | ||
relation: relation, | ||
builder, | ||
relation, | ||
expr: childExpr | ||
@@ -275,4 +268,4 @@ }); | ||
const relatedJoinSelectQuery = createRelatedJoinFromQuery({ | ||
filterQuery: filterQuery, | ||
relation: relation, | ||
filterQuery, | ||
relation, | ||
allRelations: this.allRelations | ||
@@ -291,3 +284,3 @@ }); | ||
// to the createFilterQuery function because relatedJoinSelectQuery is cloned | ||
// From the return value of that function and we don't want relation.modify | ||
// from the return value of that function and we don't want relation.modify | ||
// to be called twice for it. | ||
@@ -350,13 +343,26 @@ filterQuery.modify(relation.modify); | ||
const rootTable = this.rootModelClass.tableName; | ||
const columns = RelationJoinBuilder.columnInfo[modelClass.tableName].columns; | ||
RelationJoinBuilder.columnInfo[modelClass.tableName].columns.forEach(col => { | ||
for (let i = 0, l = columns.length; i < l; ++i) { | ||
const col = columns[i]; | ||
const filterPassed = selectFilter(col); | ||
const isIdColumn = idCols.indexOf(col) !== -1; | ||
// This needs to be var instead of let or const to prevent an optimization | ||
// bailout because of "Unsupported phi use of const or let variable". | ||
var isIdColumn = idCols.indexOf(col) !== -1; | ||
if (filterPassed || isIdColumn) { | ||
selects.push({ | ||
col: `${info.encPath || rootTable}.${col}`, | ||
alias: this.joinPath(info.encPath, col) | ||
}); | ||
const fullCol = `${info.encPath || rootTable}.${col}`; | ||
if (!builder.hasSelection(fullCol, true)) { | ||
const alias = this.joinPath(info.encPath, col); | ||
if (alias.length > ID_LENGTH_LIMIT) { | ||
throw new ValidationError({ | ||
eager: `identifier ${alias} is over ${ID_LENGTH_LIMIT} characters long and would be truncated by the database engine.` | ||
}); | ||
} | ||
selects.push(`${fullCol} as ${alias}`); | ||
} | ||
if (!filterPassed) { | ||
@@ -366,3 +372,3 @@ info.omitCols[col] = true; | ||
} | ||
}); | ||
} | ||
@@ -372,25 +378,25 @@ if (relation && relation.joinTableExtras) { | ||
relation.joinTableExtras.forEach(extra => { | ||
if (selectFilter(extra.joinTableCol)) { | ||
selects.push({ | ||
col: `${joinTable}.${extra.joinTableCol}`, | ||
alias: this.joinPath(info.encPath, extra.aliasCol) | ||
}); | ||
} | ||
}); | ||
} | ||
for (let i = 0, l = relation.joinTableExtras.length; i < l; ++i) { | ||
const extra = relation.joinTableExtras[i]; | ||
const filterPassed = selectFilter(extra.joinTableCol); | ||
const tooLongAliases = selects.filter(select => select.alias.length > idLengthLimit); | ||
if (filterPassed) { | ||
const fullCol = `${joinTable}.${extra.joinTableCol}`; | ||
if (tooLongAliases.length) { | ||
throw new ValidationError({ | ||
eager: `identifier ${tooLongAliases[0].alias} is over ${idLengthLimit} characters long ` | ||
+ `and would be truncated by the database engine.` | ||
}); | ||
if (!builder.hasSelection(fullCol, true)) { | ||
const alias = this.joinPath(info.encPath, extra.aliasCol); | ||
if (alias.length > ID_LENGTH_LIMIT) { | ||
throw new ValidationError({ | ||
eager: `identifier ${alias} is over ${ID_LENGTH_LIMIT} characters long and would be truncated by the database engine.` | ||
}); | ||
} | ||
selects.push(`${fullCol} as ${alias}`); | ||
} | ||
} | ||
} | ||
} | ||
builder.select(selects | ||
.filter(select => !builder.hasSelection(select.col, true)) | ||
.map(select => `${select.col} as ${select.alias}`) | ||
); | ||
builder.select(selects); | ||
} | ||
@@ -506,3 +512,3 @@ | ||
if (expr.isAllRecursive() || expr.maxRecursionDepth() > relationRecursionLimit) { | ||
if (expr.isAllRecursive() || expr.maxRecursionDepth() > RELATION_RECURSION_LIMIT) { | ||
throw new ValidationError({ | ||
@@ -509,0 +515,0 @@ eager: `recursion depth of eager expression ${expr.toString()} too big for JoinEagerAlgorithm` |
@@ -64,3 +64,6 @@ 'use strict'; | ||
const addedSelects = {}; | ||
let cols; | ||
// Collect columns that need to be selected for the eager fetch | ||
// to work that are not currently selected. | ||
for (let i = 0, l = this.relationsToFetch.length; i < l; ++i) { | ||
@@ -80,3 +83,5 @@ const relation = this.relationsToFetch[i].relation; | ||
const cols = Object.keys(addedSelects); | ||
// Don't move the `let` or `const` here to prevent an optimization | ||
// bailout because of "Unsupported phi use of const or let variable". | ||
cols = Object.keys(addedSelects); | ||
@@ -83,0 +88,0 @@ if (cols.length) { |
@@ -43,3 +43,3 @@ 'use strict'; | ||
deep: this.splitQueryPropsDeep, | ||
json: json | ||
json | ||
}); | ||
@@ -46,0 +46,0 @@ |
@@ -6,2 +6,5 @@ 'use strict'; | ||
const ALIAS_REGEX = /\s+as\s+/i; | ||
const COUNT_REGEX = /count/i; | ||
class SelectOperation extends WrappingQueryBuilderOperation { | ||
@@ -11,6 +14,10 @@ | ||
super(name, opt); | ||
this.selections = []; | ||
this.hasCache = Object.create(null); | ||
} | ||
static parseSelection(selection) { | ||
let dotIdx; | ||
if (typeof selection !== 'string') { | ||
@@ -21,4 +28,7 @@ return null; | ||
// Discard the possible alias. | ||
selection = selection.split(/\s+as\s+/i)[0].trim(); | ||
const dotIdx = selection.lastIndexOf('.'); | ||
if (ALIAS_REGEX.test(selection)) { | ||
selection = selection.split(ALIAS_REGEX)[0].trim(); | ||
} | ||
dotIdx = selection.lastIndexOf('.'); | ||
@@ -43,3 +53,3 @@ if (dotIdx !== -1) { | ||
// etc. because knex apparently supports it. | ||
if (selections.length === 0 && !/count/i.test(this.name)) { | ||
if (selections.length === 0 && !COUNT_REGEX.test(this.name)) { | ||
return false; | ||
@@ -46,0 +56,0 @@ } |
@@ -36,3 +36,6 @@ 'use strict'; | ||
let sql = '('; | ||
// Needs to be var instead of let to prevent a weird | ||
// optimization bailout. | ||
var sql = '('; | ||
for (let i = 0, l = columns.length; i < l; ++i) { | ||
@@ -45,2 +48,3 @@ sql += formatter.wrap(columns[i]); | ||
} | ||
sql += ')'; | ||
@@ -52,3 +56,3 @@ | ||
buildNonComposite(knexBuilder, columns, values) { | ||
let col = (typeof columns === 'string') ? columns : columns[0]; | ||
const col = (typeof columns === 'string') ? columns : columns[0]; | ||
@@ -55,0 +59,0 @@ if (Array.isArray(values)) { |
'use strict'; | ||
const memoize = require('lodash').memoize; | ||
const QueryBuilderOperation = require('./QueryBuilderOperation'); | ||
const isKnexQueryBuilder = require('../../utils/knexUtils').isKnexQueryBuilder; | ||
const isKnexJoinBuilder = require('../../utils/knexUtils').isKnexJoinBuilder; | ||
const isKnexRaw = require('../../utils/knexUtils').isKnexRaw; | ||
let QueryBuilderBase = null; | ||
let JoinBuilder = null; | ||
const getQueryBuilderBase = memoize(() => require('../QueryBuilderBase')); | ||
const getJoinBuilder = memoize(() => require('../JoinBuilder')); | ||
@@ -18,5 +20,4 @@ class WrappingQueryBuilderOperation extends QueryBuilderOperation { | ||
call(builder, args) { | ||
const ret = wrapArgs(this, builder, args); | ||
this.args = args; | ||
return ret; | ||
this.args = wrapArgs(this, builder, args); | ||
return this.args !== null; | ||
} | ||
@@ -26,7 +27,5 @@ } | ||
function wrapArgs(op, builder, args) { | ||
// Preventing cyclic deps. | ||
QueryBuilderBase = QueryBuilderBase || requireQueryBuilderBase(); | ||
const skipUndefined = builder.internalOptions().skipUndefined; | ||
const knex = builder.knex(); | ||
const out = new Array(args.length); | ||
@@ -36,64 +35,70 @@ for (let i = 0, l = args.length; i < l; ++i) { | ||
if (arg === undefined) { | ||
if (skipUndefined) { | ||
return false; | ||
} else { | ||
throw new Error(`undefined passed as argument #${l} for '${op.name}' operation. Call skipUndefined() method to ignore the undefined values.`); | ||
} | ||
} else if (arg && arg.isObjectionReferenceBuilder) { | ||
args[i] = knex.raw.apply(knex, args[i].toRawArgs()); | ||
} else if (arg && arg.isObjectionQueryBuilderBase) { | ||
// Convert QueryBuilderBase instances into knex query builders. | ||
args[i] = arg.build(); | ||
} else if (Array.isArray(arg)) { | ||
if (skipUndefined) { | ||
args[i] = withoutUndefined(arg); | ||
} else if (includesUndefined(arg)) { | ||
throw new Error(`undefined passed as an item in argument #${l} for '${op.name}' operation. Call skipUndefined() method to ignore the undefined values.`); | ||
} | ||
// convert reference builders to knex.raw | ||
args[i] = args[i].map(arg => { | ||
return (arg && arg.isObjectionReferenceBuilder) ? knex.raw.apply(knex, arg.toRawArgs()) : arg; | ||
}); | ||
} else if (typeof arg === 'function') { | ||
// If an argument is a function, knex calls it with a query builder as | ||
// first argument (and as `this` context). We wrap the query builder into | ||
// a QueryBuilderBase instance. | ||
args[i] = wrapFunctionArg(arg, knex); | ||
if (isUndefined(arg)) { | ||
return wrapUndefined(i, op, skipUndefined); | ||
} else if (isObjectionReferenceBuilder(arg)) { | ||
out[i] = wrapReferenceBuilder(arg, knex, skipUndefined); | ||
} else if (isObjectionQueryBuilderBase(arg)) { | ||
out[i] = wrapQueryBuilderBase(arg); | ||
} else if (isArray(arg)) { | ||
out[i] = wrapArray(arg, knex, i, op, skipUndefined); | ||
} else if (isFunction(arg)) { | ||
out[i] = wrapFunction(arg, knex); | ||
} else if (isPlainObject(arg)) { | ||
out[i] = wrapObject(arg, knex, i, op, skipUndefined); | ||
} else { | ||
out[i] = arg; | ||
} | ||
} | ||
return true; | ||
return out; | ||
} | ||
function wrapFunctionArg(func, knex) { | ||
// Preventing cyclic deps. | ||
QueryBuilderBase = QueryBuilderBase || requireQueryBuilderBase(); | ||
JoinBuilder = JoinBuilder || requireJoinBuilder(); | ||
function isUndefined(arg) { | ||
return arg === undefined; | ||
} | ||
return function wrappedKnexFunctionArg() { | ||
if (isKnexQueryBuilder(this)) { | ||
const knexQueryBuilder = this; | ||
const wrappedQueryBuilder = new QueryBuilderBase(knex); | ||
function wrapUndefined(i, op, skipUndefined) { | ||
if (skipUndefined) { | ||
return null; | ||
} else { | ||
throw new Error(`undefined passed as argument #${i} for '${op.name}' operation. Call skipUndefined() method to ignore the undefined values.`); | ||
} | ||
} | ||
func.call(wrappedQueryBuilder, wrappedQueryBuilder); | ||
wrappedQueryBuilder.buildInto(knexQueryBuilder); | ||
} else if (isKnexJoinBuilder(this)) { | ||
const knexQueryBuilder = this; | ||
const joinClauseBuilder = new JoinBuilder(knex); | ||
function isObjectionReferenceBuilder(item) { | ||
return item !== null && item.isObjectionReferenceBuilder === true; | ||
} | ||
func.call(joinClauseBuilder, joinClauseBuilder); | ||
joinClauseBuilder.buildInto(knexQueryBuilder); | ||
} else { | ||
return func.apply(this, arguments); | ||
} | ||
}; | ||
function wrapReferenceBuilder(builder, knex) { | ||
return knex.raw.apply(knex, builder.toRawArgs()); | ||
} | ||
function withoutUndefined(arr) { | ||
function isObjectionQueryBuilderBase(item) { | ||
return item !== null && item.isObjectionQueryBuilderBase === true; | ||
} | ||
function wrapQueryBuilderBase(builder) { | ||
return builder.build(); | ||
} | ||
function isArray(arg) { | ||
return Array.isArray(arg); | ||
} | ||
function wrapArray(arr, knex, i, op, skipUndefined) { | ||
const out = []; | ||
for (let i = 0, l = arr.length; i < l; ++i) { | ||
if (arr[i] !== undefined) { | ||
out.push(arr[i]); | ||
for (let j = 0, l = arr.length; j < l; ++j) { | ||
const item = arr[j]; | ||
if (item === undefined) { | ||
if (!skipUndefined) { | ||
throw new Error(`undefined passed as an item in argument #${i} for '${op.name}' operation. Call skipUndefined() method to ignore the undefined values.`); | ||
} | ||
} else if (isObjectionReferenceBuilder(item)) { | ||
out.push(wrapReferenceBuilder(item, knex)); | ||
} else if (isObjectionQueryBuilderBase(item)) { | ||
out.push(wrapQueryBuilderBase(item)); | ||
} else { | ||
out.push(item); | ||
} | ||
@@ -105,20 +110,65 @@ } | ||
function includesUndefined(arr) { | ||
for (let i = 0, l = arr.length; i < l; ++i) { | ||
if (arr[i] === undefined) { | ||
return true; | ||
function isFunction(arg) { | ||
return typeof arg === 'function'; | ||
} | ||
function wrapFunction(func, knex) { | ||
return function wrappedKnexFunctionArg() { | ||
if (isKnexQueryBuilder(this)) { | ||
wrapQueryBuilderFunction(this, func, knex) | ||
} else if (isKnexJoinBuilder(this)) { | ||
wrapJoinBuilderFunction(this, func, knex); | ||
} else { | ||
return func.apply(this, arguments); | ||
} | ||
} | ||
}; | ||
} | ||
return false; | ||
function wrapQueryBuilderFunction(knexQueryBuilder, func, knex) { | ||
const QueryBuilderBase = getQueryBuilderBase(); | ||
const wrappedQueryBuilder = new QueryBuilderBase(knex); | ||
func.call(wrappedQueryBuilder, wrappedQueryBuilder); | ||
wrappedQueryBuilder.buildInto(knexQueryBuilder); | ||
} | ||
function requireQueryBuilderBase() { | ||
return require('../QueryBuilderBase'); | ||
function wrapJoinBuilderFunction(knexJoinBuilder, func, knex) { | ||
const JoinBuilder = getJoinBuilder(); | ||
const joinClauseBuilder = new JoinBuilder(knex); | ||
func.call(joinClauseBuilder, joinClauseBuilder); | ||
joinClauseBuilder.buildInto(knexJoinBuilder); | ||
} | ||
function requireJoinBuilder() { | ||
return require('../JoinBuilder'); | ||
function isPlainObject(arg) { | ||
return arg !== null | ||
&& typeof arg === 'object' | ||
&& (!arg.constructor || arg.constructor === Object) | ||
&& (!arg.toString || arg.toString === Object.prototype.toString) | ||
} | ||
function wrapObject(obj, knex, i, op, skipUndefined) { | ||
const out = {}; | ||
const keys = Object.keys(obj); | ||
for (let j = 0, l = keys.length; j < l; ++j) { | ||
const key = keys[j]; | ||
const item = obj[key]; | ||
if (item === undefined) { | ||
if (!skipUndefined) { | ||
throw new Error(`undefined passed as an item in argument #${i} for '${op.name}' operation. Call skipUndefined() method to ignore the undefined values.`); | ||
} | ||
} else if (isObjectionReferenceBuilder(item)) { | ||
out[key] = wrapReferenceBuilder(item, knex); | ||
} else if (isObjectionQueryBuilderBase(item)) { | ||
out[key] = wrapQueryBuilderBase(item); | ||
} else { | ||
out[key] = item; | ||
} | ||
} | ||
return out; | ||
} | ||
module.exports = WrappingQueryBuilderOperation; |
@@ -445,3 +445,3 @@ 'use strict'; | ||
const table = this.modelClass().tableName; | ||
const table = this._modelClass.tableName; | ||
let noSelectStatements = true; | ||
@@ -452,3 +452,3 @@ | ||
if (op instanceof SelectOperation) { | ||
if (op.constructor === SelectOperation) { | ||
noSelectStatements = false; | ||
@@ -839,4 +839,4 @@ | ||
function build(builder) { | ||
let context = builder.context() || {}; | ||
let internalContext = builder.internalContext(); | ||
const context = builder.context() || {}; | ||
const internalContext = builder.internalContext(); | ||
let knexBuilder = builder.knex().queryBuilder(); | ||
@@ -843,0 +843,0 @@ |
@@ -49,3 +49,3 @@ 'use strict'; | ||
knex(knex) { | ||
knex() { | ||
if (arguments.length === 0) { | ||
@@ -62,3 +62,3 @@ const knex = this._context.knex || this._knex; | ||
} else { | ||
this._knex = knex; | ||
this._knex = arguments[0]; | ||
return this; | ||
@@ -115,5 +115,5 @@ } | ||
if (operationSelector instanceof RegExp) { | ||
forEachOperationRegex(this, operationSelector, callback, match); | ||
forEachOperationRegex(this._operations, operationSelector, callback, match); | ||
} else { | ||
forEachOperationInstanceOf(this, operationSelector, callback, match); | ||
forEachOperationInstanceOf(this._operations, operationSelector, callback, match); | ||
} | ||
@@ -232,5 +232,5 @@ | ||
function forEachOperationRegex(builder, operationSelector, callback, match) { | ||
for (let i = 0, l = builder._operations.length; i < l; ++i) { | ||
const op = builder._operations[i]; | ||
function forEachOperationRegex(operations, operationSelector, callback, match) { | ||
for (let i = 0, l = operations.length; i < l; ++i) { | ||
const op = operations[i]; | ||
@@ -245,5 +245,5 @@ if (operationSelector.test(op.name) === match) { | ||
function forEachOperationInstanceOf(builder, operationSelector, callback, match) { | ||
for (let i = 0, l = builder._operations.length; i < l; ++i) { | ||
const op = builder._operations[i]; | ||
function forEachOperationInstanceOf(operations, operationSelector, callback, match) { | ||
for (let i = 0, l = operations.length; i < l; ++i) { | ||
const op = operations[i]; | ||
@@ -250,0 +250,0 @@ if ((op instanceof operationSelector) === match) { |
@@ -35,3 +35,3 @@ 'use strict'; | ||
return expr; | ||
} else if (!_.isString(expr) || _.isEmpty(expr.trim())) { | ||
} else if (typeof expr !== 'string' || expr.trim().length === 0) { | ||
return new RelationExpression(); | ||
@@ -53,5 +53,5 @@ } else { | ||
return new RelationExpression(); | ||
} else { | ||
return new RelationExpression(modelGraphToNode(graph, newNode())); | ||
} | ||
return new RelationExpression(modelGraphToNode(graph, newNode())); | ||
} | ||
@@ -116,2 +116,4 @@ | ||
expr = RelationExpression.parse(expr); | ||
// Need to defined these here to prevent an optimization bailout. | ||
let maxRecursionDepth, childNames; | ||
@@ -130,3 +132,3 @@ if (this.isAllRecursive()) { | ||
const maxRecursionDepth = expr.maxRecursionDepth(); | ||
maxRecursionDepth = expr.maxRecursionDepth(); | ||
@@ -137,6 +139,8 @@ if (maxRecursionDepth > 0) { | ||
return _.every(expr.children, (child, childName) => { | ||
var ownSubExpression = this.childExpression(childName); | ||
var subExpression = expr.childExpression(childName); | ||
childNames = Object.keys(expr.children); | ||
return childNames.every(childName => { | ||
const ownSubExpression = this.childExpression(childName); | ||
const subExpression = expr.childExpression(childName); | ||
return ownSubExpression && ownSubExpression.isSubExpression(subExpression); | ||
@@ -203,4 +207,4 @@ }); | ||
addAnonymousFilterAtPath(path, filter) { | ||
let filterNodes = this._nodesAtPath(path); | ||
let filters = this.filters; | ||
const filterNodes = this.rawNodesAtPath(path); | ||
const filters = this.filters; | ||
@@ -214,33 +218,18 @@ let idx = 0; | ||
if (!_.isEmpty(filterNodes)) { | ||
if (filterNodes.length !== 0) { | ||
filters[filterName] = filter; | ||
_.each(filterNodes, node => node.args.push(filterName)); | ||
for (let i = 0, l = filterNodes.length; i < l; ++i) { | ||
filterNodes[i].args.push(filterName); | ||
} | ||
} | ||
} | ||
rawNodesAtPath(path) { | ||
return findNodesAtPath(this, RelationExpression.parse(path), []); | ||
} | ||
toString() { | ||
return toString(this); | ||
} | ||
_nodesAtPath(pathExpression) { | ||
let path = RelationExpression.parse(pathExpression); | ||
let nodes = []; | ||
RelationExpression.nodesAtPath(this, path, nodes); | ||
return nodes; | ||
} | ||
static nodesAtPath(target, path, expressions) { | ||
if (path.numChildren == 0) { | ||
expressions.push(target); | ||
} else { | ||
_.forOwn(path.children, child => { | ||
const targetChild = target.children[child.name]; | ||
if (targetChild) { | ||
this.nodesAtPath(targetChild, child, expressions); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
@@ -279,5 +268,5 @@ | ||
function toString(node) { | ||
let childExpr = _.values(node.children).map(toString); | ||
let str = node.name; | ||
@@ -290,4 +279,2 @@ if (childExpr.length > 1) { | ||
let str = node.name; | ||
if (node.args.length) { | ||
@@ -355,2 +342,23 @@ str += `(${node.args.join(', ')})`; | ||
function findNodesAtPath(target, path, results) { | ||
if (path.numChildren == 0) { | ||
// Path leaf reached, add target node to result set. | ||
results.push(target); | ||
} else { | ||
const childNames = Object.keys(path.children); | ||
for (let i = 0, l = childNames.length; i < l; ++i) { | ||
const childName = childNames[i]; | ||
const child = path.children[childName]; | ||
const targetChild = target.children[childName]; | ||
if (targetChild) { | ||
findNodesAtPath(targetChild, child, results); | ||
} | ||
} | ||
} | ||
return results; | ||
} | ||
module.exports = RelationExpression; |
{ | ||
"name": "objection", | ||
"version": "0.8.0-rc.1", | ||
"version": "0.8.0-rc.2", | ||
"description": "An SQL-friendly ORM for Node.js", | ||
@@ -5,0 +5,0 @@ "main": "lib/objection.js", |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
338651
9788
0