@spinajs/orm 2.0.480 → 2.0.482
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/README.md +72 -3
- package/lib/cjs/bootstrap.js +10 -0
- package/lib/cjs/bootstrap.js.map +1 -1
- package/lib/cjs/builders.d.ts +115 -11
- package/lib/cjs/builders.d.ts.map +1 -1
- package/lib/cjs/builders.js +342 -283
- package/lib/cjs/builders.js.map +1 -1
- package/lib/cjs/converters.d.ts +9 -2
- package/lib/cjs/converters.d.ts.map +1 -1
- package/lib/cjs/converters.js +48 -8
- package/lib/cjs/converters.js.map +1 -1
- package/lib/cjs/decorators.d.ts +26 -3
- package/lib/cjs/decorators.d.ts.map +1 -1
- package/lib/cjs/decorators.js +95 -42
- package/lib/cjs/decorators.js.map +1 -1
- package/lib/cjs/dehydrators.js +1 -1
- package/lib/cjs/dehydrators.js.map +1 -1
- package/lib/cjs/descriptor.d.ts +1 -0
- package/lib/cjs/descriptor.d.ts.map +1 -1
- package/lib/cjs/descriptor.js +16 -40
- package/lib/cjs/descriptor.js.map +1 -1
- package/lib/cjs/driver.d.ts +136 -7
- package/lib/cjs/driver.d.ts.map +1 -1
- package/lib/cjs/driver.js +279 -0
- package/lib/cjs/driver.js.map +1 -1
- package/lib/cjs/existsRelationHandlers.d.ts +29 -0
- package/lib/cjs/existsRelationHandlers.d.ts.map +1 -0
- package/lib/cjs/existsRelationHandlers.js +104 -0
- package/lib/cjs/existsRelationHandlers.js.map +1 -0
- package/lib/cjs/fp.d.ts.map +1 -1
- package/lib/cjs/fp.js +10 -15
- package/lib/cjs/fp.js.map +1 -1
- package/lib/cjs/hydrators.d.ts.map +1 -1
- package/lib/cjs/hydrators.js +29 -2
- package/lib/cjs/hydrators.js.map +1 -1
- package/lib/cjs/identity-map.d.ts +60 -0
- package/lib/cjs/identity-map.d.ts.map +1 -0
- package/lib/cjs/identity-map.js +135 -0
- package/lib/cjs/identity-map.js.map +1 -0
- package/lib/cjs/index.d.ts +12 -0
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +12 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/interfaces.d.ts +295 -9
- package/lib/cjs/interfaces.d.ts.map +1 -1
- package/lib/cjs/interfaces.js +54 -1
- package/lib/cjs/interfaces.js.map +1 -1
- package/lib/cjs/metadata.d.ts.map +1 -1
- package/lib/cjs/metadata.js +4 -1
- package/lib/cjs/metadata.js.map +1 -1
- package/lib/cjs/metrics.d.ts +53 -0
- package/lib/cjs/metrics.d.ts.map +1 -0
- package/lib/cjs/metrics.js +82 -0
- package/lib/cjs/metrics.js.map +1 -0
- package/lib/cjs/middlewares.d.ts +13 -1
- package/lib/cjs/middlewares.d.ts.map +1 -1
- package/lib/cjs/middlewares.js +55 -15
- package/lib/cjs/middlewares.js.map +1 -1
- package/lib/cjs/model.d.ts +129 -13
- package/lib/cjs/model.d.ts.map +1 -1
- package/lib/cjs/model.js +449 -93
- package/lib/cjs/model.js.map +1 -1
- package/lib/cjs/orm.d.ts.map +1 -1
- package/lib/cjs/orm.js +12 -3
- package/lib/cjs/orm.js.map +1 -1
- package/lib/cjs/orphan.d.ts +27 -0
- package/lib/cjs/orphan.d.ts.map +1 -0
- package/lib/cjs/orphan.js +55 -0
- package/lib/cjs/orphan.js.map +1 -0
- package/lib/cjs/primary-keys.d.ts +60 -0
- package/lib/cjs/primary-keys.d.ts.map +1 -0
- package/lib/cjs/primary-keys.js +243 -0
- package/lib/cjs/primary-keys.js.map +1 -0
- package/lib/cjs/relation-objects.d.ts +101 -9
- package/lib/cjs/relation-objects.d.ts.map +1 -1
- package/lib/cjs/relation-objects.js +194 -43
- package/lib/cjs/relation-objects.js.map +1 -1
- package/lib/cjs/relations.d.ts +7 -1
- package/lib/cjs/relations.d.ts.map +1 -1
- package/lib/cjs/relations.js +16 -1
- package/lib/cjs/relations.js.map +1 -1
- package/lib/cjs/resilience.d.ts +50 -0
- package/lib/cjs/resilience.d.ts.map +1 -0
- package/lib/cjs/resilience.js +70 -0
- package/lib/cjs/resilience.js.map +1 -0
- package/lib/cjs/snapshot.d.ts +58 -0
- package/lib/cjs/snapshot.d.ts.map +1 -0
- package/lib/cjs/snapshot.js +125 -0
- package/lib/cjs/snapshot.js.map +1 -0
- package/lib/cjs/statements.d.ts +11 -1
- package/lib/cjs/statements.d.ts.map +1 -1
- package/lib/cjs/statements.js +13 -2
- package/lib/cjs/statements.js.map +1 -1
- package/lib/cjs/subject-builder.d.ts +79 -0
- package/lib/cjs/subject-builder.d.ts.map +1 -0
- package/lib/cjs/subject-builder.js +296 -0
- package/lib/cjs/subject-builder.js.map +1 -0
- package/lib/cjs/subject-executor.d.ts +122 -0
- package/lib/cjs/subject-executor.d.ts.map +1 -0
- package/lib/cjs/subject-executor.js +319 -0
- package/lib/cjs/subject-executor.js.map +1 -0
- package/lib/cjs/subject-sorter.d.ts +70 -0
- package/lib/cjs/subject-sorter.d.ts.map +1 -0
- package/lib/cjs/subject-sorter.js +173 -0
- package/lib/cjs/subject-sorter.js.map +1 -0
- package/lib/cjs/subject.d.ts +100 -0
- package/lib/cjs/subject.d.ts.map +1 -0
- package/lib/cjs/subject.js +89 -0
- package/lib/cjs/subject.js.map +1 -0
- package/lib/cjs/unit-of-work.d.ts +52 -0
- package/lib/cjs/unit-of-work.d.ts.map +1 -0
- package/lib/cjs/unit-of-work.js +170 -0
- package/lib/cjs/unit-of-work.js.map +1 -0
- package/lib/mjs/bootstrap.js +10 -0
- package/lib/mjs/bootstrap.js.map +1 -1
- package/lib/mjs/builders.d.ts +115 -11
- package/lib/mjs/builders.d.ts.map +1 -1
- package/lib/mjs/builders.js +344 -285
- package/lib/mjs/builders.js.map +1 -1
- package/lib/mjs/converters.d.ts +9 -2
- package/lib/mjs/converters.d.ts.map +1 -1
- package/lib/mjs/converters.js +48 -8
- package/lib/mjs/converters.js.map +1 -1
- package/lib/mjs/decorators.d.ts +26 -3
- package/lib/mjs/decorators.d.ts.map +1 -1
- package/lib/mjs/decorators.js +96 -44
- package/lib/mjs/decorators.js.map +1 -1
- package/lib/mjs/dehydrators.js +1 -1
- package/lib/mjs/dehydrators.js.map +1 -1
- package/lib/mjs/descriptor.d.ts +1 -0
- package/lib/mjs/descriptor.d.ts.map +1 -1
- package/lib/mjs/descriptor.js +17 -39
- package/lib/mjs/descriptor.js.map +1 -1
- package/lib/mjs/driver.d.ts +136 -7
- package/lib/mjs/driver.d.ts.map +1 -1
- package/lib/mjs/driver.js +279 -0
- package/lib/mjs/driver.js.map +1 -1
- package/lib/mjs/existsRelationHandlers.d.ts +29 -0
- package/lib/mjs/existsRelationHandlers.d.ts.map +1 -0
- package/lib/mjs/existsRelationHandlers.js +100 -0
- package/lib/mjs/existsRelationHandlers.js.map +1 -0
- package/lib/mjs/fp.d.ts.map +1 -1
- package/lib/mjs/fp.js +10 -15
- package/lib/mjs/fp.js.map +1 -1
- package/lib/mjs/hydrators.d.ts.map +1 -1
- package/lib/mjs/hydrators.js +29 -2
- package/lib/mjs/hydrators.js.map +1 -1
- package/lib/mjs/identity-map.d.ts +60 -0
- package/lib/mjs/identity-map.d.ts.map +1 -0
- package/lib/mjs/identity-map.js +130 -0
- package/lib/mjs/identity-map.js.map +1 -0
- package/lib/mjs/index.d.ts +12 -0
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +12 -0
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/interfaces.d.ts +295 -9
- package/lib/mjs/interfaces.d.ts.map +1 -1
- package/lib/mjs/interfaces.js +53 -0
- package/lib/mjs/interfaces.js.map +1 -1
- package/lib/mjs/metadata.d.ts.map +1 -1
- package/lib/mjs/metadata.js +4 -1
- package/lib/mjs/metadata.js.map +1 -1
- package/lib/mjs/metrics.d.ts +53 -0
- package/lib/mjs/metrics.d.ts.map +1 -0
- package/lib/mjs/metrics.js +76 -0
- package/lib/mjs/metrics.js.map +1 -0
- package/lib/mjs/middlewares.d.ts +13 -1
- package/lib/mjs/middlewares.d.ts.map +1 -1
- package/lib/mjs/middlewares.js +55 -15
- package/lib/mjs/middlewares.js.map +1 -1
- package/lib/mjs/model.d.ts +129 -13
- package/lib/mjs/model.d.ts.map +1 -1
- package/lib/mjs/model.js +451 -95
- package/lib/mjs/model.js.map +1 -1
- package/lib/mjs/orm.d.ts.map +1 -1
- package/lib/mjs/orm.js +12 -3
- package/lib/mjs/orm.js.map +1 -1
- package/lib/mjs/orphan.d.ts +27 -0
- package/lib/mjs/orphan.d.ts.map +1 -0
- package/lib/mjs/orphan.js +52 -0
- package/lib/mjs/orphan.js.map +1 -0
- package/lib/mjs/primary-keys.d.ts +60 -0
- package/lib/mjs/primary-keys.d.ts.map +1 -0
- package/lib/mjs/primary-keys.js +223 -0
- package/lib/mjs/primary-keys.js.map +1 -0
- package/lib/mjs/relation-objects.d.ts +101 -9
- package/lib/mjs/relation-objects.d.ts.map +1 -1
- package/lib/mjs/relation-objects.js +194 -43
- package/lib/mjs/relation-objects.js.map +1 -1
- package/lib/mjs/relations.d.ts +7 -1
- package/lib/mjs/relations.d.ts.map +1 -1
- package/lib/mjs/relations.js +16 -1
- package/lib/mjs/relations.js.map +1 -1
- package/lib/mjs/resilience.d.ts +50 -0
- package/lib/mjs/resilience.d.ts.map +1 -0
- package/lib/mjs/resilience.js +64 -0
- package/lib/mjs/resilience.js.map +1 -0
- package/lib/mjs/snapshot.d.ts +58 -0
- package/lib/mjs/snapshot.d.ts.map +1 -0
- package/lib/mjs/snapshot.js +115 -0
- package/lib/mjs/snapshot.js.map +1 -0
- package/lib/mjs/statements.d.ts +11 -1
- package/lib/mjs/statements.d.ts.map +1 -1
- package/lib/mjs/statements.js +14 -3
- package/lib/mjs/statements.js.map +1 -1
- package/lib/mjs/subject-builder.d.ts +79 -0
- package/lib/mjs/subject-builder.d.ts.map +1 -0
- package/lib/mjs/subject-builder.js +292 -0
- package/lib/mjs/subject-builder.js.map +1 -0
- package/lib/mjs/subject-executor.d.ts +122 -0
- package/lib/mjs/subject-executor.d.ts.map +1 -0
- package/lib/mjs/subject-executor.js +312 -0
- package/lib/mjs/subject-executor.js.map +1 -0
- package/lib/mjs/subject-sorter.d.ts +70 -0
- package/lib/mjs/subject-sorter.d.ts.map +1 -0
- package/lib/mjs/subject-sorter.js +168 -0
- package/lib/mjs/subject-sorter.js.map +1 -0
- package/lib/mjs/subject.d.ts +100 -0
- package/lib/mjs/subject.d.ts.map +1 -0
- package/lib/mjs/subject.js +84 -0
- package/lib/mjs/subject.js.map +1 -0
- package/lib/mjs/unit-of-work.d.ts +52 -0
- package/lib/mjs/unit-of-work.d.ts.map +1 -0
- package/lib/mjs/unit-of-work.js +166 -0
- package/lib/mjs/unit-of-work.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +9 -7
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/* eslint-disable prettier/prettier */
|
|
2
|
+
import { extractModelDescriptor } from './descriptor.js';
|
|
3
|
+
import { identityKey } from './identity-map.js';
|
|
4
|
+
import { OrphanPolicy, RelationType } from './interfaces.js';
|
|
5
|
+
import { resolveOrphanPolicy } from './orphan.js';
|
|
6
|
+
import { SingleRelation } from './relation-objects.js';
|
|
7
|
+
import { OrmException } from './exceptions.js';
|
|
8
|
+
import { Subject, SubjectOperation, SubjectSet } from './subject.js';
|
|
9
|
+
/**
|
|
10
|
+
* Turns a mutated object graph into a `SubjectSet` — the complete, unordered description of
|
|
11
|
+
* what one `save()` must do.
|
|
12
|
+
*
|
|
13
|
+
* Traversal rules:
|
|
14
|
+
*
|
|
15
|
+
* - a `belongsTo` is followed whenever its `Value` is set, populated or not, because
|
|
16
|
+
* attaching a model to it is an explicit act;
|
|
17
|
+
* - a `hasMany` or `manyToMany` is followed **only when `Populated` is true**, so a relation
|
|
18
|
+
* the caller never loaded is invisible and `Items: OrderItem[] = []` on a freshly
|
|
19
|
+
* constructed model deletes nothing;
|
|
20
|
+
* - `Query` and `Virtual` relations are read-only projections and are never followed.
|
|
21
|
+
*/
|
|
22
|
+
export class SubjectBuilder {
|
|
23
|
+
constructor(identityMap) {
|
|
24
|
+
this.identityMap = identityMap;
|
|
25
|
+
this._visited = new Set();
|
|
26
|
+
}
|
|
27
|
+
build(root) {
|
|
28
|
+
return this.buildFrom(this.collect(root));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Walks the graph breadth-first from `root`, canonicalizing every model through the
|
|
32
|
+
* identity map so a row reached by two paths yields one instance.
|
|
33
|
+
*
|
|
34
|
+
* @param root - the model `save()` was called on
|
|
35
|
+
*/
|
|
36
|
+
collect(root) {
|
|
37
|
+
this._visited = new Set();
|
|
38
|
+
const ordered = [];
|
|
39
|
+
const queue = [this.identityMap.add(root)];
|
|
40
|
+
while (queue.length > 0) {
|
|
41
|
+
const model = queue.shift();
|
|
42
|
+
if (this._visited.has(model)) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
this._visited.add(model);
|
|
46
|
+
ordered.push(model);
|
|
47
|
+
for (const related of this.relatedOf(model)) {
|
|
48
|
+
const canonical = this.identityMap.add(related);
|
|
49
|
+
if (!this._visited.has(canonical)) {
|
|
50
|
+
queue.push(canonical);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return ordered;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Models directly reachable from `model` under the traversal rules.
|
|
58
|
+
*/
|
|
59
|
+
relatedOf(model) {
|
|
60
|
+
const descriptor = extractModelDescriptor(model.constructor);
|
|
61
|
+
if (!descriptor) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const out = [];
|
|
65
|
+
for (const [name, relation] of descriptor.Relations) {
|
|
66
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
67
|
+
const value = model[name];
|
|
68
|
+
if (value === null || value === undefined) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
switch (relation.Type) {
|
|
72
|
+
case RelationType.One:
|
|
73
|
+
if (value instanceof SingleRelation && value.Value) {
|
|
74
|
+
out.push(value.Value);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case RelationType.Many:
|
|
78
|
+
case RelationType.ManyToMany:
|
|
79
|
+
if (value.Populated === true) {
|
|
80
|
+
out.push(...[...value]);
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
case RelationType.Query:
|
|
84
|
+
case RelationType.Virtual:
|
|
85
|
+
default:
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Diffs each collected model against its snapshot and records its `belongsTo` foreign keys.
|
|
93
|
+
* hasMany, manyToMany and orphan handling are layered on by later passes.
|
|
94
|
+
*
|
|
95
|
+
* @param models - output of `collect()`
|
|
96
|
+
*/
|
|
97
|
+
buildFrom(models) {
|
|
98
|
+
const set = new SubjectSet();
|
|
99
|
+
for (const model of models) {
|
|
100
|
+
const descriptor = extractModelDescriptor(model.constructor);
|
|
101
|
+
if (!descriptor) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const subject = new Subject(model, descriptor, classify(model));
|
|
105
|
+
// Only an UPDATE needs a column list: an INSERT writes every column, and a no-op
|
|
106
|
+
// writes none. Reading the diff again here is cheap and keeps `classify` pure.
|
|
107
|
+
if (subject.Operation === SubjectOperation.Update) {
|
|
108
|
+
subject.ChangedColumns = model.changedColumns();
|
|
109
|
+
}
|
|
110
|
+
set.add(subject);
|
|
111
|
+
}
|
|
112
|
+
for (const subject of set.Subjects) {
|
|
113
|
+
this.buildBelongsTo(subject);
|
|
114
|
+
this.buildHasMany(subject, set);
|
|
115
|
+
this.buildManyToMany(subject, set);
|
|
116
|
+
}
|
|
117
|
+
this.buildOrphans(set);
|
|
118
|
+
return set;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Records, for every `belongsTo` with a `Value`, that this subject's foreign-key column
|
|
122
|
+
* takes the target's primary key. The value is *not* read here — the target may not have
|
|
123
|
+
* been inserted yet; the executor resolves it immediately before the statement.
|
|
124
|
+
*/
|
|
125
|
+
buildBelongsTo(subject) {
|
|
126
|
+
for (const [name, relation] of subject.Descriptor.Relations) {
|
|
127
|
+
if (relation.Type !== RelationType.One) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
131
|
+
const rel = subject.Model[name];
|
|
132
|
+
if (!(rel instanceof SingleRelation) || !rel.Value) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
subject.PendingForeignKeys.push({ Column: relation.ForeignKey, Target: rel.Value });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Diffs each populated `hasMany` on `subject` against the owner's relation snapshot and
|
|
140
|
+
* records the owner's foreign key as pending on every member that stays.
|
|
141
|
+
*
|
|
142
|
+
* A relation with `Populated === false` is skipped entirely — that is the anti-footgun
|
|
143
|
+
* guarantee, and it is why a freshly constructed model with `Items: OrderItem[] = []`
|
|
144
|
+
* deletes nothing.
|
|
145
|
+
*
|
|
146
|
+
* Both new *and* kept members get the pending foreign key. That is what makes re-parenting
|
|
147
|
+
* work: a clean child moved to another owner has its key rewritten and is promoted from a
|
|
148
|
+
* no-op to an UPDATE, instead of keeping its old owner id in the database ( B20 ).
|
|
149
|
+
*/
|
|
150
|
+
buildHasMany(subject, set) {
|
|
151
|
+
for (const [name, relation] of subject.Descriptor.Relations) {
|
|
152
|
+
if (relation.Type !== RelationType.Many) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
156
|
+
const rel = subject.Model[name];
|
|
157
|
+
if (!rel || rel.Populated !== true) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const members = [...rel];
|
|
161
|
+
const snapshotKeys = subject.Model.Snapshot?.Relations.get(name) ?? [];
|
|
162
|
+
const presentKeys = new Set(members.map((m) => identityKey(m.PrimaryKeyValue)).filter((k) => k !== null));
|
|
163
|
+
const delta = {
|
|
164
|
+
Descriptor: relation,
|
|
165
|
+
Added: members.filter((m) => m.Snapshot === null),
|
|
166
|
+
Kept: members.filter((m) => m.Snapshot !== null),
|
|
167
|
+
RemovedKeys: snapshotKeys.filter((k) => !presentKeys.has(identityKey(k))),
|
|
168
|
+
};
|
|
169
|
+
subject.RelationDeltas.push(delta);
|
|
170
|
+
for (const member of members) {
|
|
171
|
+
const memberSubject = set.find(member);
|
|
172
|
+
if (memberSubject) {
|
|
173
|
+
memberSubject.PendingForeignKeys.push({ Column: relation.ForeignKey, Target: subject.Model });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Diffs each populated `manyToMany` against the owner's relation snapshot into junction
|
|
180
|
+
* rows to create and destroy.
|
|
181
|
+
*
|
|
182
|
+
* Only the *junction* row is created or destroyed. A target that is new gets its own insert
|
|
183
|
+
* subject from `collect()` so its key exists before the junction row references it; a target
|
|
184
|
+
* that is merely unlinked is left completely alone — removing a tag from an order must
|
|
185
|
+
* never delete the tag.
|
|
186
|
+
*/
|
|
187
|
+
buildManyToMany(subject, set) {
|
|
188
|
+
for (const [name, relation] of subject.Descriptor.Relations) {
|
|
189
|
+
if (relation.Type !== RelationType.ManyToMany) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
193
|
+
const rel = subject.Model[name];
|
|
194
|
+
if (!rel || rel.Populated !== true) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (!relation.JunctionModel) {
|
|
198
|
+
throw new OrmException(`manyToMany relation ${relation.Name} on ${subject.Descriptor.Name} has no junction model`);
|
|
199
|
+
}
|
|
200
|
+
const junctionDescriptor = extractModelDescriptor(relation.JunctionModel);
|
|
201
|
+
if (!junctionDescriptor) {
|
|
202
|
+
throw new OrmException(`junction model ${relation.JunctionModel.name} for relation ${relation.Name} has no model descriptor`);
|
|
203
|
+
}
|
|
204
|
+
const members = [...rel];
|
|
205
|
+
const snapshotKeys = subject.Model.Snapshot?.Relations.get(name) ?? [];
|
|
206
|
+
const snapshotSet = new Set(snapshotKeys.map((k) => identityKey(k)).filter((k) => k !== null));
|
|
207
|
+
const presentKeys = new Set(members.map((m) => identityKey(m.PrimaryKeyValue)).filter((k) => k !== null));
|
|
208
|
+
const delta = {
|
|
209
|
+
Descriptor: relation,
|
|
210
|
+
JunctionDescriptor: junctionDescriptor,
|
|
211
|
+
Owner: subject.Model,
|
|
212
|
+
// A member with no key yet cannot have been linked before, so it is always an add.
|
|
213
|
+
Added: members.filter((m) => {
|
|
214
|
+
const key = identityKey(m.PrimaryKeyValue);
|
|
215
|
+
return key === null || !snapshotSet.has(key);
|
|
216
|
+
}),
|
|
217
|
+
RemovedKeys: snapshotKeys.filter((k) => !presentKeys.has(identityKey(k))),
|
|
218
|
+
};
|
|
219
|
+
set.Junctions.push(delta);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Turns every `hasMany` removal recorded during the diff into an orphan action.
|
|
224
|
+
*
|
|
225
|
+
* Runs once over the whole set rather than per relation, because the decision needs global
|
|
226
|
+
* information: a child removed from one owner and pushed onto another is a re-parent, not
|
|
227
|
+
* an orphan, and only a set-wide view can tell the two apart. Every key that has a live
|
|
228
|
+
* subject of the same target model in this graph is therefore subtracted.
|
|
229
|
+
*/
|
|
230
|
+
buildOrphans(set) {
|
|
231
|
+
// Primary keys, per target model, that are alive somewhere in this graph. Keyed on
|
|
232
|
+
// TableName rather than on the constructor: a discrimination map can produce several
|
|
233
|
+
// constructors for one table, and a subclass instance is still the same row.
|
|
234
|
+
const alive = new Map();
|
|
235
|
+
for (const subject of set.Subjects) {
|
|
236
|
+
const key = identityKey(subject.Model.PrimaryKeyValue);
|
|
237
|
+
if (key === null) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const table = subject.Descriptor.TableName;
|
|
241
|
+
if (!alive.has(table)) {
|
|
242
|
+
alive.set(table, new Set());
|
|
243
|
+
}
|
|
244
|
+
alive.get(table).add(key);
|
|
245
|
+
}
|
|
246
|
+
for (const subject of set.Subjects) {
|
|
247
|
+
for (const delta of subject.RelationDeltas) {
|
|
248
|
+
if (delta.RemovedKeys.length === 0) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const targetDescriptor = extractModelDescriptor(delta.Descriptor.TargetModel);
|
|
252
|
+
if (!targetDescriptor) {
|
|
253
|
+
throw new OrmException(`relation ${delta.Descriptor.Name} on ${subject.Descriptor.Name} has an unresolved target model`);
|
|
254
|
+
}
|
|
255
|
+
const policy = resolveOrphanPolicy(delta.Descriptor, targetDescriptor);
|
|
256
|
+
if (policy === OrphanPolicy.Disable) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const survivors = alive.get(targetDescriptor.TableName) ?? new Set();
|
|
260
|
+
const keys = delta.RemovedKeys.filter((k) => {
|
|
261
|
+
const key = identityKey(k);
|
|
262
|
+
return key !== null && !survivors.has(key);
|
|
263
|
+
});
|
|
264
|
+
// Drop an empty list rather than emitting a statement with `IN ()`.
|
|
265
|
+
if (keys.length === 0) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const orphan = {
|
|
269
|
+
Descriptor: delta.Descriptor,
|
|
270
|
+
TargetDescriptor: targetDescriptor,
|
|
271
|
+
Policy: policy,
|
|
272
|
+
PrimaryKeys: keys,
|
|
273
|
+
};
|
|
274
|
+
set.Orphans.push(orphan);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* `Insert` when the model has never been hydrated from the database, `Update` when its
|
|
281
|
+
* snapshot diff is non-empty, `None` otherwise.
|
|
282
|
+
*
|
|
283
|
+
* Deliberately not keyed on the primary key: `setDefaults()` pre-fills @Uuid keys on
|
|
284
|
+
* construction, so a brand-new UUID-keyed model already has one.
|
|
285
|
+
*/
|
|
286
|
+
function classify(model) {
|
|
287
|
+
if (model.Snapshot === null) {
|
|
288
|
+
return SubjectOperation.Insert;
|
|
289
|
+
}
|
|
290
|
+
return model.changedColumns().length > 0 ? SubjectOperation.Update : SubjectOperation.None;
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=subject-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject-builder.js","sourceRoot":"","sources":["../../src/subject-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { ISaveOptions, ISaveResult, OrphanPolicy } from './interfaces.js';
|
|
2
|
+
import { ISortedPlan } from './subject-sorter.js';
|
|
3
|
+
import { IJunctionDelta, IOrphanDelta, Subject } from './subject.js';
|
|
4
|
+
/** Rows per batched statement when the caller does not say. */
|
|
5
|
+
export declare const DEFAULT_SAVE_CHUNK = 100;
|
|
6
|
+
/**
|
|
7
|
+
* Runs a sorted plan.
|
|
8
|
+
*
|
|
9
|
+
* Every statement is built through `createQuery`, so table naming, schema qualification and
|
|
10
|
+
* identifier escaping are the same as on the ActiveRecord paths. No connection is threaded by
|
|
11
|
+
* hand: `OrmDriver.transaction()` carries it in `AsyncLocalStorage` and the builders resolved
|
|
12
|
+
* from that driver pick it up automatically.
|
|
13
|
+
*
|
|
14
|
+
* The executor does **not** open the transaction — `UnitOfWork` does — so that a caller can
|
|
15
|
+
* drive it directly in a test and so that nested saves inside one transaction fold into
|
|
16
|
+
* savepoints rather than each opening their own.
|
|
17
|
+
*/
|
|
18
|
+
export declare class SubjectExecutor {
|
|
19
|
+
protected options: ISaveOptions;
|
|
20
|
+
constructor(options: ISaveOptions);
|
|
21
|
+
execute(plan: ISortedPlan): Promise<ISaveResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Inserts every insert subject in the order the sorter produced, reading each generated key
|
|
24
|
+
* back before moving on so the next subject's foreign keys can be resolved.
|
|
25
|
+
*
|
|
26
|
+
* One statement per row, deliberately, and NOT subject to `options.chunk`: a batched
|
|
27
|
+
* multi-row INSERT can only return keys where the dialect supports RETURNING or where
|
|
28
|
+
* `insertIdIsFirstOfBatch` holds, and a subject's key is needed by the very next subject in
|
|
29
|
+
* the order. Batching here is a real feature — it has to carry the per-dialect key-backfill
|
|
30
|
+
* rules with it — not something to fold into the chunking used for junction and orphan rows.
|
|
31
|
+
*/
|
|
32
|
+
protected runInserts(plan: ISortedPlan, result: ISaveResult): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* One INSERT, with the key read back.
|
|
35
|
+
*
|
|
36
|
+
* Mirrors `ModelBase.insert()`'s key handling rather than inventing a second one: client-side
|
|
37
|
+
* keys are generated first, `assigned` keys are asserted before anything touches the database,
|
|
38
|
+
* and RETURNING is requested when an `auto` key needs to come back and the dialect supports it.
|
|
39
|
+
*/
|
|
40
|
+
protected insertOne(subject: Subject): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Runs every update, including the follow-up updates that carry deferred self-referencing
|
|
43
|
+
* foreign keys.
|
|
44
|
+
*
|
|
45
|
+
* A subject whose payload comes out empty emits nothing. This is the single place that
|
|
46
|
+
* decides whether a row actually changed: pending foreign keys are written onto the model
|
|
47
|
+
* first and `changedColumns()` is read afterwards, so a re-parented child that was clean
|
|
48
|
+
* when the subjects were built is caught here and nowhere else.
|
|
49
|
+
*/
|
|
50
|
+
protected runUpdates(plan: ISortedPlan, result: ISaveResult): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* The column payload for one UPDATE, or `null` when there is nothing to write.
|
|
53
|
+
*
|
|
54
|
+
* The primary key columns are excluded: writing them is a no-op at best and, for a model
|
|
55
|
+
* whose key column differs from its snapshot for any other reason, a silent identity change.
|
|
56
|
+
*
|
|
57
|
+
* @param subject - an update subject, or an insert subject with deferred foreign keys
|
|
58
|
+
*/
|
|
59
|
+
protected updatePayload(subject: Subject): Record<string, unknown> | null;
|
|
60
|
+
/**
|
|
61
|
+
* The column payload for one INSERT.
|
|
62
|
+
*
|
|
63
|
+
* Starts from the model's own serialization, drops every column whose foreign key is
|
|
64
|
+
* deferred to a follow-up UPDATE, then writes every pending foreign key from its target's
|
|
65
|
+
* now-known primary key. The overwrite matters: `StandardModelToSqlConverter` already wrote
|
|
66
|
+
* that column from the relation object, and for a target inserted moments ago that value was
|
|
67
|
+
* `undefined` when the model was serialized.
|
|
68
|
+
*/
|
|
69
|
+
protected insertPayload(subject: Subject): Record<string, unknown>;
|
|
70
|
+
/**
|
|
71
|
+
* Writes a generated key onto the model.
|
|
72
|
+
*
|
|
73
|
+
* Uses `setPkValue` — the primary-keys helper that assigns the key columns and nothing else —
|
|
74
|
+
* rather than the `PrimaryKeyValue` setter, whose `RelationType.One` branch also writes the
|
|
75
|
+
* new key onto the owner's `SingleRelation` wrapper, which persists nothing. The executor
|
|
76
|
+
* resolves foreign keys itself and does not want that side effect.
|
|
77
|
+
*
|
|
78
|
+
* A key the caller already supplied ( uuid / assigned strategies ) is never overwritten.
|
|
79
|
+
*/
|
|
80
|
+
protected backfillKey(subject: Subject, returning: any[], lastInsertId: number, needsKeyBack: boolean): void;
|
|
81
|
+
/**
|
|
82
|
+
* Creates and destroys junction rows.
|
|
83
|
+
*
|
|
84
|
+
* Rows are written column-first rather than through the junction model, so a junction model
|
|
85
|
+
* is not required to declare `@BelongsTo` on both sides — `ManyToManyRelationList.update()`
|
|
86
|
+
* does require that, and none of the existing junction fixtures satisfy it.
|
|
87
|
+
*
|
|
88
|
+
* Inserts run before deletes so that re-linking the same pair inside one save cannot
|
|
89
|
+
* momentarily violate a unique constraint on the junction table in the other order.
|
|
90
|
+
*/
|
|
91
|
+
protected runJunctions(plan: ISortedPlan, result: ISaveResult): Promise<void>;
|
|
92
|
+
protected insertJunctionRows(delta: IJunctionDelta, result: ISaveResult): Promise<void>;
|
|
93
|
+
protected deleteJunctionRows(delta: IJunctionDelta, result: ISaveResult): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Applies the orphan policy to every detached row.
|
|
96
|
+
*
|
|
97
|
+
* `nullify` and `soft-delete` run first as UPDATEs, then `delete` runs as DELETEs, and the
|
|
98
|
+
* deltas arrive from the sorter already ordered children-before-parents so a delete cannot
|
|
99
|
+
* strand a foreign key.
|
|
100
|
+
*
|
|
101
|
+
* `createQuery` only adds the default `DeletedAt IS NULL` filter to a SelectQueryBuilder, so
|
|
102
|
+
* these builders are unfiltered — which is what stamping an already-soft-deleted row needs.
|
|
103
|
+
*/
|
|
104
|
+
protected runOrphans(plan: ISortedPlan, result: ISaveResult): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* The policy actually applied to `delta`.
|
|
107
|
+
*
|
|
108
|
+
* `delete` on a model that declares `@SoftDelete` degrades to `soft-delete`, so orphaning a
|
|
109
|
+
* row and calling `destroy()` on it mean the same thing. `ModelBase.destroy()` has always
|
|
110
|
+
* stamped `DeletedAt` rather than issuing a DELETE for such a model; an orphan taking the
|
|
111
|
+
* other branch made "delete this row" depend on which code path reached it, and hard-erased
|
|
112
|
+
* rows the model had declared should never be hard-erased.
|
|
113
|
+
*/
|
|
114
|
+
protected effectivePolicy(delta: IOrphanDelta): OrphanPolicy;
|
|
115
|
+
protected updateOrphans(delta: IOrphanDelta, result: ISaveResult): Promise<void>;
|
|
116
|
+
protected deleteOrphans(delta: IOrphanDelta, result: ISaveResult): Promise<void>;
|
|
117
|
+
/** Rows per batched statement. */
|
|
118
|
+
protected get chunkSize(): number;
|
|
119
|
+
/** Splits `items` into runs of at most `chunkSize`. */
|
|
120
|
+
protected chunked<T>(items: T[]): T[][];
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=subject-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject-executor.d.ts","sourceRoot":"","sources":["../../src/subject-executor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiB,YAAY,EAAE,WAAW,EAAE,YAAY,EAAwB,MAAM,iBAAiB,CAAC;AAE/G,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAErE,+DAA+D;AAC/D,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC;;;;;;;;;;;GAWG;AACH,qBAAa,eAAe;IACd,SAAS,CAAC,OAAO,EAAE,YAAY;gBAArB,OAAO,EAAE,YAAY;IAE9B,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAkB7D;;;;;;;;;OASG;cACa,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjF;;;;;;OAMG;cACa,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB1D;;;;;;;;OAQG;cACa,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjF;;;;;;;OAOG;IACH,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAyBzE;;;;;;;;OAQG;IACH,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAkBlE;;;;;;;;;OASG;IACH,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI;IA+B5G;;;;;;;;;OASG;cACa,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;cAOnE,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;cA2B7E,kBAAkB,CAAC,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB7F;;;;;;;;;OASG;cACa,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAajF;;;;;;;;OAQG;IACH,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY;cAQ5C,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;cAkBtE,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtF,kCAAkC;IAClC,SAAS,KAAK,SAAS,IAAI,MAAM,CAGhC;IAED,uDAAuD;IACvD,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE;CAOxC"}
|