@depup/bookshelf 1.2.0-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +18 -0
- package/.nycrc.yml +3 -0
- package/.prettierrc +8 -0
- package/CHANGELOG.md +764 -0
- package/LICENSE +22 -0
- package/README.md +32 -0
- package/bookshelf.js +8 -0
- package/changes.json +14 -0
- package/lib/base/collection.js +802 -0
- package/lib/base/eager.js +111 -0
- package/lib/base/events.js +129 -0
- package/lib/base/model.js +995 -0
- package/lib/base/relation.js +69 -0
- package/lib/bookshelf.js +558 -0
- package/lib/collection.js +545 -0
- package/lib/constants.js +2 -0
- package/lib/eager.js +122 -0
- package/lib/errors.js +36 -0
- package/lib/extend.js +41 -0
- package/lib/helpers.js +212 -0
- package/lib/model.js +1568 -0
- package/lib/relation.js +947 -0
- package/lib/sync.js +244 -0
- package/package.json +111 -0
package/lib/sync.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// Sync
|
|
2
|
+
// ---------------
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
const Promise = require('bluebird');
|
|
7
|
+
const validLocks = ['forShare', 'forUpdate'];
|
|
8
|
+
|
|
9
|
+
function supportsReturning(client = {}) {
|
|
10
|
+
if (!client.config || !client.config.client) return false;
|
|
11
|
+
return ['postgresql', 'postgres', 'pg', 'oracle', 'mssql'].includes(client.config.client);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Sync is the dispatcher for any database queries,
|
|
15
|
+
// taking the "syncing" `model` or `collection` being queried, along with
|
|
16
|
+
// a hash of options that are used in the various query methods.
|
|
17
|
+
// If the `transacting` option is set, the query is assumed to be
|
|
18
|
+
// part of a transaction, and this information is passed along to `Knex`.
|
|
19
|
+
const Sync = function(syncing, options) {
|
|
20
|
+
options = options || {};
|
|
21
|
+
this.query = syncing.query();
|
|
22
|
+
this.syncing = syncing.resetQuery();
|
|
23
|
+
this.options = options;
|
|
24
|
+
if (options.debug) this.query.debug();
|
|
25
|
+
if (options.transacting) {
|
|
26
|
+
this.query.transacting(options.transacting);
|
|
27
|
+
if (validLocks.indexOf(options.lock) > -1) this.query[options.lock]();
|
|
28
|
+
}
|
|
29
|
+
if (options.withSchema) this.query.withSchema(options.withSchema);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
_.extend(Sync.prototype, {
|
|
33
|
+
// Prefix all keys of the passed in object with the
|
|
34
|
+
// current table name
|
|
35
|
+
prefixFields: function(fields) {
|
|
36
|
+
const tableName = this.syncing.tableName;
|
|
37
|
+
const prefixed = {};
|
|
38
|
+
for (const key in fields) {
|
|
39
|
+
prefixed[tableName + '.' + key] = fields[key];
|
|
40
|
+
}
|
|
41
|
+
return prefixed;
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Select the first item from the database - only used by models.
|
|
45
|
+
first: Promise.method(function(attributes) {
|
|
46
|
+
const model = this.syncing;
|
|
47
|
+
const query = this.query;
|
|
48
|
+
|
|
49
|
+
// We'll never use an JSON object for a search, because even
|
|
50
|
+
// PostgreSQL, which has JSON type columns, does not support the `=`
|
|
51
|
+
// operator.
|
|
52
|
+
//
|
|
53
|
+
// NOTE: `_.omit` returns an empty object, even if attributes are null.
|
|
54
|
+
const whereAttributes = _.omitBy(attributes, (attribute, name) => {
|
|
55
|
+
return _.isPlainObject(attribute) || name === model.idAttribute;
|
|
56
|
+
});
|
|
57
|
+
const formattedAttributes = model.format(whereAttributes);
|
|
58
|
+
|
|
59
|
+
if (model.idAttribute in attributes) {
|
|
60
|
+
formattedAttributes[model.idAttribute] = attributes[model.idAttribute];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!_.isEmpty(formattedAttributes)) query.where(this.prefixFields(formattedAttributes));
|
|
64
|
+
query.limit(1);
|
|
65
|
+
|
|
66
|
+
return this.select();
|
|
67
|
+
}),
|
|
68
|
+
|
|
69
|
+
// Runs a `count` query on the database, adding any necessary relational
|
|
70
|
+
// constraints. Returns a promise that resolves to an integer count.
|
|
71
|
+
count: Promise.method(function(column) {
|
|
72
|
+
const knex = this.query,
|
|
73
|
+
options = this.options,
|
|
74
|
+
relatedData = this.syncing.relatedData,
|
|
75
|
+
fks = {};
|
|
76
|
+
|
|
77
|
+
return Promise.bind(this)
|
|
78
|
+
.then(function() {
|
|
79
|
+
// Inject all appropriate select costraints dealing with the relation
|
|
80
|
+
// into the `knex` query builder for the current instance.
|
|
81
|
+
if (relatedData)
|
|
82
|
+
return Promise.try(function() {
|
|
83
|
+
if (relatedData.isThrough()) {
|
|
84
|
+
fks[relatedData.key('foreignKey')] = relatedData.parentFk;
|
|
85
|
+
const through = new relatedData.throughTarget(fks);
|
|
86
|
+
relatedData.pivotColumns = through.parse(relatedData.pivotColumns);
|
|
87
|
+
} else if (relatedData.type === 'hasMany') {
|
|
88
|
+
const fk = relatedData.key('foreignKey');
|
|
89
|
+
knex.where(fk, relatedData.parentFk);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
})
|
|
93
|
+
.then(function() {
|
|
94
|
+
options.query = knex;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Counting event.
|
|
98
|
+
*
|
|
99
|
+
* Fired before a `count` query. A promise may be
|
|
100
|
+
* returned from the event handler for async behaviour.
|
|
101
|
+
*
|
|
102
|
+
* @event Model#counting
|
|
103
|
+
* @tutorial events
|
|
104
|
+
* @param {Model} model The model firing the event.
|
|
105
|
+
* @param {Object} options Options object passed to {@link Model#count count}.
|
|
106
|
+
* @returns {Promise}
|
|
107
|
+
*/
|
|
108
|
+
return this.syncing.triggerThen('counting', this.syncing, options);
|
|
109
|
+
})
|
|
110
|
+
.then(function() {
|
|
111
|
+
return knex.count((column || '*') + ' as count');
|
|
112
|
+
})
|
|
113
|
+
.then(function(rows) {
|
|
114
|
+
return rows[0].count;
|
|
115
|
+
});
|
|
116
|
+
}),
|
|
117
|
+
|
|
118
|
+
// Runs a `select` query on the database, adding any necessary relational
|
|
119
|
+
// constraints, resetting the query when complete. If there are results and
|
|
120
|
+
// eager loaded relations, those are fetched and returned on the model before
|
|
121
|
+
// the promise is resolved. Any `success` handler passed in the
|
|
122
|
+
// options will be called - used by both models & collections.
|
|
123
|
+
select: Promise.method(function() {
|
|
124
|
+
const knex = this.query;
|
|
125
|
+
const options = this.options;
|
|
126
|
+
const relatedData = this.syncing.relatedData;
|
|
127
|
+
const fks = {};
|
|
128
|
+
let columns = null;
|
|
129
|
+
|
|
130
|
+
// Check if any `select` style statements have been called with column
|
|
131
|
+
// specifications. This could include `distinct()` with no arguments, which
|
|
132
|
+
// does not affect inform the columns returned.
|
|
133
|
+
const queryContainsColumns = _(knex._statements)
|
|
134
|
+
.filter({grouping: 'columns'})
|
|
135
|
+
.some('value.length');
|
|
136
|
+
|
|
137
|
+
return Promise.bind(this)
|
|
138
|
+
.then(function() {
|
|
139
|
+
// Set the query builder on the options, in-case we need to
|
|
140
|
+
// access in the `fetching` event handlers.
|
|
141
|
+
options.query = knex;
|
|
142
|
+
|
|
143
|
+
// Inject all appropriate select costraints dealing with the relation
|
|
144
|
+
// into the `knex` query builder for the current instance.
|
|
145
|
+
if (relatedData)
|
|
146
|
+
return Promise.try(function() {
|
|
147
|
+
if (relatedData.isThrough()) {
|
|
148
|
+
fks[relatedData.key('foreignKey')] = relatedData.parentFk;
|
|
149
|
+
const through = new relatedData.throughTarget(fks);
|
|
150
|
+
|
|
151
|
+
return through.triggerThen('fetching', through, relatedData.pivotColumns, options).then(function() {
|
|
152
|
+
relatedData.pivotColumns = through.parse(relatedData.pivotColumns);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
})
|
|
157
|
+
.tap(() => {
|
|
158
|
+
// If this is a relation, apply the appropriate constraints.
|
|
159
|
+
if (relatedData) {
|
|
160
|
+
relatedData.selectConstraints(knex, options);
|
|
161
|
+
} else {
|
|
162
|
+
// Call the function, if one exists, to constrain the eager loaded query.
|
|
163
|
+
if (options._beforeFn) options._beforeFn.call(knex, knex);
|
|
164
|
+
|
|
165
|
+
if (options.columns) {
|
|
166
|
+
// Normalize single column name into array.
|
|
167
|
+
columns = Array.isArray(options.columns) ? options.columns : [options.columns];
|
|
168
|
+
} else if (!queryContainsColumns) {
|
|
169
|
+
// If columns have already been selected via the `query` method
|
|
170
|
+
// we will use them. Otherwise, select all columns in this table.
|
|
171
|
+
columns = [_.result(this.syncing, 'tableName') + '.*'];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Set the query builder on the options, for access in the `fetching`
|
|
176
|
+
// event handlers.
|
|
177
|
+
options.query = knex;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Fired before a `fetch` operation. A promise may be returned from the event handler for
|
|
181
|
+
* async behaviour.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* const MyModel = bookshelf.model('MyModel', {
|
|
185
|
+
* initialize() {
|
|
186
|
+
* this.on('fetching', function(model, columns, options) {
|
|
187
|
+
* options.query.where('status', 'active')
|
|
188
|
+
* })
|
|
189
|
+
* }
|
|
190
|
+
* })
|
|
191
|
+
*
|
|
192
|
+
* @event Model#fetching
|
|
193
|
+
* @tutorial events
|
|
194
|
+
* @param {Model} model The model which is about to be fetched.
|
|
195
|
+
* @param {string[]} columns The columns to be retrieved by the query.
|
|
196
|
+
* @param {Object} options Options object passed to {@link Model#fetch fetch}.
|
|
197
|
+
* @param {QueryBuilder} options.query
|
|
198
|
+
* Query builder to be used for fetching. This can be used to modify or add to the query
|
|
199
|
+
* before it is executed. See example above.
|
|
200
|
+
* @return {Promise}
|
|
201
|
+
*/
|
|
202
|
+
return this.syncing.triggerThen('fetching', this.syncing, columns, options);
|
|
203
|
+
})
|
|
204
|
+
.then(() => knex.select(columns));
|
|
205
|
+
}),
|
|
206
|
+
|
|
207
|
+
// Issues an `insert` command on the query - only used by models.
|
|
208
|
+
insert: Promise.method(function() {
|
|
209
|
+
const syncing = this.syncing;
|
|
210
|
+
return this.query.insert(
|
|
211
|
+
syncing.format(_.extend(Object.create(null), syncing.attributes)),
|
|
212
|
+
supportsReturning(this.query.client) && this.options.autoRefresh !== false ? '*' : null
|
|
213
|
+
);
|
|
214
|
+
}),
|
|
215
|
+
|
|
216
|
+
// Issues an `update` command on the query - only used by models.
|
|
217
|
+
update: Promise.method(function(attrs) {
|
|
218
|
+
const syncing = this.syncing,
|
|
219
|
+
query = this.query;
|
|
220
|
+
if (syncing.id != null) query.where(syncing.format({[syncing.idAttribute]: syncing.id}));
|
|
221
|
+
if (_.filter(query._statements, {grouping: 'where'}).length === 0) {
|
|
222
|
+
throw new Error('A model cannot be updated without a "where" clause or an idAttribute.');
|
|
223
|
+
}
|
|
224
|
+
var updating = syncing.format(_.extend(Object.create(null), attrs));
|
|
225
|
+
if (syncing.id === updating[syncing.idAttribute]) {
|
|
226
|
+
delete updating[syncing.idAttribute];
|
|
227
|
+
}
|
|
228
|
+
if (supportsReturning(query.client) && this.options.autoRefresh !== false) query.returning('*');
|
|
229
|
+
return query.update(updating);
|
|
230
|
+
}),
|
|
231
|
+
|
|
232
|
+
// Issues a `delete` command on the query.
|
|
233
|
+
del: Promise.method(function() {
|
|
234
|
+
const query = this.query,
|
|
235
|
+
syncing = this.syncing;
|
|
236
|
+
if (syncing.id != null) query.where(syncing.format({[syncing.idAttribute]: syncing.id}));
|
|
237
|
+
if (_.filter(query._statements, {grouping: 'where'}).length === 0) {
|
|
238
|
+
throw new Error('A model cannot be destroyed without a "where" clause or an idAttribute.');
|
|
239
|
+
}
|
|
240
|
+
return this.query.del();
|
|
241
|
+
})
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
module.exports = Sync;
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@depup/bookshelf",
|
|
3
|
+
"version": "1.2.0-depup.0",
|
|
4
|
+
"description": "[DepUp] A lightweight ORM for PostgreSQL, MySQL, and SQLite3",
|
|
5
|
+
"main": "bookshelf.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"format": "prettier --write \"{lib,scripts,test}/**/*.js\"",
|
|
8
|
+
"lint": "eslint bookshelf.js lib/",
|
|
9
|
+
"cover": "npm run lint && nyc mocha --check-leaks -t 10000 -b",
|
|
10
|
+
"test": "npm run lint && mocha --check-leaks -t 10000 -b",
|
|
11
|
+
"jsdoc": "./scripts/jsdoc.sh",
|
|
12
|
+
"postpublish": "./scripts/postpublish.sh"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://bookshelfjs.org",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/bookshelf/bookshelf.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"depup",
|
|
21
|
+
"dependency-bumped",
|
|
22
|
+
"updated-deps",
|
|
23
|
+
"bookshelf",
|
|
24
|
+
"orm",
|
|
25
|
+
"mysql",
|
|
26
|
+
"postgresql",
|
|
27
|
+
"sqlite",
|
|
28
|
+
"datamapper",
|
|
29
|
+
"active record"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"bluebird": "^3.7.2",
|
|
33
|
+
"create-error": "~0.3.1",
|
|
34
|
+
"inflection": "^3.0.2",
|
|
35
|
+
"lodash": "^4.17.23"
|
|
36
|
+
},
|
|
37
|
+
"husky": {
|
|
38
|
+
"hooks": {
|
|
39
|
+
"pre-commit": "lint-staged"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"lint-staged": {
|
|
43
|
+
"*.{js,json}": [
|
|
44
|
+
"prettier --write",
|
|
45
|
+
"git add"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"bookshelf-jsdoc-theme": "^1.0.1",
|
|
50
|
+
"chai": "^4.2.0",
|
|
51
|
+
"eslint": "^6.8.0",
|
|
52
|
+
"eslint-config-prettier": "^6.10.0",
|
|
53
|
+
"eslint-plugin-prettier": "^3.1.2",
|
|
54
|
+
"husky": "^3.0.5",
|
|
55
|
+
"jsdoc": "^3.6.3",
|
|
56
|
+
"knex": "~0.21.0",
|
|
57
|
+
"lint-staged": "^9.2.5",
|
|
58
|
+
"mocha": "^7.1.2",
|
|
59
|
+
"mysql": "^2.18.1",
|
|
60
|
+
"nyc": "^15.0.0",
|
|
61
|
+
"pg": "^8.0.3",
|
|
62
|
+
"prettier": "^1.18.2",
|
|
63
|
+
"sinon": "^8.1.1",
|
|
64
|
+
"sinon-chai": "^3.3.0",
|
|
65
|
+
"sqlite3": "^4.1.1",
|
|
66
|
+
"uuid": "^3.3.3"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"knex": ">=0.15.0 <0.22.0"
|
|
70
|
+
},
|
|
71
|
+
"author": {
|
|
72
|
+
"name": "Tim Griesser",
|
|
73
|
+
"url": "https://github.com/tgriesser"
|
|
74
|
+
},
|
|
75
|
+
"contributors": [
|
|
76
|
+
{
|
|
77
|
+
"name": "Edward Greve",
|
|
78
|
+
"url": "https://github.com/anyong"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "Rhys van der Waerden",
|
|
82
|
+
"url": "https://github.com/rhys-vdw"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "Ricardo Graça",
|
|
86
|
+
"url": "https://github.com/ricardograca"
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
"license": "MIT",
|
|
90
|
+
"readmeFilename": "README.md",
|
|
91
|
+
"engines": {
|
|
92
|
+
"node": ">=6"
|
|
93
|
+
},
|
|
94
|
+
"depup": {
|
|
95
|
+
"changes": {
|
|
96
|
+
"inflection": {
|
|
97
|
+
"from": "^1.12.0",
|
|
98
|
+
"to": "^3.0.2"
|
|
99
|
+
},
|
|
100
|
+
"lodash": {
|
|
101
|
+
"from": "^4.17.15",
|
|
102
|
+
"to": "^4.17.23"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"depsUpdated": 2,
|
|
106
|
+
"originalPackage": "bookshelf",
|
|
107
|
+
"originalVersion": "1.2.0",
|
|
108
|
+
"processedAt": "2026-03-17T18:41:05.185Z",
|
|
109
|
+
"smokeTest": "passed"
|
|
110
|
+
}
|
|
111
|
+
}
|