@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.
Files changed (143) hide show
  1. package/db.darwin-arm64.node +0 -0
  2. package/db.darwin-x64.node +0 -0
  3. package/db.linux-arm64-gnu.node +0 -0
  4. package/db.linux-x64-gnu.node +0 -0
  5. package/db.win32-x64-msvc.node +0 -0
  6. package/dist/AtlasProvider.d.ts +106 -0
  7. package/dist/AtlasProvider.d.ts.map +1 -0
  8. package/dist/AtlasProvider.js +190 -0
  9. package/dist/AtlasProvider.js.map +1 -0
  10. package/dist/BaseEntity.d.ts +255 -0
  11. package/dist/BaseEntity.d.ts.map +1 -0
  12. package/dist/BaseEntity.js +420 -0
  13. package/dist/BaseEntity.js.map +1 -0
  14. package/dist/BaseRepository.d.ts +175 -0
  15. package/dist/BaseRepository.d.ts.map +1 -0
  16. package/dist/BaseRepository.js +1394 -0
  17. package/dist/BaseRepository.js.map +1 -0
  18. package/dist/ModelQuery.d.ts +338 -0
  19. package/dist/ModelQuery.d.ts.map +1 -0
  20. package/dist/ModelQuery.js +1598 -0
  21. package/dist/ModelQuery.js.map +1 -0
  22. package/dist/Transaction.d.ts +15 -0
  23. package/dist/Transaction.d.ts.map +1 -0
  24. package/dist/Transaction.js +67 -0
  25. package/dist/Transaction.js.map +1 -0
  26. package/dist/adapters/NapiDbAdapter.d.ts +34 -0
  27. package/dist/adapters/NapiDbAdapter.d.ts.map +1 -0
  28. package/dist/adapters/NapiDbAdapter.js +96 -0
  29. package/dist/adapters/NapiDbAdapter.js.map +1 -0
  30. package/dist/config.d.ts +4 -0
  31. package/dist/config.d.ts.map +1 -0
  32. package/dist/config.js +4 -0
  33. package/dist/config.js.map +1 -0
  34. package/dist/configure.d.ts +10 -0
  35. package/dist/configure.d.ts.map +1 -0
  36. package/dist/configure.js +25 -0
  37. package/dist/configure.js.map +1 -0
  38. package/dist/decorators/entity.d.ts +235 -0
  39. package/dist/decorators/entity.d.ts.map +1 -0
  40. package/dist/decorators/entity.js +264 -0
  41. package/dist/decorators/entity.js.map +1 -0
  42. package/dist/decorators/hooks.d.ts +70 -0
  43. package/dist/decorators/hooks.d.ts.map +1 -0
  44. package/dist/decorators/hooks.js +107 -0
  45. package/dist/decorators/hooks.js.map +1 -0
  46. package/dist/decorators/scope.d.ts +35 -0
  47. package/dist/decorators/scope.d.ts.map +1 -0
  48. package/dist/decorators/scope.js +29 -0
  49. package/dist/decorators/scope.js.map +1 -0
  50. package/dist/errors.d.ts +54 -0
  51. package/dist/errors.d.ts.map +1 -0
  52. package/dist/errors.js +82 -0
  53. package/dist/errors.js.map +1 -0
  54. package/dist/index.d.ts +39 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +28 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/naming/NamingStrategy.d.ts +52 -0
  59. package/dist/naming/NamingStrategy.d.ts.map +1 -0
  60. package/dist/naming/NamingStrategy.js +65 -0
  61. package/dist/naming/NamingStrategy.js.map +1 -0
  62. package/dist/query/QueryBuilder.d.ts +95 -0
  63. package/dist/query/QueryBuilder.d.ts.map +1 -0
  64. package/dist/query/QueryBuilder.js +310 -0
  65. package/dist/query/QueryBuilder.js.map +1 -0
  66. package/dist/query/native.d.ts +18 -0
  67. package/dist/query/native.d.ts.map +1 -0
  68. package/dist/query/native.js +49 -0
  69. package/dist/query/native.js.map +1 -0
  70. package/dist/schema/Migration.d.ts +55 -0
  71. package/dist/schema/Migration.d.ts.map +1 -0
  72. package/dist/schema/Migration.js +68 -0
  73. package/dist/schema/Migration.js.map +1 -0
  74. package/dist/schema/MigrationRunner.d.ts +115 -0
  75. package/dist/schema/MigrationRunner.d.ts.map +1 -0
  76. package/dist/schema/MigrationRunner.js +402 -0
  77. package/dist/schema/MigrationRunner.js.map +1 -0
  78. package/dist/schema/Schema.d.ts +22 -0
  79. package/dist/schema/Schema.d.ts.map +1 -0
  80. package/dist/schema/Schema.js +51 -0
  81. package/dist/schema/Schema.js.map +1 -0
  82. package/dist/schema/SchemaBuilder.d.ts +14 -0
  83. package/dist/schema/SchemaBuilder.d.ts.map +1 -0
  84. package/dist/schema/SchemaBuilder.js +13 -0
  85. package/dist/schema/SchemaBuilder.js.map +1 -0
  86. package/dist/schema/Seeder.d.ts +54 -0
  87. package/dist/schema/Seeder.d.ts.map +1 -0
  88. package/dist/schema/Seeder.js +89 -0
  89. package/dist/schema/Seeder.js.map +1 -0
  90. package/dist/schema/TableBuilder.d.ts +86 -0
  91. package/dist/schema/TableBuilder.d.ts.map +1 -0
  92. package/dist/schema/TableBuilder.js +211 -0
  93. package/dist/schema/TableBuilder.js.map +1 -0
  94. package/dist/schema/types.d.ts +28 -0
  95. package/dist/schema/types.d.ts.map +1 -0
  96. package/dist/schema/types.js +19 -0
  97. package/dist/schema/types.js.map +1 -0
  98. package/dist/services/db.d.ts +22 -0
  99. package/dist/services/db.d.ts.map +1 -0
  100. package/dist/services/db.js +37 -0
  101. package/dist/services/db.js.map +1 -0
  102. package/dist/testing/DatabaseCleanup.d.ts +19 -0
  103. package/dist/testing/DatabaseCleanup.d.ts.map +1 -0
  104. package/dist/testing/DatabaseCleanup.js +40 -0
  105. package/dist/testing/DatabaseCleanup.js.map +1 -0
  106. package/dist/testing/Factory.d.ts +59 -0
  107. package/dist/testing/Factory.d.ts.map +1 -0
  108. package/dist/testing/Factory.js +105 -0
  109. package/dist/testing/Factory.js.map +1 -0
  110. package/dist/testing/TestDatabase.d.ts +25 -0
  111. package/dist/testing/TestDatabase.d.ts.map +1 -0
  112. package/dist/testing/TestDatabase.js +52 -0
  113. package/dist/testing/TestDatabase.js.map +1 -0
  114. package/dist/testing/index.d.ts +4 -0
  115. package/dist/testing/index.d.ts.map +1 -0
  116. package/dist/testing/index.js +4 -0
  117. package/dist/testing/index.js.map +1 -0
  118. package/dist/utils/casing.d.ts +5 -0
  119. package/dist/utils/casing.d.ts.map +1 -0
  120. package/dist/utils/casing.js +9 -0
  121. package/dist/utils/casing.js.map +1 -0
  122. package/dist/utils/dialectFromUrl.d.ts +10 -0
  123. package/dist/utils/dialectFromUrl.d.ts.map +1 -0
  124. package/dist/utils/dialectFromUrl.js +8 -0
  125. package/dist/utils/dialectFromUrl.js.map +1 -0
  126. package/dist/utils/identifier.d.ts +19 -0
  127. package/dist/utils/identifier.d.ts.map +1 -0
  128. package/dist/utils/identifier.js +27 -0
  129. package/dist/utils/identifier.js.map +1 -0
  130. package/dist/utils/safePath.d.ts +22 -0
  131. package/dist/utils/safePath.d.ts.map +1 -0
  132. package/dist/utils/safePath.js +46 -0
  133. package/dist/utils/safePath.js.map +1 -0
  134. package/dist/utils/transactionBrand.d.ts +5 -0
  135. package/dist/utils/transactionBrand.d.ts.map +1 -0
  136. package/dist/utils/transactionBrand.js +5 -0
  137. package/dist/utils/transactionBrand.js.map +1 -0
  138. package/index.darwin-arm64.node +0 -0
  139. package/index.darwin-x64.node +0 -0
  140. package/index.linux-arm64-gnu.node +0 -0
  141. package/index.linux-x64-gnu.node +0 -0
  142. package/index.win32-x64-msvc.node +0 -0
  143. 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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"}