objection 4.0.0-rc.4 → 4.0.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/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/CheckViolationError/parser.js +2 -9
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +2 -9
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/NotNullViolationError/parser.js +4 -2
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/UniqueViolationError/parser.js +4 -2
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/constraintMessage.js +16 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/parser.js +2 -1
- package/lib/dbErrors/parsers/sqlite/DBError/parser.js +2 -1
- package/lib/model/modelJsonAttributes.js +15 -6
- package/lib/objection.js +1 -0
- package/lib/queryBuilder/InternalOptions.js +4 -4
- package/lib/queryBuilder/StaticHookArguments.js +4 -2
- package/lib/queryBuilder/graph/GraphSchedule.js +20 -6
- package/lib/queryBuilder/graph/GraphWriter.js +92 -35
- package/lib/queryBuilder/graph/graphKeys.js +78 -59
- package/lib/queryBuilder/operations/AsArrayOperation.js +10 -0
- package/lib/queryBuilder/operations/UpdateOperation.js +20 -10
- package/lib/queryBuilder/operations/fieldExpressions.js +14 -6
- package/lib/relations/RelationProperty.js +5 -0
- package/package.json +9 -3
- package/typings/objection/index.d.ts +100 -94
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { nonStrictEquals } from '../../utils/objectUtils.js';
|
|
2
2
|
import { ValidationErrorType } from '../../model/ValidationError.js';
|
|
3
3
|
import { formatPath } from './GraphNode.js';
|
|
4
|
+
import { RelationProperty } from '../../relations/RelationProperty.js';
|
|
4
5
|
|
|
5
6
|
const UNRESOLVED = Symbol('unresolved');
|
|
6
7
|
|
|
@@ -16,18 +17,15 @@ export class GraphKeys {
|
|
|
16
17
|
constructor(plan) {
|
|
17
18
|
this.plan = plan;
|
|
18
19
|
this.activeNodes = new Set(plan.activeNodes);
|
|
20
|
+
// The row of each node, and the rows by their keys:
|
|
19
21
|
this.rows = new Map();
|
|
20
|
-
this.
|
|
21
|
-
// The cells in their order
|
|
22
|
+
this.rowsByKey = new Map();
|
|
23
|
+
// The cells in their order:
|
|
22
24
|
this.cells = [];
|
|
23
|
-
this.cellsByRow = new Map();
|
|
24
25
|
// The props that each node only reads, and that other nodes write:
|
|
25
26
|
this.readProps = new Map();
|
|
26
27
|
// The cells that each place sets or reads:
|
|
27
28
|
this.placeCells = new Map();
|
|
28
|
-
// The rows that the graph inserts, and the ones that it inserted already:
|
|
29
|
-
this.insertRows = new Set();
|
|
30
|
-
this.insertedRows = new Set();
|
|
31
29
|
// The nodes whose `#ref{}` values are resolved:
|
|
32
30
|
this.resolvedNodes = new Set();
|
|
33
31
|
|
|
@@ -36,7 +34,7 @@ export class GraphKeys {
|
|
|
36
34
|
const row = this._getRow(node);
|
|
37
35
|
|
|
38
36
|
if (plan.actions.get(node) === 'insert') {
|
|
39
|
-
|
|
37
|
+
row.isInsert = true;
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
|
|
@@ -85,18 +83,20 @@ export class GraphKeys {
|
|
|
85
83
|
|
|
86
84
|
// 4. The clears, with the nodes that clear the cells. The places of a node
|
|
87
85
|
// read them as `null` if another node clears them:
|
|
88
|
-
this.clearingNodes = new Map();
|
|
89
86
|
const addClearingNode = (row, prop, node) =>
|
|
90
87
|
prop.props.forEach((_, i) => {
|
|
91
|
-
const
|
|
92
|
-
|
|
88
|
+
const description = prop.propDescription(i);
|
|
89
|
+
row.clearingNodes.set(
|
|
90
|
+
description,
|
|
91
|
+
(row.clearingNodes.get(description) || new Set()).add(node),
|
|
92
|
+
);
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
for (const currentNode of plan.activeCurrentNodes) {
|
|
96
96
|
const { relation, parent } = currentNode;
|
|
97
97
|
|
|
98
98
|
if (relation?.keySide === 'related' && plan.actions.get(currentNode) === 'unrelate') {
|
|
99
|
-
addClearingNode(currentNode.rowKey, relation.relatedProp, parent.node);
|
|
99
|
+
addClearingNode(this._getRowByKey(currentNode.rowKey), relation.relatedProp, parent.node);
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -113,7 +113,7 @@ export class GraphKeys {
|
|
|
113
113
|
// Bind the cells of the rows of the inserted `nodes` to their values.
|
|
114
114
|
setInserted(nodes) {
|
|
115
115
|
for (const node of nodes) {
|
|
116
|
-
this.
|
|
116
|
+
this.rows.get(node).isInserted = true;
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -172,9 +172,8 @@ export class GraphKeys {
|
|
|
172
172
|
// that moves the row.
|
|
173
173
|
isSetByOtherPlaces(node, relation) {
|
|
174
174
|
const row = this._getRow(node);
|
|
175
|
-
const cells = this.cellsByRow.get(row);
|
|
176
175
|
return relation.ownerProp.props.some((_, i) =>
|
|
177
|
-
[...(cells
|
|
176
|
+
[...(row.cells.get(relation.ownerProp.propDescription(i))?.placeNodes || [])].some(
|
|
178
177
|
(other) => !(other.relation === relation && this._getRow(other.parent) === row),
|
|
179
178
|
),
|
|
180
179
|
);
|
|
@@ -194,9 +193,7 @@ export class GraphKeys {
|
|
|
194
193
|
getReadProps(node, prop) {
|
|
195
194
|
return this._getCells(node, prop).map((cell) => {
|
|
196
195
|
const values = this._getAnchorCells(cell.slot).map((anchor) =>
|
|
197
|
-
this.
|
|
198
|
-
? null
|
|
199
|
-
: (this._getStoredValue(anchor) ?? this._getAnchorValue(anchor)),
|
|
196
|
+
anchor.row.isInsert ? null : (this._getStoredValue(anchor) ?? this._getAnchorValue(anchor)),
|
|
200
197
|
);
|
|
201
198
|
const value = this._getSlotValue(cell);
|
|
202
199
|
return values.length > 0
|
|
@@ -213,7 +210,7 @@ export class GraphKeys {
|
|
|
213
210
|
|
|
214
211
|
// Return true if a place of `node` sets the key column `prop` of its row:
|
|
215
212
|
isSet(node, prop, index) {
|
|
216
|
-
const cell = this.
|
|
213
|
+
const cell = this._getRow(node).cells.get(prop.propDescription(index));
|
|
217
214
|
return !!cell?.setNodes.has(node);
|
|
218
215
|
}
|
|
219
216
|
|
|
@@ -234,13 +231,11 @@ export class GraphKeys {
|
|
|
234
231
|
for (const cell of this.placeCells.get(node) || []) {
|
|
235
232
|
for (const anchorCell of this._getAnchorCells(cell.slot)) {
|
|
236
233
|
const { row, prop, index } = anchorCell;
|
|
237
|
-
anchorCell.waitedNodes ??=
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
(other)
|
|
241
|
-
|
|
242
|
-
this._hasReference(other, prop, index),
|
|
243
|
-
);
|
|
234
|
+
anchorCell.waitedNodes ??= row.nodes.filter(
|
|
235
|
+
(other) =>
|
|
236
|
+
(row.isInsert && this.plan.actions.get(other) === 'insert') ||
|
|
237
|
+
this._hasReference(other, prop, index),
|
|
238
|
+
);
|
|
244
239
|
addWaits(cell, anchorCell.waitedNodes, false);
|
|
245
240
|
|
|
246
241
|
// Only the places that set a cell write the changes of its slot:
|
|
@@ -263,6 +258,29 @@ export class GraphKeys {
|
|
|
263
258
|
return waits;
|
|
264
259
|
}
|
|
265
260
|
|
|
261
|
+
// Return the patches and relates that change the column of another row that
|
|
262
|
+
// the `#ref{}` of `reference` copies into `node`, as for the keys:
|
|
263
|
+
getChangingNodes(node, { referencedNode, refPath }) {
|
|
264
|
+
const { modelClass } = referencedNode;
|
|
265
|
+
const [propName] = refPath;
|
|
266
|
+
// The cell of a key on the column, or one of its own:
|
|
267
|
+
const cell =
|
|
268
|
+
[...this._getRow(referencedNode).cells.values()].find(
|
|
269
|
+
({ prop, index }) => prop.props[index] === propName && prop.isColumn(index),
|
|
270
|
+
) ??
|
|
271
|
+
this._getCell(
|
|
272
|
+
referencedNode,
|
|
273
|
+
new RelationProperty(
|
|
274
|
+
`${modelClass.getTableName()}.${modelClass.propertyNameToColumnName(propName)}`,
|
|
275
|
+
() => modelClass,
|
|
276
|
+
),
|
|
277
|
+
0,
|
|
278
|
+
);
|
|
279
|
+
return this._getAnchorCells(cell.slot)
|
|
280
|
+
.map((anchorCell) => this._getChangingNode(anchorCell))
|
|
281
|
+
.filter((other) => other && this.rows.get(other) !== this.rows.get(node));
|
|
282
|
+
}
|
|
283
|
+
|
|
266
284
|
// Return the patch or relate that changes the value of an anchor cell, or
|
|
267
285
|
// may change it, when its row isn't loaded. A foreign key may reference the
|
|
268
286
|
// cell, so the rows that read the new value wait for it (#428). Ids don't
|
|
@@ -272,7 +290,7 @@ export class GraphKeys {
|
|
|
272
290
|
const { writtenValue, writingNode } = this._getGivenValues(cell);
|
|
273
291
|
return writingNode &&
|
|
274
292
|
!cell.isId &&
|
|
275
|
-
(
|
|
293
|
+
(cell.row.isInsert || (writingNode.referencedNode || writingNode).obj.$hasId()) &&
|
|
276
294
|
!nonStrictEquals(writtenValue, this._getStoredValue(cell))
|
|
277
295
|
? writingNode
|
|
278
296
|
: null;
|
|
@@ -311,7 +329,7 @@ export class GraphKeys {
|
|
|
311
329
|
// parent and the relation.
|
|
312
330
|
_getPlaceKey(node, prop) {
|
|
313
331
|
const { parent, relation } = node;
|
|
314
|
-
return `${this._getRow(node)}|${parent ? this._getRow(parent) : ''}|${relation?.name}|${prop}`;
|
|
332
|
+
return `${this._getRow(node).key}|${parent ? this._getRow(parent).key : ''}|${relation?.name}|${prop}`;
|
|
315
333
|
}
|
|
316
334
|
|
|
317
335
|
_addCells(node, cells) {
|
|
@@ -337,48 +355,49 @@ export class GraphKeys {
|
|
|
337
355
|
_getRow(node) {
|
|
338
356
|
let row = this.rows.get(node);
|
|
339
357
|
|
|
340
|
-
if (row
|
|
358
|
+
if (!row) {
|
|
341
359
|
// A `#ref` model has the row of the model it refers to, and a new model
|
|
342
360
|
// a row of its own:
|
|
343
361
|
const { referencedNode } = node;
|
|
344
362
|
row = referencedNode
|
|
345
363
|
? this._getRow(referencedNode)
|
|
346
|
-
: node.obj.$hasId()
|
|
347
|
-
|
|
348
|
-
: `new:${this.rows.size}`;
|
|
364
|
+
: this._getRowByKey(node.obj.$hasId() ? node.rowKey : `new:${this.rows.size}`);
|
|
365
|
+
row.nodes.push(node);
|
|
349
366
|
this.rows.set(node, row);
|
|
350
|
-
|
|
351
|
-
const nodes = this.nodesByRow.get(row);
|
|
352
|
-
|
|
353
|
-
if (nodes) {
|
|
354
|
-
nodes.push(node);
|
|
355
|
-
} else {
|
|
356
|
-
this.nodesByRow.set(row, [node]);
|
|
357
|
-
}
|
|
358
367
|
}
|
|
359
368
|
|
|
360
369
|
return row;
|
|
361
370
|
}
|
|
362
371
|
|
|
363
|
-
|
|
364
|
-
|
|
372
|
+
// The row with the `key` of its table and id, or of a new row:
|
|
373
|
+
_getRowByKey(key) {
|
|
374
|
+
let row = this.rowsByKey.get(key);
|
|
375
|
+
|
|
376
|
+
if (!row) {
|
|
377
|
+
row = {
|
|
378
|
+
key,
|
|
379
|
+
nodes: [],
|
|
380
|
+
// The cells by their props, and the nodes that clear them:
|
|
381
|
+
cells: new Map(),
|
|
382
|
+
clearingNodes: new Map(),
|
|
383
|
+
// Whether the graph inserts the row, and whether it is inserted:
|
|
384
|
+
isInsert: false,
|
|
385
|
+
isInserted: false,
|
|
386
|
+
};
|
|
387
|
+
this.rowsByKey.set(key, row);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return row;
|
|
365
391
|
}
|
|
366
392
|
|
|
367
393
|
_getCell(node, prop, index) {
|
|
368
394
|
const row = this._getRow(node);
|
|
369
395
|
const description = prop.propDescription(index);
|
|
370
|
-
let
|
|
371
|
-
|
|
372
|
-
if (!cells) {
|
|
373
|
-
cells = new Map();
|
|
374
|
-
this.cellsByRow.set(row, cells);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
let cell = cells.get(description);
|
|
396
|
+
let cell = row.cells.get(description);
|
|
378
397
|
|
|
379
398
|
if (!cell) {
|
|
380
399
|
cell = {
|
|
381
|
-
|
|
400
|
+
description,
|
|
382
401
|
row,
|
|
383
402
|
node,
|
|
384
403
|
prop,
|
|
@@ -391,7 +410,7 @@ export class GraphKeys {
|
|
|
391
410
|
};
|
|
392
411
|
cell.slot = { cells: [cell] };
|
|
393
412
|
this.cells.push(cell);
|
|
394
|
-
cells.set(description, cell);
|
|
413
|
+
row.cells.set(description, cell);
|
|
395
414
|
}
|
|
396
415
|
|
|
397
416
|
return cell;
|
|
@@ -415,7 +434,7 @@ export class GraphKeys {
|
|
|
415
434
|
const { actions, graphOptions } = this.plan;
|
|
416
435
|
const givenValues = {};
|
|
417
436
|
|
|
418
|
-
for (const node of
|
|
437
|
+
for (const node of row.nodes) {
|
|
419
438
|
const action = actions.get(node);
|
|
420
439
|
|
|
421
440
|
if (
|
|
@@ -476,12 +495,12 @@ export class GraphKeys {
|
|
|
476
495
|
// current value.
|
|
477
496
|
_getAnchorValue(cell) {
|
|
478
497
|
const { row, prop, index } = cell;
|
|
479
|
-
const nodes =
|
|
498
|
+
const { nodes } = row;
|
|
480
499
|
const [node] = nodes;
|
|
481
500
|
const getValue = (obj) => (obj ? prop.getProp(obj, index) : undefined);
|
|
482
501
|
const { writtenValue } = this._getGivenValues(cell);
|
|
483
502
|
|
|
484
|
-
if (
|
|
503
|
+
if (row.isInsert) {
|
|
485
504
|
const insertNode = nodes.find((node) => this.plan.actions.get(node) === 'insert');
|
|
486
505
|
return writtenValue ?? getValue(insertNode.obj) ?? null;
|
|
487
506
|
} else if (writtenValue !== undefined) {
|
|
@@ -505,7 +524,7 @@ export class GraphKeys {
|
|
|
505
524
|
// Return true if an anchor cell waits for an insert or a `#ref{}` value:
|
|
506
525
|
_isWaiting(cell) {
|
|
507
526
|
return (
|
|
508
|
-
(
|
|
527
|
+
(cell.row.isInsert && !cell.row.isInserted) ||
|
|
509
528
|
this._getGivenValues(cell).writtenValue === UNRESOLVED
|
|
510
529
|
);
|
|
511
530
|
}
|
|
@@ -516,14 +535,14 @@ export class GraphKeys {
|
|
|
516
535
|
_getKnownKey(cell) {
|
|
517
536
|
const { row, prop, index } = cell;
|
|
518
537
|
|
|
519
|
-
if (
|
|
538
|
+
if (row.isInsert) {
|
|
520
539
|
const { value } = this._getGivenValues(cell);
|
|
521
540
|
return value === UNRESOLVED
|
|
522
541
|
? undefined
|
|
523
542
|
: value !== undefined
|
|
524
543
|
? `=${value}`
|
|
525
544
|
: cell.isId
|
|
526
|
-
? row
|
|
545
|
+
? row.key
|
|
527
546
|
: undefined;
|
|
528
547
|
} else {
|
|
529
548
|
const value = this._isWaiting(cell) ? undefined : this._getAnchorValue(cell);
|
|
@@ -566,7 +585,7 @@ export class GraphKeys {
|
|
|
566
585
|
_getStoredValue(cell) {
|
|
567
586
|
const { row, prop, index } = cell;
|
|
568
587
|
|
|
569
|
-
for (const node of
|
|
588
|
+
for (const node of row.nodes) {
|
|
570
589
|
const value =
|
|
571
590
|
!this.activeNodes.has(node) && prop.getProp(node.obj, index) !== undefined
|
|
572
591
|
? prop.getProp(node.obj, index)
|
|
@@ -582,7 +601,7 @@ export class GraphKeys {
|
|
|
582
601
|
|
|
583
602
|
// Return true if another node than the ones that read `cell` clears it:
|
|
584
603
|
_isCleared(cell) {
|
|
585
|
-
const clearingNodes =
|
|
604
|
+
const clearingNodes = cell.row.clearingNodes.get(cell.description);
|
|
586
605
|
return (cell.isCleared ??=
|
|
587
606
|
!!clearingNodes && [...clearingNodes].some((node) => !cell.readerNodes.has(node)));
|
|
588
607
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { QueryBuilderOperation } from './QueryBuilderOperation.js';
|
|
2
|
+
import { asArray } from '../../utils/objectUtils.js';
|
|
3
|
+
|
|
4
|
+
// Wrap the result in an array in the order of the operations, so a `first()`
|
|
5
|
+
// added after this unwraps it again.
|
|
6
|
+
export class AsArrayOperation extends QueryBuilderOperation {
|
|
7
|
+
onAfter3(_, result) {
|
|
8
|
+
return asArray(result);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { isEmpty } from '../../utils/objectUtils.js';
|
|
1
|
+
import { hasOwn, isEmpty } from '../../utils/objectUtils.js';
|
|
2
2
|
import { QueryBuilderOperation } from './QueryBuilderOperation.js';
|
|
3
3
|
import { StaticHookArguments } from '../StaticHookArguments.js';
|
|
4
4
|
import { removeOverriddenValues } from '../../model/modelQueryProps.js';
|
|
5
5
|
import {
|
|
6
6
|
checkFieldExpressions,
|
|
7
7
|
convertFieldExpressionsToRaw,
|
|
8
|
+
getJsonKeyPatch,
|
|
8
9
|
removeFieldExpressions,
|
|
9
10
|
} from './fieldExpressions.js';
|
|
10
11
|
|
|
@@ -65,17 +66,26 @@ export class UpdateOperation extends QueryBuilderOperation {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
onBuildKnex(knexBuilder, builder) {
|
|
68
|
-
|
|
69
|
+
if (!this.modelOptions.patch) {
|
|
70
|
+
checkFieldExpressions(builder.modelClass(), this.model, 'update');
|
|
71
|
+
}
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
);
|
|
73
|
+
const json = this.model.$toDatabaseJson(builder);
|
|
74
|
+
const { jsonKeyPatches } = builder.internalOptions();
|
|
75
|
+
|
|
76
|
+
// The path updates are join expressions in column names, so they replace
|
|
77
|
+
// the mapped columns in the json:
|
|
78
|
+
for (const prop of jsonKeyPatches?.keys() || []) {
|
|
79
|
+
const patch = getJsonKeyPatch(this.model, prop, jsonKeyPatches);
|
|
80
|
+
const column = builder.modelClass().propertyNameToColumnName(prop);
|
|
81
|
+
|
|
82
|
+
if (patch && hasOwn(json, column)) {
|
|
83
|
+
delete json[column];
|
|
84
|
+
Object.assign(json, patch);
|
|
85
|
+
}
|
|
78
86
|
}
|
|
87
|
+
|
|
88
|
+
return knexBuilder.update(convertFieldExpressionsToRaw(builder, json));
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
async onAfter2(builder, numUpdated) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ref } from '../ReferenceBuilder.js';
|
|
2
|
-
import { isObject } from '../../utils/objectUtils.js';
|
|
2
|
+
import { isObject, jsonEquals } from '../../utils/objectUtils.js';
|
|
3
3
|
import { getFieldExpressionColumn } from '../../utils/parseFieldExpression.js';
|
|
4
4
|
import { isKnexRaw, isKnexQueryBuilder } from '../../utils/knexUtils.js';
|
|
5
5
|
import { getJsonDialect } from '../jsonDialects/index.js';
|
|
@@ -7,14 +7,13 @@ import { ValidationErrorType } from '../../model/ValidationError.js';
|
|
|
7
7
|
import { getQueryPropKeys } from '../../model/modelQueryProps.js';
|
|
8
8
|
|
|
9
9
|
// Inserts and updates write whole values, so they can't write into json
|
|
10
|
-
// columns with field expressions like `'meta:color'
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
export function checkFieldExpressions(modelClass, model, operation, allowedKeys = []) {
|
|
10
|
+
// columns with field expressions like `'meta:color'`. Keys with a ':' that don't
|
|
11
|
+
// parse as field expressions are columns, as in the query properties.
|
|
12
|
+
export function checkFieldExpressions(modelClass, model, operation) {
|
|
14
13
|
const keys = getFieldExpressionKeys(modelClass, [
|
|
15
14
|
...Object.keys(model),
|
|
16
15
|
...getQueryPropKeys(model),
|
|
17
|
-
])
|
|
16
|
+
]);
|
|
18
17
|
|
|
19
18
|
if (keys.length > 0) {
|
|
20
19
|
throw modelClass.createValidationError({
|
|
@@ -29,6 +28,15 @@ export function checkFieldExpressions(modelClass, model, operation, allowedKeys
|
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
|
|
31
|
+
// Return the path updates that write the keys that `upsertGraph()` gives the
|
|
32
|
+
// json column `prop` of `model`, while the column holds nothing but these keys,
|
|
33
|
+
// which keeps its other values (#415). A column that hooks change is written
|
|
34
|
+
// whole (#488):
|
|
35
|
+
export function getJsonKeyPatch(model, prop, jsonKeyPatches) {
|
|
36
|
+
const entry = jsonKeyPatches?.get(prop);
|
|
37
|
+
return entry && jsonEquals(model[prop], entry.keys) ? entry.patch : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
// Field expressions only describe the writes into json columns, they aren't
|
|
33
41
|
// properties of the models that the writes return:
|
|
34
42
|
export function removeFieldExpressions(modelClass, model) {
|
|
@@ -126,6 +126,11 @@ export class RelationProperty {
|
|
|
126
126
|
return this._propSetters[index](obj, value);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// Returns true if the index:th column is a whole column.
|
|
130
|
+
isColumn(index) {
|
|
131
|
+
return this._refs[index].isPlainColumnRef;
|
|
132
|
+
}
|
|
133
|
+
|
|
129
134
|
// Returns true if the index:th column is a key at the top level of a json
|
|
130
135
|
// column.
|
|
131
136
|
isJsonKey(index) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objection",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "An SQL-friendly ORM for Node.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/objection.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"docs:preview": "vitepress preview doc"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
|
-
"tag": "
|
|
32
|
+
"tag": "latest"
|
|
33
33
|
},
|
|
34
34
|
"author": {
|
|
35
35
|
"name": "Sami Koskimäki",
|
|
@@ -76,7 +76,13 @@
|
|
|
76
76
|
"ajv-formats": "^3.0.1"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"knex": "^3.3.0"
|
|
79
|
+
"knex": "^3.3.0",
|
|
80
|
+
"typescript": ">=5.8"
|
|
81
|
+
},
|
|
82
|
+
"peerDependenciesMeta": {
|
|
83
|
+
"typescript": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
80
86
|
},
|
|
81
87
|
"devDependencies": {
|
|
82
88
|
"@types/node": "^22.15.3",
|