New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

leoric

Package Overview
Dependencies
Maintainers
1
Versions
144
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

leoric - npm Package Compare versions

Comparing version 1.11.0 to 1.11.1

10

History.md

@@ -0,1 +1,11 @@

1.11.1 / 2021-09-28
===================
This version fixes lots of issues regarding logical operator in object conditions.
* fix: logical operator with multiple conditions such as (#190)
* fix: sequelize mode support HAVING, and select fields raw sql support (#187)
* fix: support len validator (#188)
* fix: normalize logical operator conditions before formatting with spellbook (#186)
1.11.0 / 2021-09-24

@@ -2,0 +12,0 @@ ===================

2

package.json
{
"name": "leoric",
"version": "1.11.0",
"version": "1.11.1",
"description": "JavaScript Object-relational mapping alchemy",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -7,3 +7,3 @@ 'use strict';

function translateOptions(spell, options) {
const { attributes, where, group, order, offset, limit, include } = options;
const { attributes, where, group, order, offset, limit, include, having } = options;

@@ -15,3 +15,8 @@ if (attributes) spell.$select(attributes);

if (where) spell.$where(where);
if (group) spell.$group(group);
if (group) {
if (Array.isArray(group)) spell.$group(...group);
else spell.$group(group);
}
if (having) spell.$having(having);
if (order) {

@@ -18,0 +23,0 @@ if (typeof order === 'string') {

@@ -170,3 +170,3 @@ 'use strict';

const params = args.map(arg => {
return isLogicalOp(ast) && isLogicalOp(arg) && precedes(name, arg.name) <= 0
return isLogicalOp(ast) && isLogicalOp(arg) && precedes(name, arg.name) < 0
? `(${formatExpr(spell, arg)})`

@@ -207,53 +207,2 @@ : formatExpr(spell, arg);

}
}
else if (args[1].type == 'op' && Array.isArray(args[1].args) && !isLogicalOp(ast) && isLogicalOp(args[1])) {
let innerOp = args[1].name;
if ([ 'or', 'and' ].includes(innerOp)) {
// { title: { $or: [ 'Leah', 'Diablo' ] } }
// { title: { $or: [ 'Leah', { $like: '%jjj' } ] } }
const expr = [];
const leftValue = { type: 'id', value: args[0].value };
for (const arg of args[1].args) {
if (arg.type === 'literal') {
const innerAst = {
type: 'op',
name: '=',
args: [ leftValue, arg ],
};
expr.push(formatExpr(spell, innerAst));
} else {
arg.args[0] = leftValue;
expr.push(formatExpr(spell, arg));
}
}
return `(${expr.join(` ${innerOp.toUpperCase()} `)})`;
} else if ('not' === innerOp) {
// { title: { $not: [ 'Leah', 'jss' ] } }
const expr = [];
const leftValue = { type: 'id', value: args[0].value };
// if all args are literal, it should be `NOT IN (?, ?, ?)`
const notAllLiteral = args[1].args.find(arg => arg.type !== 'literal');
if (!notAllLiteral) {
const values = args[1].args.map(arg => arg.value);
return `(NOT IN (${values.map(v => {
if (v == null) return 'NULL';
return '?';
})}))`;
}
for (const arg of args[1].args) {
if (arg.type === 'literal') {
const innerAst = {
type: 'op',
name: '=',
args: [ leftValue, arg ],
};
expr.push(formatExpr(spell, innerAst));
}
else {
arg.args[0] = leftValue;
expr.push(formatExpr(spell, arg));
}
}
return `(NOT (${expr.join(' AND ')}))`;
}
} else if (params[1] !== '') {

@@ -260,0 +209,0 @@ return `${params[0]} ${name.toUpperCase()} ${params[1]}`;

@@ -51,9 +51,10 @@ 'use strict';

function parseConditions(conditions, ...values) {
if (conditions.__raw) return [ conditions ];
if (isPlainObject(conditions)) {
return parseObjectConditions(conditions);
}
else if (typeof conditions == 'string') {
} else if (typeof conditions == 'string') {
return [parseExpr(conditions, ...values)];
}
else {
} else if (Array.isArray(conditions)) {
return [parseExpr(...conditions)];
} else {
throw new Error(`unexpected conditions ${conditions}`);

@@ -65,4 +66,4 @@ }

* Parse object values as literal or subquery
* @param {*} value
* @returns {Object}
* @param {Object} value
* @returns {Array<Object>}
*/

@@ -73,6 +74,3 @@ function parseObjectValue(value) {

// value maybe an object conditions
if (isPlainObject(value)) {
const [ args ] = parseObjectConditions(value);
return args;
}
if (isPlainObject(value)) return parseObjectConditions(value);
return parseExpr('?', value);

@@ -135,6 +133,13 @@ }

function isLogicalOperator(condition) {
return LOGICAL_OPERATOR_MAP.hasOwnProperty(condition);
function isLogicalOperator(operator) {
return LOGICAL_OPERATOR_MAP.hasOwnProperty(operator);
}
function isLogicalCondition(condition) {
for (const name in condition) {
if (LOGICAL_OPERATOR_MAP.hasOwnProperty(name)) return true;
}
return false;
}
function parseLogicalObjectConditionValue(value) {

@@ -152,2 +157,9 @@ if (value == null || typeof value !== 'object') {

function combineMultipleConditions(result) {
return result.reduce((res, arg) => {
if (!res) return arg;
return { type: 'op', name: 'and', args: [ res, arg ] };
});
}
/**

@@ -157,19 +169,26 @@ * @example

* { $or: [ { title: 'Leah' }, { content: 'Diablo' } ] }
* { title: { $or: [ 'Leah', 'Diablo' ] } }
* { title: { $or: [ 'Leah', { $like: '%jjj' } ] } }
* { title: { $not: [ 'Leah', 'jss' ] } }
* @param {string} name logical operators, such as `$or`, `$and`
* { $or: [ 'Leah', 'Diablo' ] }
* { $or: [ 'Leah', { $like: '%jjj' } ] }
* { $not: [ 'Leah', 'jss' ] }
* @param {string} $op logical operators, such as `$or`, `$and`
* @param {Object|Object[]} value logical operands
*/
function parseLogicalObjectCondition(name, value) {
const operator = LOGICAL_OPERATOR_MAP[name];
function parseLogicalObjectCondition($op, value) {
const operator = LOGICAL_OPERATOR_MAP[$op];
const conditions = parseLogicalObjectConditionValue(value);
// { $not: [ 1, 2, 3 ] }
if (operator === 'not' && conditions.every(entry => !isPlainObject(entry))) {
return { type: 'op', name: 'not in', args: [ { type: 'literal', value: conditions } ] };
}
const args = conditions.reduce((res, condition) => {
if (!isPlainObject(condition)) {
return res.concat({
type: 'literal',
value: condition,
type: 'op',
name: '=',
args: [ { type: 'literal', value: condition } ],
});
}
const [ arg ] = parseObjectConditions(condition);
const arg = combineMultipleConditions(parseObjectConditions(condition));
if (res.length >= 2) {

@@ -184,5 +203,12 @@ return [ { type: 'op', name: operator, args: res }, arg ];

}
// malformed logical condition: { title: { $or: [ 'Leah' ] } }
// { title: { $or: [ 'Leah' ] } }
if (args.length === 1 && operator !== 'not') return args[0];
// { title: { $not: [ { $like: '%foo%' }, { $like: '%bar%' } ] } }
if (args.length > 1 && operator === 'not') {
const result = combineMultipleConditions(args);
return { type: 'op', name: operator, args: [ result ] };
}
return { type: 'op', name: operator, args };

@@ -192,2 +218,34 @@ }

/**
* normalize logical condition objects like below:
*
* { createdAt: { $not: { $gt: '2021-01-01', $lte: '2021-12-31' } } }
* { createdAt: { $not: [ '2021-09-30', { $gte: '2021-10-07' } ] } }
* { title: { $not: [ 'Leah', 'Nephalem' ] } }
*
* into following format
*
* { $not: [] }
* @param {Object} condition logical condition ast
* @param {string} name expression as the left operand
*/
function normalizeLogicalCondition(condition, name) {
let args;
if (condition.name === 'not') {
args = condition.args[0].args;
} else {
args = condition.args;
}
if (args.length === 1) {
args.unshift(parseExpr(name));
} else {
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg.args && arg.args.length === 1) {
arg.args.unshift(parseExpr(name));
}
}
}
}
/**
* parse conditions in MongoDB style, which is quite polular in ORMs for JavaScript. See {@link module:src/spell~OPERATOR_MAP} for supported `$op`s.

@@ -220,13 +278,16 @@ * @example

// if name is a common operator
/**
* {
* $like: '%no%'
* }
*/
// { $like: '%no%' }
result.push({
type: 'op',
name: OPERATOR_MAP[name],
args: [ parseExpr(name), parseObjectValue(value) ],
args: [ parseObjectValue(value) ],
});
}
else if (isLogicalCondition(value)) {
const conds = parseObjectValue(value);
for (const condition of conds) {
normalizeLogicalCondition(condition, name);
result.push(condition);
}
}
else {

@@ -254,3 +315,4 @@ result.push({

for (const name of names) {
columns.push(...parseExprList(name));
if (name.__raw) columns.push(name);
else columns.push(...parseExprList(name));
}

@@ -928,3 +990,3 @@

// https://stackoverflow.com/questions/32730296/referring-to-a-select-aggregate-column-alias-in-the-having-clause-in-postgres
if (this.Model.driver.type === 'postgres') {
if (this.Model.driver.type === 'postgres' && !condition.__raw) {
const { value } = condition.args[0];

@@ -931,0 +993,0 @@ for (const column of this.columns) {

@@ -59,2 +59,5 @@ 'use strict';

},
len(str, min, max) {
return Validator.isLength(str, min, max);
}
};

@@ -61,0 +64,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc