Comparing version 1.9.0 to 1.10.0
@@ -0,1 +1,8 @@ | ||
1.10.0 / 2021-09-14 | ||
================== | ||
* feat: SQLite driver should emit "connection" event when new connection is created (#168) | ||
* fix: bulkCreate(...records) should recognize custom setters (#168) | ||
* fix: attribute.equals() check should ignore defaultValue (#172) | ||
1.9.0 / 2021-09-04 | ||
@@ -77,3 +84,3 @@ ================== | ||
* feat: support class static attributes and hooks (#131) | ||
* fix: names defined in Bone.attributes should be always enumerable (#128) | ||
* fix: names defined in Bone.attributes should always be enumerable (#128) | ||
* chore: add quality badge to readme (#129) | ||
@@ -80,0 +87,0 @@ |
@@ -59,3 +59,3 @@ 'use strict'; | ||
const { columnName, dataType, defaultValue, ...restInfo } = columnInfo; | ||
const name = columnName == '_id' ? columnName : camelCase(columnName); | ||
const name = columnName === '_id' ? columnName : camelCase(columnName); | ||
// leave out defaultValue to let database take over the default | ||
@@ -189,3 +189,3 @@ attributes[name] = { | ||
if (!replacements.hasOwnProperty(key)) { | ||
throw new Error(`unable to replace :${key}`); | ||
throw new Error(`unable to replace: ${key}`); | ||
} | ||
@@ -192,0 +192,0 @@ values.push(replacements[key]); |
{ | ||
"name": "leoric", | ||
"version": "1.9.0", | ||
"version": "1.10.0", | ||
"description": "JavaScript Object-relational mapping alchemy", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -946,3 +946,3 @@ 'use strict'; | ||
attributeMap, | ||
associations: [], | ||
associations: {}, | ||
tableAlias, | ||
@@ -1317,2 +1317,5 @@ synchronized: Object.keys(compare(attributes, columns)).length === 0, | ||
// records might change when filter through custom setters | ||
records = instances.map(instance => instance.getRaw()); | ||
// bulk create with instances is possible only if | ||
@@ -1324,6 +1327,3 @@ // 1) either all of records primary key are set | ||
if (options.validate !== false) { | ||
records.map(record => { | ||
if (record instanceof Bone) record._validateAttributes(); | ||
else this._validateAttributes(record); | ||
}); | ||
for (const record of records) this._validateAttributes(record); | ||
} | ||
@@ -1499,3 +1499,3 @@ return await new Spell(this, options).$bulkInsert(records); | ||
if (Object.keys(columns).length === 0) { | ||
if (columns.length === 0) { | ||
await driver.createTable(table, attributes); | ||
@@ -1502,0 +1502,0 @@ } else { |
@@ -67,4 +67,4 @@ 'use strict'; | ||
const { type } = token; | ||
if (type == 'func') return false; | ||
if (type == 'alias' && token.args[0].type == 'func') return false; | ||
if (type === 'func') return false; | ||
if (type === 'alias' && token.args[0].type === 'func') return false; | ||
} | ||
@@ -89,3 +89,3 @@ if (groups.length > 0) return false; | ||
if (Object.keys(spell.joins).length == 0) { | ||
if (Object.keys(spell.joins).length === 0) { | ||
return Collection.from(rows, row => Model.instantiate(Object.values(row)[0])); | ||
@@ -123,3 +123,3 @@ } | ||
if (!current[qualifier]) current[qualifier] = new Collection(); | ||
if (!id || current[qualifier].some(item => item[Model.primaryKey] == id)) continue; | ||
if (!id || current[qualifier].some(item => item[Model.primaryKey] === id)) continue; | ||
current[qualifier].push(Model.instantiate(values)); | ||
@@ -162,5 +162,5 @@ } else { | ||
// mysql2 sometimes nests rows with table name instead of table alias | ||
const qualifier = prop == table ? tableAlias : prop; | ||
const qualifier = prop === table ? tableAlias : prop; | ||
const obj = result[qualifier] || (result[qualifier] = {}); | ||
if (qualifier == '') { | ||
if (qualifier === '') { | ||
Object.assign(obj, data); | ||
@@ -167,0 +167,0 @@ } |
@@ -109,3 +109,3 @@ 'use strict'; | ||
if (!targetAttribute) return false; | ||
const props = [ 'dataType', 'allowNull', 'defaultValue', 'primaryKey' ]; | ||
const props = [ 'dataType', 'allowNull', 'primaryKey' ]; | ||
for (const prop of props) { | ||
@@ -112,0 +112,0 @@ // SQLite has default value as string even if data type is integer |
'use strict'; | ||
const EventEmitter = require('events'); | ||
const strftime = require('strftime'); | ||
@@ -12,126 +11,3 @@ | ||
const spellbook = require('./spellbook'); | ||
// SELECT users.id AS "users:id", ... | ||
// => [ { users: { id, ... } } ] | ||
function nest(rows, fields, spell) { | ||
const { Model } = spell; | ||
const { tableAlias } = Model; | ||
const results = []; | ||
for (const row of rows) { | ||
const result = {}; | ||
const qualified = Object.keys(row).some(entry => entry.includes(':')); | ||
for (const key in row) { | ||
const parts = key.split(':'); | ||
const [qualifier, column] = qualified | ||
? (parts.length > 1 ? parts : ['', key]) | ||
: [Model.attributeMap.hasOwnProperty(key) ? tableAlias : '', key]; | ||
const obj = result[qualifier] || (result[qualifier] = {}); | ||
obj[column] = row[key]; | ||
} | ||
results.push(result); | ||
} | ||
return { rows: results, fields }; | ||
} | ||
class Connection { | ||
constructor({ client, database, mode, pool }) { | ||
const { Database, OPEN_READWRITE, OPEN_CREATE } = client; | ||
if (mode == null) mode = OPEN_READWRITE | OPEN_CREATE; | ||
this.database = new Database(database, mode); | ||
this.pool = pool; | ||
} | ||
async query(query, values, spell) { | ||
const { sql, nestTables } = query.sql ? query : { sql: query }; | ||
if (/^(?:pragma|select)/i.test(sql)) { | ||
const result = await this.all(sql, values); | ||
if (nestTables) return nest(result.rows, result.fields, spell); | ||
return result; | ||
} | ||
return await this.run(sql, values); | ||
} | ||
all(sql, values) { | ||
return new Promise((resolve, reject) => { | ||
this.database.all(sql, values, (err, rows, fields) => { | ||
if (err) reject(err); | ||
else resolve({ rows, fields }); | ||
}); | ||
}); | ||
} | ||
run(sql, values) { | ||
return new Promise((resolve, reject) => { | ||
this.database.run(sql, values, function Leoric_sqliteRun(err) { | ||
if (err) reject(err); | ||
else resolve({ insertId: this.lastID, affectedRows: this.changes }); | ||
}); | ||
}); | ||
} | ||
release() { | ||
this.pool.releaseConnection(this); | ||
} | ||
async end() { | ||
const { connections } = this.pool; | ||
const index = connections.indexOf(this); | ||
if (index >= 0) connections.splice(index, 1); | ||
return await new Promise((resolve, reject) => { | ||
this.database.close(function(err) { | ||
if (err) reject(err); | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
} | ||
class Pool extends EventEmitter { | ||
constructor(opts) { | ||
super(opts); | ||
this.options = { | ||
connectionLimit: 10, | ||
...opts, | ||
client: opts.client || 'sqlite3', | ||
}; | ||
this.client = require(this.options.client); | ||
this.connections = []; | ||
this.queue = []; | ||
} | ||
async getConnection() { | ||
const { connections, queue, client, options } = this; | ||
for (const connection of connections) { | ||
if (connection.idle) { | ||
connection.idle = false; | ||
this.emit('acquire', connection); | ||
return connection; | ||
} | ||
} | ||
if (connections.length < options.connectionLimit) { | ||
const connection = new Connection({ ...options, client, pool: this }); | ||
connections.push(connection); | ||
this.emit('acquire', connection); | ||
return connection; | ||
} | ||
await new Promise(resolve => queue.push(resolve)); | ||
return await this.getConnection(); | ||
} | ||
releaseConnection(connection) { | ||
connection.idle = true; | ||
this.emit('release', connection); | ||
const { queue } = this; | ||
while (queue.length > 0) { | ||
const task = queue.shift(); | ||
task(); | ||
} | ||
} | ||
} | ||
const Pool = require('./pool'); | ||
class SqliteDriver extends AbstractDriver { | ||
@@ -138,0 +14,0 @@ constructor(opts = {}) { |
@@ -58,7 +58,3 @@ 'use strict'; | ||
const OPERATORS = new Set( | ||
['between', 'not between', ...UNARY_OPERATORS, ...BINARY_OPERATORS].sort((a, b) => { | ||
if (a.length > b.length) return -1; | ||
else if (a.length < b.length) return 1; | ||
else return 0; | ||
}) | ||
['between', 'not between', ...UNARY_OPERATORS, ...BINARY_OPERATORS].sort((a, b) => b.length - a.length) | ||
); | ||
@@ -157,3 +153,3 @@ | ||
next(); | ||
while (chr && chr != quote) { | ||
while (chr && chr !== quote) { | ||
value += chr; | ||
@@ -176,3 +172,3 @@ next(); | ||
space(); | ||
} while (chr == ','); | ||
} while (chr === ','); | ||
next(); | ||
@@ -198,6 +194,6 @@ return { type: 'func', name, args }; | ||
next(); | ||
while (chr && chr != ')') { | ||
while (chr && chr !== ')') { | ||
space(); | ||
const item = token(); | ||
if (item.type == 'literal') { | ||
if (item.type === 'literal') { | ||
items.push(item.value); | ||
@@ -221,4 +217,4 @@ } else { | ||
if (/['"]/.test(chr)) return string(); | ||
if (chr == '?') return placeholder(); | ||
if (chr == '(') return array(); | ||
if (chr === '?') return placeholder(); | ||
if (chr === '(') return array(); | ||
@@ -228,3 +224,3 @@ for (const name of OPERATORS) { | ||
const chunk = str.slice(i, j); | ||
if (chunk.toLowerCase() == name && (str[j] == ' ' || !/[a-z]$/.test(name))) { | ||
if (chunk.toLowerCase() === name && (str[j] === ' ' || !/[a-z]$/.test(name))) { | ||
i += name.length; | ||
@@ -249,3 +245,3 @@ chr = str[i]; | ||
} | ||
else if (chr == '(') { | ||
else if (chr === '(') { | ||
return func(lowerCase); | ||
@@ -269,3 +265,3 @@ } | ||
const conj = token(); | ||
if (conj.name != 'and') throw new Error(`Unexpected conj ${conj}`); | ||
if (conj.name !== 'and') throw new Error(`Unexpected conj ${conj}`); | ||
space(); | ||
@@ -282,4 +278,4 @@ const end = token(); | ||
function unary(op) { | ||
const arg = chr == '(' ? expr() : token(); | ||
if (op.name == '-' && arg.type == 'literal' && Number.isFinite(arg.value)) { | ||
const arg = chr === '(' ? expr() : token(); | ||
if (op.name === '-' && arg.type === 'literal' && Number.isFinite(arg.value)) { | ||
return { type: 'literal', value: -arg.value }; | ||
@@ -293,4 +289,4 @@ } else { | ||
const op = token(); | ||
if (op.name == 'as') return alias(t); | ||
if (op.name == 'between' || op.name == 'not between') { | ||
if (op.name === 'as') return alias(t); | ||
if (['between', 'not between'].includes(op.name)) { | ||
return between(op, t); | ||
@@ -300,3 +296,3 @@ } | ||
space(); | ||
const isLower = chr == '('; | ||
const isLower = chr === '('; | ||
const operand = LOGICAL_OPERATORS.includes(op.name) ? expr() : token(); | ||
@@ -307,7 +303,7 @@ // parseExpr('1 > -1') | ||
} | ||
else if (operand.type == 'op' && operand.args.length < 2) { | ||
else if (operand.type === 'op' && operand.args.length < 2) { | ||
throw new Error(`Unexpected token ${operand.name}`); | ||
} | ||
// parseExpr('a = 1 && b = 2 && c = 3') | ||
else if (operand.type == 'op' && !isLower && precedes(op.name, operand.name) <= 0) { | ||
else if (operand.type === 'op' && !isLower && precedes(op.name, operand.name) <= 0) { | ||
const { args } = operand; | ||
@@ -318,3 +314,3 @@ operand.args = [{ ...op, args: [t, args[0]] }, args[1]]; | ||
// parseExpr('a + b * c') | ||
else if (operand.type !== 'op' && t.type == 'op' && precedes(op.name, t.name) < 0) { | ||
else if (operand.type !== 'op' && t.type === 'op' && precedes(op.name, t.name) < 0) { | ||
t.args[1] = { ...op, args: [t.args[1], operand] }; | ||
@@ -334,7 +330,7 @@ return t; | ||
let node; | ||
while (chr && chr != ',' && chr != ')') { | ||
while (chr && chr !== ',' && chr !== ')') { | ||
space(); | ||
if (node) { | ||
// check arguments length to differentiate unary minus and binary minus | ||
if (UNARY_OPERATORS.includes(node.name) && node.args.length == 0) { | ||
if (UNARY_OPERATORS.includes(node.name) && node.args.length === 0) { | ||
node = unary(node); | ||
@@ -349,3 +345,3 @@ } | ||
} | ||
else if (chr == '(') { | ||
else if (chr === '(') { | ||
next(); | ||
@@ -355,3 +351,3 @@ node = expr(); | ||
} | ||
else if (chr == '*') { | ||
else if (chr === '*') { | ||
node = wildcard(); | ||
@@ -370,3 +366,3 @@ } | ||
results.push(expr()); | ||
if (chr == ',') next(); | ||
if (chr === ',') next(); | ||
} | ||
@@ -373,0 +369,0 @@ return results; |
@@ -84,5 +84,9 @@ 'use strict'; | ||
function isOperatorCondition(condition) { | ||
return isPlainObject(condition) && | ||
Object.keys(condition).length > 0 && | ||
Object.keys(condition).every($op => OPERATOR_MAP.hasOwnProperty($op)); | ||
if (!isPlainObject(condition)) return false; | ||
const keys = Object.keys(condition); | ||
return ( | ||
keys.length > 0 && | ||
keys.every($op => OPERATOR_MAP.hasOwnProperty($op)) | ||
); | ||
} | ||
@@ -256,3 +260,3 @@ | ||
const { joins, Model } = spell; | ||
if (typeof names[0] == 'function') { | ||
if (typeof names[0] === 'function') { | ||
names = Object.keys(Model.attributes).filter(names[0]); | ||
@@ -436,3 +440,3 @@ } else { | ||
walkExpr(token, node => { | ||
if (node.type == 'id' && !node.qualifiers && RefModel.attributes[node.value]) { | ||
if (node.type === 'id' && !node.qualifiers && RefModel.attributes[node.value]) { | ||
node.qualifiers = [refName]; | ||
@@ -469,3 +473,3 @@ } | ||
walkExpr(condition, ({ type, value }) => { | ||
if (type == 'id' && value == deletedAt) { | ||
if (type === 'id' && value == deletedAt) { | ||
found = true; | ||
@@ -533,3 +537,3 @@ } | ||
this.scopes = scopes; | ||
this.scopes = scopes; | ||
@@ -846,3 +850,3 @@ /** | ||
const token = parseExpr(name); | ||
if (token.type == 'alias') { | ||
if (token.type === 'alias') { | ||
groups.push({ type: 'id', value: token.value }); | ||
@@ -852,3 +856,3 @@ } else { | ||
} | ||
if (!columns.some(entry => entry.value == token.value)) { | ||
if (!columns.some(entry => entry.value === token.value)) { | ||
columns.push(token); | ||
@@ -1000,3 +1004,3 @@ } | ||
$join(Model, onConditions, ...values) { | ||
if (typeof Model == 'string') { | ||
if (typeof Model === 'string') { | ||
return this.$with(...arguments); | ||
@@ -1003,0 +1007,0 @@ } |
@@ -72,3 +72,3 @@ 'use strict'; | ||
const value = setValue != null? setValue : defaultValue; | ||
const value = setValue != null ? setValue : defaultValue; | ||
if (typeof validateArgs === 'function') { | ||
@@ -106,4 +106,4 @@ let needToRevert = false; | ||
if (['true', 'false'].indexOf(String(validateArgs)) >= 0) { | ||
if (validator.call(ctx, value == null? value : String(value)) !== args) { | ||
throw new LeoricValidateError(name, field, msg, !validateArgs? validateArgs : undefined); | ||
if (validator.call(ctx, value == null ? value : String(value)) !== args) { | ||
throw new LeoricValidateError(name, field, msg, !validateArgs ? validateArgs : undefined); | ||
} | ||
@@ -110,0 +110,0 @@ return; |
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
256291
44
7408