@c9up/atlas 0.1.3 → 0.1.4
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/db.darwin-arm64.node +0 -0
- package/db.darwin-x64.node +0 -0
- package/db.linux-arm64-gnu.node +0 -0
- package/db.linux-x64-gnu.node +0 -0
- package/db.win32-x64-msvc.node +0 -0
- package/dist/AtlasProvider.d.ts +106 -0
- package/dist/AtlasProvider.d.ts.map +1 -0
- package/dist/AtlasProvider.js +190 -0
- package/dist/AtlasProvider.js.map +1 -0
- package/dist/BaseEntity.d.ts +255 -0
- package/dist/BaseEntity.d.ts.map +1 -0
- package/dist/BaseEntity.js +420 -0
- package/dist/BaseEntity.js.map +1 -0
- package/dist/BaseRepository.d.ts +175 -0
- package/dist/BaseRepository.d.ts.map +1 -0
- package/dist/BaseRepository.js +1394 -0
- package/dist/BaseRepository.js.map +1 -0
- package/dist/ModelQuery.d.ts +338 -0
- package/dist/ModelQuery.d.ts.map +1 -0
- package/dist/ModelQuery.js +1598 -0
- package/dist/ModelQuery.js.map +1 -0
- package/dist/Transaction.d.ts +15 -0
- package/dist/Transaction.d.ts.map +1 -0
- package/dist/Transaction.js +67 -0
- package/dist/Transaction.js.map +1 -0
- package/dist/adapters/NapiDbAdapter.d.ts +34 -0
- package/dist/adapters/NapiDbAdapter.d.ts.map +1 -0
- package/dist/adapters/NapiDbAdapter.js +96 -0
- package/dist/adapters/NapiDbAdapter.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -0
- package/dist/configure.d.ts +10 -0
- package/dist/configure.d.ts.map +1 -0
- package/dist/configure.js +25 -0
- package/dist/configure.js.map +1 -0
- package/dist/decorators/entity.d.ts +235 -0
- package/dist/decorators/entity.d.ts.map +1 -0
- package/dist/decorators/entity.js +264 -0
- package/dist/decorators/entity.js.map +1 -0
- package/dist/decorators/hooks.d.ts +70 -0
- package/dist/decorators/hooks.d.ts.map +1 -0
- package/dist/decorators/hooks.js +107 -0
- package/dist/decorators/hooks.js.map +1 -0
- package/dist/decorators/scope.d.ts +35 -0
- package/dist/decorators/scope.d.ts.map +1 -0
- package/dist/decorators/scope.js +29 -0
- package/dist/decorators/scope.js.map +1 -0
- package/dist/errors.d.ts +54 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +82 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/naming/NamingStrategy.d.ts +52 -0
- package/dist/naming/NamingStrategy.d.ts.map +1 -0
- package/dist/naming/NamingStrategy.js +65 -0
- package/dist/naming/NamingStrategy.js.map +1 -0
- package/dist/query/QueryBuilder.d.ts +95 -0
- package/dist/query/QueryBuilder.d.ts.map +1 -0
- package/dist/query/QueryBuilder.js +310 -0
- package/dist/query/QueryBuilder.js.map +1 -0
- package/dist/query/native.d.ts +18 -0
- package/dist/query/native.d.ts.map +1 -0
- package/dist/query/native.js +49 -0
- package/dist/query/native.js.map +1 -0
- package/dist/schema/Migration.d.ts +55 -0
- package/dist/schema/Migration.d.ts.map +1 -0
- package/dist/schema/Migration.js +68 -0
- package/dist/schema/Migration.js.map +1 -0
- package/dist/schema/MigrationRunner.d.ts +115 -0
- package/dist/schema/MigrationRunner.d.ts.map +1 -0
- package/dist/schema/MigrationRunner.js +402 -0
- package/dist/schema/MigrationRunner.js.map +1 -0
- package/dist/schema/Schema.d.ts +22 -0
- package/dist/schema/Schema.d.ts.map +1 -0
- package/dist/schema/Schema.js +51 -0
- package/dist/schema/Schema.js.map +1 -0
- package/dist/schema/SchemaBuilder.d.ts +14 -0
- package/dist/schema/SchemaBuilder.d.ts.map +1 -0
- package/dist/schema/SchemaBuilder.js +13 -0
- package/dist/schema/SchemaBuilder.js.map +1 -0
- package/dist/schema/Seeder.d.ts +54 -0
- package/dist/schema/Seeder.d.ts.map +1 -0
- package/dist/schema/Seeder.js +89 -0
- package/dist/schema/Seeder.js.map +1 -0
- package/dist/schema/TableBuilder.d.ts +86 -0
- package/dist/schema/TableBuilder.d.ts.map +1 -0
- package/dist/schema/TableBuilder.js +211 -0
- package/dist/schema/TableBuilder.js.map +1 -0
- package/dist/schema/types.d.ts +28 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +19 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/services/db.d.ts +22 -0
- package/dist/services/db.d.ts.map +1 -0
- package/dist/services/db.js +37 -0
- package/dist/services/db.js.map +1 -0
- package/dist/testing/DatabaseCleanup.d.ts +19 -0
- package/dist/testing/DatabaseCleanup.d.ts.map +1 -0
- package/dist/testing/DatabaseCleanup.js +40 -0
- package/dist/testing/DatabaseCleanup.js.map +1 -0
- package/dist/testing/Factory.d.ts +59 -0
- package/dist/testing/Factory.d.ts.map +1 -0
- package/dist/testing/Factory.js +105 -0
- package/dist/testing/Factory.js.map +1 -0
- package/dist/testing/TestDatabase.d.ts +25 -0
- package/dist/testing/TestDatabase.d.ts.map +1 -0
- package/dist/testing/TestDatabase.js +52 -0
- package/dist/testing/TestDatabase.js.map +1 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +4 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/utils/casing.d.ts +5 -0
- package/dist/utils/casing.d.ts.map +1 -0
- package/dist/utils/casing.js +9 -0
- package/dist/utils/casing.js.map +1 -0
- package/dist/utils/dialectFromUrl.d.ts +10 -0
- package/dist/utils/dialectFromUrl.d.ts.map +1 -0
- package/dist/utils/dialectFromUrl.js +8 -0
- package/dist/utils/dialectFromUrl.js.map +1 -0
- package/dist/utils/identifier.d.ts +19 -0
- package/dist/utils/identifier.d.ts.map +1 -0
- package/dist/utils/identifier.js +27 -0
- package/dist/utils/identifier.js.map +1 -0
- package/dist/utils/safePath.d.ts +22 -0
- package/dist/utils/safePath.d.ts.map +1 -0
- package/dist/utils/safePath.js +46 -0
- package/dist/utils/safePath.js.map +1 -0
- package/dist/utils/transactionBrand.d.ts +5 -0
- package/dist/utils/transactionBrand.d.ts.map +1 -0
- package/dist/utils/transactionBrand.js +5 -0
- package/dist/utils/transactionBrand.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseEntity — base class for all Atlas entities.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Domain event accumulation (flushed post-commit through the event bus)
|
|
6
|
+
* - `$extras` bag for ad-hoc / computed columns (32.5)
|
|
7
|
+
* - `$original` snapshot + `$dirty` diff tracking (32.2)
|
|
8
|
+
* - Serialization layer hooks (`hidden` / `visible` / `@column` serializeAs) (32.4)
|
|
9
|
+
* - Computed-property collection (32.3)
|
|
10
|
+
*
|
|
11
|
+
* @implements FR29, FR35, stories 32.1 through 32.5
|
|
12
|
+
*/
|
|
13
|
+
import { MassAssignmentError } from "./errors.js";
|
|
14
|
+
/** Symbol metadata key for the computed-property registry on an entity class. */
|
|
15
|
+
export const COMPUTED_KEY = Symbol.for("atlas:computed");
|
|
16
|
+
/** Symbol metadata key for the serialize-as / serializer overrides on columns. */
|
|
17
|
+
export const COLUMN_SERIALIZE_KEY = Symbol.for("atlas:columnSerialize");
|
|
18
|
+
/** Symbol property key used by entities to back-reference their hydrating repo. */
|
|
19
|
+
export const REPO_REF = Symbol.for("atlas:repoRef");
|
|
20
|
+
/**
|
|
21
|
+
* Internal reserved keys on BaseEntity that must never be treated as database
|
|
22
|
+
* columns or serialized as data. Used by dirty tracking and by `toJSON`.
|
|
23
|
+
*/
|
|
24
|
+
const INTERNAL_KEYS = new Set(["$extras", "$original"]);
|
|
25
|
+
export class BaseEntity {
|
|
26
|
+
/** Accumulated domain events — dispatched on the event bus after DB commit. */
|
|
27
|
+
#domainEvents = [];
|
|
28
|
+
/**
|
|
29
|
+
* `$extras` — bag for ad-hoc/computed values that are NOT declared as `@Column`.
|
|
30
|
+
* Used by `withCount`, pivot extras, and aggregate loaders. Kept separate from
|
|
31
|
+
* real columns so persistence (`#entityToRow`) never tries to write them back.
|
|
32
|
+
*
|
|
33
|
+
* @implements Story 32.5
|
|
34
|
+
*/
|
|
35
|
+
$extras = {};
|
|
36
|
+
/**
|
|
37
|
+
* Snapshot of the column values at the moment this entity was hydrated from
|
|
38
|
+
* the database. Used by dirty tracking (`isDirty`, `$dirty`). Populated by
|
|
39
|
+
* `BaseRepository.#hydrate` via `markAsPersisted` below; empty for entities
|
|
40
|
+
* built in memory with `new MyEntity()`.
|
|
41
|
+
*
|
|
42
|
+
* @implements Story 32.2
|
|
43
|
+
*/
|
|
44
|
+
$original = {};
|
|
45
|
+
/** Set a property dynamically (used by hydrate/create). */
|
|
46
|
+
setProp(key, value) {
|
|
47
|
+
this[key] = value;
|
|
48
|
+
}
|
|
49
|
+
/** Set an `$extras` value (used by `withCount`, pivot extras, aggregate loaders). */
|
|
50
|
+
setExtra(key, value) {
|
|
51
|
+
this.$extras[key] = value;
|
|
52
|
+
}
|
|
53
|
+
/** Get an `$extras` value with optional default. */
|
|
54
|
+
getExtra(key, defaultValue) {
|
|
55
|
+
return this.$extras[key] ?? defaultValue;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Freeze the current column values as the "persisted" snapshot. Called by
|
|
59
|
+
* `BaseRepository.#hydrate` after a SELECT and by `save()` after INSERT/UPDATE
|
|
60
|
+
* succeeds. From now on, `isDirty()` compares against this snapshot.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Freeze the current column values as the persisted snapshot. Atlas uses
|
|
64
|
+
* **reference-based dirty tracking**: `$original` holds the SAME reference
|
|
65
|
+
* the hydrator produced, not a deep clone. A column is dirty iff
|
|
66
|
+
* `Object.is(current, original) === false`.
|
|
67
|
+
*
|
|
68
|
+
* **Contract (important)**: to mark an object/array column dirty, the user
|
|
69
|
+
* MUST reassign it — in-place mutation is undetectable by design:
|
|
70
|
+
*
|
|
71
|
+
* entity.settings = { ...entity.settings, theme: 'dark' } // ✅ dirty
|
|
72
|
+
* entity.settings.theme = 'dark' // ❌ NOT detected
|
|
73
|
+
*
|
|
74
|
+
* This matches Lucid's contract and gives us O(1) dirty checks + zero
|
|
75
|
+
* allocations on hydrate. The alternative (deep-equal with cloned snapshot)
|
|
76
|
+
* was correct but allocated a full copy of every column on every load and
|
|
77
|
+
* traversed nested JSON on every `save()` — unacceptable for hot paths.
|
|
78
|
+
*
|
|
79
|
+
* Rollback can only restore reassigned columns; in-place mutations are
|
|
80
|
+
* unrecoverable because the snapshot is the same reference as the current
|
|
81
|
+
* value. Use immutable update patterns if you rely on rollback.
|
|
82
|
+
*
|
|
83
|
+
* @implements Story 32.2
|
|
84
|
+
*/
|
|
85
|
+
markAsPersisted() {
|
|
86
|
+
const snapshot = {};
|
|
87
|
+
for (const key of Object.keys(this)) {
|
|
88
|
+
if (INTERNAL_KEYS.has(key))
|
|
89
|
+
continue;
|
|
90
|
+
snapshot[key] = this[key];
|
|
91
|
+
}
|
|
92
|
+
this.$original = snapshot;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Compute the set of columns whose current value differs from `$original`.
|
|
96
|
+
* Called on demand by `save()` (to emit UPDATEs that only touch dirty cols)
|
|
97
|
+
* and by lifecycle hooks (e.g. `beforeSave` only rehashes password if dirty).
|
|
98
|
+
*
|
|
99
|
+
* @implements Story 32.2
|
|
100
|
+
*/
|
|
101
|
+
get $dirty() {
|
|
102
|
+
const diff = {};
|
|
103
|
+
for (const key of Object.keys(this)) {
|
|
104
|
+
if (INTERNAL_KEYS.has(key))
|
|
105
|
+
continue;
|
|
106
|
+
if (!this.#columnEqualsOriginal(key)) {
|
|
107
|
+
diff[key] = this[key];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return diff;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Reference-based dirty comparison for a single column — O(1), no allocation.
|
|
114
|
+
* `Object.is` handles NaN correctly and treats same-reference objects as
|
|
115
|
+
* equal (the core of the tracking contract — see `markAsPersisted` doc).
|
|
116
|
+
*
|
|
117
|
+
* The only structural exception is `Date`: two Date instances representing
|
|
118
|
+
* the same instant are compared by `getTime()` so hydration through a driver
|
|
119
|
+
* that rebuilds Date objects from ISO strings doesn't flag spurious dirty.
|
|
120
|
+
*/
|
|
121
|
+
#columnEqualsOriginal(key) {
|
|
122
|
+
const current = this[key];
|
|
123
|
+
const original = this.$original[key];
|
|
124
|
+
if (current instanceof Date && original instanceof Date) {
|
|
125
|
+
return current.getTime() === original.getTime();
|
|
126
|
+
}
|
|
127
|
+
return Object.is(current, original);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check whether a specific column is dirty, or whether any column is dirty
|
|
131
|
+
* when called without arguments.
|
|
132
|
+
*/
|
|
133
|
+
isDirty(field) {
|
|
134
|
+
if (field === undefined)
|
|
135
|
+
return Object.keys(this.$dirty).length > 0;
|
|
136
|
+
return !this.#columnEqualsOriginal(field);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Revert all dirty columns back to their `$original` values.
|
|
140
|
+
*
|
|
141
|
+
* Because dirty tracking is reference-based, `rollback` only restores
|
|
142
|
+
* reassigned columns to their persisted reference. In-place mutations on
|
|
143
|
+
* object/array columns are NOT recoverable — the snapshot holds the same
|
|
144
|
+
* reference the user mutated. If you rely on rollback, use immutable
|
|
145
|
+
* update patterns (`entity.field = { ...entity.field, x: y }`).
|
|
146
|
+
*/
|
|
147
|
+
rollback() {
|
|
148
|
+
for (const key of Object.keys(this.$dirty)) {
|
|
149
|
+
this[key] = this.$original[key];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Repository back-pointer set by `BaseRepository.#hydrate` so instances can
|
|
154
|
+
* self-refresh / lazy-load. Not serialized (symbol key).
|
|
155
|
+
*
|
|
156
|
+
* @implements Story 32.6
|
|
157
|
+
*/
|
|
158
|
+
[REPO_REF];
|
|
159
|
+
/**
|
|
160
|
+
* Re-read this entity's row from the database and mutate THIS instance
|
|
161
|
+
* with the latest values. Throws if the row no longer exists.
|
|
162
|
+
*/
|
|
163
|
+
async refresh() {
|
|
164
|
+
const repo = this[REPO_REF];
|
|
165
|
+
if (!repo)
|
|
166
|
+
throw new Error("refresh() requires the entity to be hydrated by a BaseRepository");
|
|
167
|
+
await repo.refresh(this);
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Re-read this entity's row from the database and return a NEW instance.
|
|
172
|
+
* `this` is NOT mutated. The returned object has the same runtime class
|
|
173
|
+
* because the repository that produced it is the same one we back-reference.
|
|
174
|
+
*/
|
|
175
|
+
async fresh() {
|
|
176
|
+
const repo = this[REPO_REF];
|
|
177
|
+
if (!repo)
|
|
178
|
+
throw new Error("fresh() requires the entity to be hydrated by a BaseRepository");
|
|
179
|
+
return repo.fresh(this);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Lazy-load a relation count into `this.$extras[alias ?? `${relationName}_count`]`.
|
|
183
|
+
* Issues a single `SELECT COUNT(*) FROM related WHERE <fk> = ?` for this entity.
|
|
184
|
+
*
|
|
185
|
+
* @implements Story 29.2
|
|
186
|
+
*/
|
|
187
|
+
async loadCount(relationName, alias) {
|
|
188
|
+
const repo = this[REPO_REF];
|
|
189
|
+
if (!repo)
|
|
190
|
+
throw new Error("loadCount() requires the entity to be hydrated by a BaseRepository");
|
|
191
|
+
await repo.loadCount(this, relationName, alias);
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Lazy-load a relation aggregate. The builder callback must set the aggregate
|
|
196
|
+
* via `.sum('col')` / `.avg(...)` / `.min(...)` / `.max(...)` / `.count()` and
|
|
197
|
+
* an alias via `.as('name')`. The result lands on `this.$extras[alias]`.
|
|
198
|
+
*
|
|
199
|
+
* await user.loadAggregate('posts', q => q.sum('views').as('total_views'))
|
|
200
|
+
*
|
|
201
|
+
* @implements Story 29.2
|
|
202
|
+
*/
|
|
203
|
+
async loadAggregate(relationName, build) {
|
|
204
|
+
const repo = this[REPO_REF];
|
|
205
|
+
if (!repo)
|
|
206
|
+
throw new Error("loadAggregate() requires the entity to be hydrated by a BaseRepository");
|
|
207
|
+
await repo.loadAggregate(this, relationName, build);
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Lazy-load a relation onto this entity after it was initially fetched.
|
|
212
|
+
*
|
|
213
|
+
* @implements Story 31.10
|
|
214
|
+
*/
|
|
215
|
+
async load(relationName, callback) {
|
|
216
|
+
const repo = this[REPO_REF];
|
|
217
|
+
if (!repo)
|
|
218
|
+
throw new Error("load() requires the entity to be hydrated by a BaseRepository");
|
|
219
|
+
await repo.loadRelation(this, relationName, callback);
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Return a relation proxy bound to this instance. The proxy exposes
|
|
224
|
+
* `create` / `createMany` / `save` / `saveMany` that auto-set the FK.
|
|
225
|
+
*
|
|
226
|
+
* @implements Story 31.5
|
|
227
|
+
*/
|
|
228
|
+
related(relationName) {
|
|
229
|
+
const repo = this[REPO_REF];
|
|
230
|
+
if (!repo)
|
|
231
|
+
throw new Error("related() requires the entity to be hydrated by a BaseRepository");
|
|
232
|
+
return repo.relatedProxy(this, relationName);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Mass-assign columns from a plain payload. Only columns that are in the
|
|
236
|
+
* class's `static fillable` allowlist (or absent from `static guarded` when
|
|
237
|
+
* no fillable is declared) are assigned. Columns not in the payload are
|
|
238
|
+
* reset to undefined so the entity reflects exactly what was filled.
|
|
239
|
+
*
|
|
240
|
+
* @implements Story 30.7
|
|
241
|
+
*/
|
|
242
|
+
fill(payload) {
|
|
243
|
+
const ctor = this.constructor;
|
|
244
|
+
if (ctor.fillable && ctor.guarded) {
|
|
245
|
+
throw new Error(`${ctor.name}: cannot declare both 'fillable' and 'guarded'`);
|
|
246
|
+
}
|
|
247
|
+
const allowed = (key) => {
|
|
248
|
+
if (ctor.fillable)
|
|
249
|
+
return ctor.fillable.includes(key);
|
|
250
|
+
if (ctor.guarded)
|
|
251
|
+
return !ctor.guarded.includes(key);
|
|
252
|
+
return true;
|
|
253
|
+
};
|
|
254
|
+
// Reset fillable fields that are absent from the payload. To keep dirty
|
|
255
|
+
// tracking honest, we restore the persisted `$original` reference (so the
|
|
256
|
+
// column reads as clean) for hydrated entities, and `delete` the property
|
|
257
|
+
// entirely for freshly-constructed ones (so `Object.keys(this)` doesn't
|
|
258
|
+
// list a phantom undefined column).
|
|
259
|
+
if (ctor.fillable) {
|
|
260
|
+
const hasOriginal = Object.keys(this.$original).length > 0;
|
|
261
|
+
for (const f of ctor.fillable) {
|
|
262
|
+
if (!(f in payload)) {
|
|
263
|
+
if (hasOriginal && f in this.$original) {
|
|
264
|
+
this[f] = this.$original[f];
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
delete this[f];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
273
|
+
if (!allowed(k))
|
|
274
|
+
throw new MassAssignmentError(ctor.name, k);
|
|
275
|
+
this[k] = v;
|
|
276
|
+
}
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Patch the entity with a payload, only touching the provided keys. Same
|
|
281
|
+
* allowlist/blocklist rules as `fill` but preserves fields not present in
|
|
282
|
+
* the payload.
|
|
283
|
+
*/
|
|
284
|
+
merge(payload) {
|
|
285
|
+
const ctor = this.constructor;
|
|
286
|
+
if (ctor.fillable && ctor.guarded) {
|
|
287
|
+
throw new Error(`${ctor.name}: cannot declare both 'fillable' and 'guarded'`);
|
|
288
|
+
}
|
|
289
|
+
const allowed = (key) => {
|
|
290
|
+
if (ctor.fillable)
|
|
291
|
+
return ctor.fillable.includes(key);
|
|
292
|
+
if (ctor.guarded)
|
|
293
|
+
return !ctor.guarded.includes(key);
|
|
294
|
+
return true;
|
|
295
|
+
};
|
|
296
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
297
|
+
if (!allowed(k)) {
|
|
298
|
+
throw new MassAssignmentError(ctor.name, k);
|
|
299
|
+
}
|
|
300
|
+
this[k] = v;
|
|
301
|
+
}
|
|
302
|
+
return this;
|
|
303
|
+
}
|
|
304
|
+
/** Add a domain event to be dispatched after save. */
|
|
305
|
+
addDomainEvent(name, data) {
|
|
306
|
+
this.#domainEvents.push({ name, data });
|
|
307
|
+
}
|
|
308
|
+
/** Get accumulated domain events (non-destructive read). */
|
|
309
|
+
getDomainEvents() {
|
|
310
|
+
return [...this.#domainEvents];
|
|
311
|
+
}
|
|
312
|
+
/** Clear accumulated domain events. */
|
|
313
|
+
clearDomainEvents() {
|
|
314
|
+
this.#domainEvents = [];
|
|
315
|
+
}
|
|
316
|
+
/** Get and clear accumulated domain events atomically. */
|
|
317
|
+
flushDomainEvents() {
|
|
318
|
+
const events = [...this.#domainEvents];
|
|
319
|
+
this.#domainEvents = [];
|
|
320
|
+
return events;
|
|
321
|
+
}
|
|
322
|
+
/** Check if entity has pending domain events. */
|
|
323
|
+
hasDomainEvents() {
|
|
324
|
+
return this.#domainEvents.length > 0;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Serialize to JSON — honors class-level `hidden`/`visible` allowlists,
|
|
328
|
+
* per-column `serializeAs` / `serialize` overrides, and `@computed` getters.
|
|
329
|
+
* `$extras` is merged on top so callers see `withCount` / pivot extras next
|
|
330
|
+
* to regular columns. `#private` fields are excluded automatically by ES.
|
|
331
|
+
*
|
|
332
|
+
* @implements Story 32.4
|
|
333
|
+
*/
|
|
334
|
+
toJSON() {
|
|
335
|
+
const ctor = this.constructor;
|
|
336
|
+
const hidden = new Set(ctor.hidden ?? []);
|
|
337
|
+
const visible = ctor.visible && ctor.visible.length > 0 ? new Set(ctor.visible) : null;
|
|
338
|
+
const serializeConfig = getColumnSerializeConfig(ctor);
|
|
339
|
+
const result = {};
|
|
340
|
+
// Regular columns (respecting hidden/visible + serialize overrides)
|
|
341
|
+
for (const key of Object.keys(this)) {
|
|
342
|
+
if (INTERNAL_KEYS.has(key))
|
|
343
|
+
continue;
|
|
344
|
+
if (visible && !visible.has(key))
|
|
345
|
+
continue;
|
|
346
|
+
if (hidden.has(key))
|
|
347
|
+
continue;
|
|
348
|
+
const cfg = serializeConfig[key];
|
|
349
|
+
if (cfg?.serializeAs === null)
|
|
350
|
+
continue; // explicit hide
|
|
351
|
+
const outKey = cfg?.serializeAs ?? key;
|
|
352
|
+
const rawValue = this[key];
|
|
353
|
+
result[outKey] = cfg?.serialize ? cfg.serialize(rawValue) : rawValue;
|
|
354
|
+
}
|
|
355
|
+
// Computed getters (@computed on the prototype)
|
|
356
|
+
const computed = getComputedProperties(ctor);
|
|
357
|
+
for (const prop of computed) {
|
|
358
|
+
if (visible && !visible.has(prop))
|
|
359
|
+
continue;
|
|
360
|
+
if (hidden.has(prop))
|
|
361
|
+
continue;
|
|
362
|
+
result[prop] = this[prop];
|
|
363
|
+
}
|
|
364
|
+
// $extras merged last — aggregates and pivot values show up alongside columns
|
|
365
|
+
return { ...result, ...this.$extras };
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Pick / limit the fields returned by `toJSON()` for a single call.
|
|
369
|
+
*
|
|
370
|
+
* entity.serialize({ fields: ['id', 'title'] })
|
|
371
|
+
*/
|
|
372
|
+
serialize(options) {
|
|
373
|
+
const full = this.toJSON();
|
|
374
|
+
if (!options?.fields)
|
|
375
|
+
return full;
|
|
376
|
+
const picked = {};
|
|
377
|
+
for (const key of options.fields) {
|
|
378
|
+
if (key in full)
|
|
379
|
+
picked[key] = full[key];
|
|
380
|
+
}
|
|
381
|
+
return picked;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// ─── Computed / serialize metadata accessors ────────────────────
|
|
385
|
+
/** Collect the names of all `@computed` getters declared on the prototype chain. */
|
|
386
|
+
function getComputedProperties(ctor) {
|
|
387
|
+
const names = new Set();
|
|
388
|
+
let current = typeof ctor === "object" || typeof ctor === "function"
|
|
389
|
+
? ctor
|
|
390
|
+
: null;
|
|
391
|
+
while (current && current !== Function.prototype) {
|
|
392
|
+
const list = Reflect.getOwnMetadata?.(COMPUTED_KEY, current);
|
|
393
|
+
if (list)
|
|
394
|
+
for (const n of list)
|
|
395
|
+
names.add(n);
|
|
396
|
+
current = Object.getPrototypeOf(current);
|
|
397
|
+
}
|
|
398
|
+
return [...names];
|
|
399
|
+
}
|
|
400
|
+
/** Collect the serialize config for every column declared on the prototype chain. */
|
|
401
|
+
function getColumnSerializeConfig(ctor) {
|
|
402
|
+
const config = {};
|
|
403
|
+
let current = typeof ctor === "object" || typeof ctor === "function"
|
|
404
|
+
? ctor
|
|
405
|
+
: null;
|
|
406
|
+
while (current && current !== Function.prototype) {
|
|
407
|
+
const map = Reflect.getOwnMetadata?.(COLUMN_SERIALIZE_KEY, current);
|
|
408
|
+
if (map)
|
|
409
|
+
Object.assign(config, map);
|
|
410
|
+
current = Object.getPrototypeOf(current);
|
|
411
|
+
}
|
|
412
|
+
return config;
|
|
413
|
+
}
|
|
414
|
+
// `equalsDeep` was intentionally removed with the move to reference-based
|
|
415
|
+
// dirty tracking (Story 32.2 perf revision). The single comparison path is
|
|
416
|
+
// `Object.is` in `#columnEqualsOriginal`, with `Date` as the only structural
|
|
417
|
+
// exception. If you find yourself wanting a deep-equal here, reach for an
|
|
418
|
+
// immutable update pattern at the call site instead — the framework does not
|
|
419
|
+
// traverse object columns at save time by design.
|
|
420
|
+
//# sourceMappingURL=BaseEntity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseEntity.js","sourceRoot":"","sources":["../src/BaseEntity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseRepository — Data Mapper ORM with typed CRUD, soft deletes, and domain events.
|
|
3
|
+
*
|
|
4
|
+
* @implements FR29, FR31, FR35
|
|
5
|
+
*/
|
|
6
|
+
import type { BaseEntity, DomainEvent, RelationProxy } from "./BaseEntity.js";
|
|
7
|
+
import { ModelQuery } from "./ModelQuery.js";
|
|
8
|
+
import { type AtlasDialect } from "./query/native.js";
|
|
9
|
+
type EntityConstructor<T extends BaseEntity> = new () => T;
|
|
10
|
+
/**
|
|
11
|
+
* String-keyed bag of values — covers the recurring DB-shaped objects:
|
|
12
|
+
* row dictionaries, parameter maps, JSON column blobs. Duplicated locally
|
|
13
|
+
* (mirror of `Dict` in `@c9up/ream`) to keep atlas import-graph agnostic.
|
|
14
|
+
*/
|
|
15
|
+
export type Dict<V = string> = Record<string, V>;
|
|
16
|
+
/** Convenience alias for a DB row (column name → value). */
|
|
17
|
+
export type Row = Dict<unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* Async database connection — matches the `AsyncDatabaseConnection` shape
|
|
20
|
+
* exposed by `AtlasProvider` (Rust-backed napi adapter). All BaseRepository
|
|
21
|
+
* I/O is async (`execute` for writes, `query` for reads). The legacy sync
|
|
22
|
+
* `prepare()` API was removed in favour of this surface to align with the
|
|
23
|
+
* actual binding produced by the provider.
|
|
24
|
+
*
|
|
25
|
+
* Drivers backed by `AsyncDatabaseConnection` (`createNapiConnection`)
|
|
26
|
+
* satisfy this interface out-of-the-box.
|
|
27
|
+
*/
|
|
28
|
+
export interface DatabaseConnection {
|
|
29
|
+
/** Run a write statement; returns rowsAffected. */
|
|
30
|
+
execute(sql: string, params?: unknown[]): Promise<{
|
|
31
|
+
rowsAffected: number;
|
|
32
|
+
}>;
|
|
33
|
+
/** Run a SELECT and return all rows. */
|
|
34
|
+
query<T = Row>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
35
|
+
}
|
|
36
|
+
export declare class BaseRepository<T extends BaseEntity> {
|
|
37
|
+
#private;
|
|
38
|
+
/** Callback to dispatch domain events (set by framework integration). */
|
|
39
|
+
onDomainEvents?: (events: DomainEvent[]) => Promise<void>;
|
|
40
|
+
constructor(entityClass: EntityConstructor<T>, db: DatabaseConnection, options?: {
|
|
41
|
+
dialect?: AtlasDialect;
|
|
42
|
+
});
|
|
43
|
+
query(): ModelQuery<T>;
|
|
44
|
+
useTransaction(trx: DatabaseConnection): BaseRepository<T>;
|
|
45
|
+
find(id: string | number | bigint): Promise<T | null>;
|
|
46
|
+
findOrFail(id: string | number): Promise<T>;
|
|
47
|
+
findBy(column: string, value: unknown): Promise<T | null>;
|
|
48
|
+
all(): Promise<T[]>;
|
|
49
|
+
allWithTrashed(): Promise<T[]>;
|
|
50
|
+
onlyTrashed(): Promise<T[]>;
|
|
51
|
+
where(column: string, value: unknown): Promise<T[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Build an entity from a plain object and persist it. Fires `beforeSave` →
|
|
54
|
+
* `beforeCreate` → INSERT → `afterCreate` → `afterSave`.
|
|
55
|
+
*/
|
|
56
|
+
create(data: Partial<Record<string, unknown>>): Promise<T>;
|
|
57
|
+
/**
|
|
58
|
+
* Persist an entity. Insert if PK is missing or row doesn't exist, update
|
|
59
|
+
* otherwise. Fires `beforeSave` → (`beforeCreate` | `beforeUpdate`) → DB →
|
|
60
|
+
* (`afterCreate` | `afterUpdate`) → `afterSave`, then dispatches
|
|
61
|
+
* accumulated domain events through `onDomainEvents`.
|
|
62
|
+
*
|
|
63
|
+
* Race-safety: the `find(pk)` → branch decision has a TOCTOU window. If a
|
|
64
|
+
* concurrent save inserts the same PK between our `find` and our `#insert`,
|
|
65
|
+
* the INSERT hits a unique-key violation; we catch it and fall back to the
|
|
66
|
+
* UPDATE path. The race-loser still fires `beforeCreate` before the
|
|
67
|
+
* recovery (its hook ran once before the conflict surfaced) — design
|
|
68
|
+
* `beforeCreate` hooks to be idempotent or move side-effects into
|
|
69
|
+
* `afterCreate` / `afterSave` where they only fire on commit.
|
|
70
|
+
*/
|
|
71
|
+
save(entity: T): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Insert many rows in a single multi-row INSERT. Fires beforeSave/beforeCreate
|
|
74
|
+
* on each hydrated entity, then hydrates from the RETURNING clause (postgres +
|
|
75
|
+
* sqlite) before firing afterCreate/afterSave. On mysql, falls back to N single
|
|
76
|
+
* INSERTs (documented limitation).
|
|
77
|
+
*
|
|
78
|
+
* @implements Story 30.1 + 30.5
|
|
79
|
+
*/
|
|
80
|
+
createMany(rows: Array<Partial<Record<string, unknown>>>): Promise<T[]>;
|
|
81
|
+
/**
|
|
82
|
+
* Persist many already-constructed entity instances. Same hooks + batching
|
|
83
|
+
* as `createMany`, but accepts prebuilt entities so dirty tracking works.
|
|
84
|
+
*
|
|
85
|
+
* @implements Story 30.5
|
|
86
|
+
*/
|
|
87
|
+
saveMany(entities: T[]): Promise<T[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Dialect-aware upsert. postgres + sqlite emit `ON CONFLICT DO UPDATE`; mysql
|
|
90
|
+
* emits `ON DUPLICATE KEY UPDATE`. Empty `updateColumns` = DO NOTHING.
|
|
91
|
+
*
|
|
92
|
+
* @implements Story 30.4
|
|
93
|
+
*/
|
|
94
|
+
upsert(data: Record<string, unknown> | Array<Record<string, unknown>>, conflictColumns: string[], updateColumns?: string[]): Promise<number>;
|
|
95
|
+
/**
|
|
96
|
+
* Find a row matching `search` or create one merged with `defaults`.
|
|
97
|
+
*
|
|
98
|
+
* @implements Story 30.6
|
|
99
|
+
*/
|
|
100
|
+
firstOrCreate(search: Record<string, unknown>, defaults?: Record<string, unknown>): Promise<T>;
|
|
101
|
+
/** Find a row or build an in-memory instance without persisting. */
|
|
102
|
+
firstOrNew(search: Record<string, unknown>, defaults?: Record<string, unknown>): Promise<T>;
|
|
103
|
+
/** Atomic find-or-update-or-insert. */
|
|
104
|
+
updateOrCreate(search: Record<string, unknown>, values: Record<string, unknown>): Promise<T>;
|
|
105
|
+
/** Delete the entity. Fires `beforeDelete` → DB → `afterDelete`. Soft-delete aware. */
|
|
106
|
+
delete(entity: T): Promise<void>;
|
|
107
|
+
/** Permanently delete (bypasses soft delete). Fires `beforeDelete` / `afterDelete` hooks. */
|
|
108
|
+
forceDelete(entity: T): Promise<void>;
|
|
109
|
+
restore(entity: T): Promise<void>;
|
|
110
|
+
updateById(id: string | number, data: Partial<Record<string, unknown>>): Promise<void>;
|
|
111
|
+
updateWhere(column: string, columnValue: unknown, data: Partial<Record<string, unknown>>): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Atomically increment one or more columns on a single row.
|
|
114
|
+
* Emits `UPDATE … SET col = col + ? WHERE pk = ?` — no read-modify-write,
|
|
115
|
+
* safe under concurrent updates.
|
|
116
|
+
*
|
|
117
|
+
* await repo.increment(userId, 'views', 1)
|
|
118
|
+
* await repo.increment(userId, { balance: 10, credits: 5 })
|
|
119
|
+
*
|
|
120
|
+
* @implements Story 30.3
|
|
121
|
+
*/
|
|
122
|
+
increment(id: string | number, column: string, amount?: number): Promise<void>;
|
|
123
|
+
increment(id: string | number, columns: Record<string, number>): Promise<void>;
|
|
124
|
+
/** Symmetrical to `increment` — emits `SET col = col - ?`. */
|
|
125
|
+
decrement(id: string | number, column: string, amount?: number): Promise<void>;
|
|
126
|
+
decrement(id: string | number, columns: Record<string, number>): Promise<void>;
|
|
127
|
+
raw(sql: string, ...params: unknown[]): Promise<T[]>;
|
|
128
|
+
getTableName(): string;
|
|
129
|
+
getPrimaryKeyColumn(): string;
|
|
130
|
+
/**
|
|
131
|
+
* Re-read the entity's row from the database and mutate the instance in place.
|
|
132
|
+
* Used by `entity.refresh()` — not normally called directly.
|
|
133
|
+
*
|
|
134
|
+
* @implements Story 32.6
|
|
135
|
+
*/
|
|
136
|
+
refresh(entity: BaseEntity): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Re-read the entity's row and return a NEW instance (the input is untouched).
|
|
139
|
+
*
|
|
140
|
+
* @implements Story 32.6
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* Lazy-load a relation count into `entity.$extras[alias ?? `${relationName}_count`]`.
|
|
144
|
+
* Uses `ModelQuery.withCount` with a restrictive `WHERE pk = ?` so it reads
|
|
145
|
+
* one entity's row back with the aggregate column attached.
|
|
146
|
+
*
|
|
147
|
+
* @implements Story 29.2
|
|
148
|
+
*/
|
|
149
|
+
loadCount(entity: BaseEntity, relationName: string, alias?: string): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Lazy-load a relation aggregate. The builder callback sets the aggregate via
|
|
152
|
+
* `.sum/.avg/.min/.max/.count` and the alias via `.as('name')`.
|
|
153
|
+
*
|
|
154
|
+
* @implements Story 29.2
|
|
155
|
+
*/
|
|
156
|
+
loadAggregate(entity: BaseEntity, relationName: string, build: (q: unknown) => void): Promise<void>;
|
|
157
|
+
/**
|
|
158
|
+
* Lazy-load a relation onto an already-fetched entity. Re-uses the preload
|
|
159
|
+
* resolver by running a fresh query with `.where(pk = entity.pk).preload(...)`.
|
|
160
|
+
*
|
|
161
|
+
* @implements Story 31.10
|
|
162
|
+
*/
|
|
163
|
+
loadRelation(entity: BaseEntity, relationName: string, callback?: (q: unknown) => void): Promise<void>;
|
|
164
|
+
/**
|
|
165
|
+
* Return a thin relation proxy bound to the given parent instance. Only
|
|
166
|
+
* `hasOne` / `hasMany` (and trivially `manyToMany` insert paths) are wired
|
|
167
|
+
* here; richer operations (attach/detach/sync) live in Story 31.7's proxy.
|
|
168
|
+
*
|
|
169
|
+
* @implements Story 31.5
|
|
170
|
+
*/
|
|
171
|
+
relatedProxy(entity: BaseEntity, relationName: string): RelationProxy;
|
|
172
|
+
fresh(entity: T): Promise<T>;
|
|
173
|
+
}
|
|
174
|
+
export {};
|
|
175
|
+
//# sourceMappingURL=BaseRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseRepository.d.ts","sourceRoot":"","sources":["../src/BaseRepository.ts"],"names":[],"mappings":"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"}
|