@c9up/atlas 0.1.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +55 -14
  2. package/db.darwin-arm64.node +0 -0
  3. package/db.darwin-x64.node +0 -0
  4. package/db.linux-arm64-gnu.node +0 -0
  5. package/db.linux-x64-gnu.node +0 -0
  6. package/db.win32-x64-msvc.node +0 -0
  7. package/dist/AtlasProvider.d.ts +6 -0
  8. package/dist/AtlasProvider.d.ts.map +1 -1
  9. package/dist/AtlasProvider.js +2 -2
  10. package/dist/AtlasProvider.js.map +1 -1
  11. package/dist/BaseEntity.d.ts +171 -7
  12. package/dist/BaseEntity.d.ts.map +1 -1
  13. package/dist/BaseEntity.js +339 -31
  14. package/dist/BaseEntity.js.map +1 -1
  15. package/dist/BaseModel.d.ts +91 -0
  16. package/dist/BaseModel.d.ts.map +1 -0
  17. package/dist/BaseModel.js +193 -0
  18. package/dist/BaseModel.js.map +1 -0
  19. package/dist/BaseRepository.d.ts +77 -15
  20. package/dist/BaseRepository.d.ts.map +1 -1
  21. package/dist/BaseRepository.js +1423 -354
  22. package/dist/BaseRepository.js.map +1 -1
  23. package/dist/ModelQuery.d.ts +429 -11
  24. package/dist/ModelQuery.d.ts.map +1 -1
  25. package/dist/ModelQuery.js +1733 -145
  26. package/dist/ModelQuery.js.map +1 -1
  27. package/dist/Transaction.d.ts +17 -0
  28. package/dist/Transaction.d.ts.map +1 -1
  29. package/dist/Transaction.js +57 -5
  30. package/dist/Transaction.js.map +1 -1
  31. package/dist/adapters/NapiDbAdapter.d.ts +33 -4
  32. package/dist/adapters/NapiDbAdapter.d.ts.map +1 -1
  33. package/dist/adapters/NapiDbAdapter.js +101 -11
  34. package/dist/adapters/NapiDbAdapter.js.map +1 -1
  35. package/dist/console/migrationCommands.d.ts +48 -0
  36. package/dist/console/migrationCommands.d.ts.map +1 -0
  37. package/dist/console/migrationCommands.js +220 -0
  38. package/dist/console/migrationCommands.js.map +1 -0
  39. package/dist/decorators/entity.d.ts +37 -6
  40. package/dist/decorators/entity.d.ts.map +1 -1
  41. package/dist/decorators/entity.js +32 -2
  42. package/dist/decorators/entity.js.map +1 -1
  43. package/dist/events.d.ts +64 -0
  44. package/dist/events.d.ts.map +1 -0
  45. package/dist/events.js +82 -0
  46. package/dist/events.js.map +1 -0
  47. package/dist/index.d.ts +5 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +3 -0
  50. package/dist/index.js.map +1 -1
  51. package/dist/metadata-keys.d.ts +3 -2
  52. package/dist/metadata-keys.d.ts.map +1 -1
  53. package/dist/naming/NamingStrategy.d.ts +7 -0
  54. package/dist/naming/NamingStrategy.d.ts.map +1 -1
  55. package/dist/naming/NamingStrategy.js +16 -0
  56. package/dist/naming/NamingStrategy.js.map +1 -1
  57. package/dist/schema/Migration.d.ts +26 -3
  58. package/dist/schema/Migration.d.ts.map +1 -1
  59. package/dist/schema/Migration.js +33 -24
  60. package/dist/schema/Migration.js.map +1 -1
  61. package/dist/schema/MigrationRunner.d.ts +43 -32
  62. package/dist/schema/MigrationRunner.d.ts.map +1 -1
  63. package/dist/schema/MigrationRunner.js +211 -26
  64. package/dist/schema/MigrationRunner.js.map +1 -1
  65. package/dist/schema/Schema.d.ts +57 -0
  66. package/dist/schema/Schema.d.ts.map +1 -1
  67. package/dist/schema/Schema.js +138 -3
  68. package/dist/schema/Schema.js.map +1 -1
  69. package/dist/schema/SchemaCheck.d.ts.map +1 -1
  70. package/dist/schema/SchemaCheck.js +3 -1
  71. package/dist/schema/SchemaCheck.js.map +1 -1
  72. package/dist/schema/TableBuilder.d.ts +247 -8
  73. package/dist/schema/TableBuilder.d.ts.map +1 -1
  74. package/dist/schema/TableBuilder.js +607 -41
  75. package/dist/schema/TableBuilder.js.map +1 -1
  76. package/dist/schema/catalog.d.ts +47 -0
  77. package/dist/schema/catalog.d.ts.map +1 -0
  78. package/dist/schema/catalog.js +111 -0
  79. package/dist/schema/catalog.js.map +1 -0
  80. package/dist/schema/introspect.js.map +1 -1
  81. package/dist/schema/types.d.ts +150 -1
  82. package/dist/schema/types.d.ts.map +1 -1
  83. package/dist/schema/types.js +11 -0
  84. package/dist/schema/types.js.map +1 -1
  85. package/dist/services/db.d.ts +6 -0
  86. package/dist/services/db.d.ts.map +1 -1
  87. package/dist/services/db.js +17 -0
  88. package/dist/services/db.js.map +1 -1
  89. package/dist/testing/DatabaseCleanup.d.ts +7 -4
  90. package/dist/testing/DatabaseCleanup.d.ts.map +1 -1
  91. package/dist/testing/DatabaseCleanup.js +21 -18
  92. package/dist/testing/DatabaseCleanup.js.map +1 -1
  93. package/dist/testing/Factory.d.ts +70 -5
  94. package/dist/testing/Factory.d.ts.map +1 -1
  95. package/dist/testing/Factory.js +209 -10
  96. package/dist/testing/Factory.js.map +1 -1
  97. package/index.darwin-arm64.node +0 -0
  98. package/index.darwin-x64.node +0 -0
  99. package/index.linux-arm64-gnu.node +0 -0
  100. package/index.linux-x64-gnu.node +0 -0
  101. package/index.win32-x64-msvc.node +0 -0
  102. package/package.json +4 -1
  103. package/scripts/guard-publish.mjs +15 -0
  104. package/src/AtlasProvider.ts +8 -1
  105. package/src/BaseEntity.ts +449 -40
  106. package/src/BaseModel.ts +324 -0
  107. package/src/BaseRepository.ts +1659 -371
  108. package/src/ModelQuery.ts +2290 -203
  109. package/src/Transaction.ts +68 -5
  110. package/src/adapters/NapiDbAdapter.ts +159 -10
  111. package/src/console/migrationCommands.ts +258 -0
  112. package/src/decorators/entity.ts +53 -6
  113. package/src/events.ts +112 -0
  114. package/src/index.ts +19 -0
  115. package/src/metadata-keys.ts +3 -2
  116. package/src/naming/NamingStrategy.ts +23 -0
  117. package/src/schema/Migration.ts +42 -3
  118. package/src/schema/MigrationRunner.ts +270 -27
  119. package/src/schema/Schema.ts +210 -3
  120. package/src/schema/SchemaCheck.ts +7 -2
  121. package/src/schema/TableBuilder.ts +735 -41
  122. package/src/schema/catalog.ts +166 -0
  123. package/src/schema/introspect.ts +3 -4
  124. package/src/schema/types.ts +137 -2
  125. package/src/services/db.ts +28 -0
  126. package/src/testing/DatabaseCleanup.ts +23 -22
  127. package/src/testing/Factory.ts +332 -15
@@ -13,14 +13,33 @@
13
13
  *
14
14
  * @implements MISS-18, Story 32.13
15
15
  */
16
+ import { type Faker } from "@faker-js/faker";
16
17
  import type { BaseEntity } from "../BaseEntity.js";
17
18
  import { type DatabaseConnection } from "../BaseRepository.js";
18
19
  type EntityConstructor<T extends BaseEntity> = new () => T;
20
+ /** Resolves the factory that builds a related model (Lucid `.relation`). */
21
+ type RelationResolver = () => FactoryBuilder<BaseEntity>;
22
+ /**
23
+ * Context handed to the `define` callback (Adonis Lucid parity). Currently just
24
+ * a Faker instance for generating fake data. A callback that takes no argument
25
+ * keeps working — it simply ignores the context.
26
+ */
27
+ export interface FactoryContext {
28
+ faker: Faker;
29
+ }
30
+ /** Attributes callback — receives {@link FactoryContext}, returns the row shape. */
31
+ type DefaultsFn = (ctx: FactoryContext) => Record<string, unknown>;
19
32
  /** A named state — mutates an in-progress data object in place. */
20
33
  type StateFn<D> = (data: D) => void;
21
34
  export interface FactoryBuilder<T extends BaseEntity> {
22
35
  /** Override specific fields for the next call (reset after consumption). */
23
36
  merge(overrides: Partial<Record<string, unknown>>): FactoryBuilder<T>;
37
+ /**
38
+ * Like {@link merge} but deep — nested plain objects are merged key by key
39
+ * instead of replaced wholesale (Lucid `mergeRecursive`). Arrays and
40
+ * non-plain values still replace. Reset after consumption like `merge`.
41
+ */
42
+ mergeRecursive(overrides: Partial<Record<string, unknown>>): FactoryBuilder<T>;
24
43
  /** Declare a named variation of this factory, stored on the factory's state map. */
25
44
  state(name: string, fn: StateFn<Record<string, unknown>>): FactoryBuilder<T>;
26
45
  /**
@@ -31,19 +50,65 @@ export interface FactoryBuilder<T extends BaseEntity> {
31
50
  * (audit 2026-06-13). `merge()` behaves the same way.
32
51
  */
33
52
  apply(...stateNames: string[]): FactoryBuilder<T>;
34
- /** Create and persist a single entity (fires lifecycle hooks via `repo.create`). */
35
- create(db: DatabaseConnection): Promise<T>;
53
+ /**
54
+ * Bind a connection (e.g. a transaction) used by subsequent
55
+ * `create`/`createMany` calls that pass no explicit `db` (Adonis Lucid
56
+ * `.client`). The binding persists until changed. Ideal for test isolation:
57
+ * `factory.client(trx).create()`.
58
+ */
59
+ client(connection: DatabaseConnection): FactoryBuilder<T>;
60
+ /**
61
+ * Like {@link client} but resolves a connection registered under `name` via
62
+ * atlas's connection registry (Adonis Lucid `.connection`). Throws if no
63
+ * connection is registered under that name.
64
+ */
65
+ connection(name: string): FactoryBuilder<T>;
66
+ /**
67
+ * Create and persist a single entity (fires lifecycle hooks via
68
+ * `repo.create`). `db` is optional when a connection was bound with
69
+ * {@link client} / {@link connection}.
70
+ */
71
+ create(db?: DatabaseConnection): Promise<T>;
36
72
  /** Create and persist multiple entities (fires hooks per row). */
37
- createMany(count: number, db: DatabaseConnection): Promise<T[]>;
73
+ createMany(count: number, db?: DatabaseConnection): Promise<T[]>;
38
74
  /** Build the data object without persisting and without instantiating an entity. */
39
75
  make(): Record<string, unknown>;
40
76
  /** Build multiple data objects without persisting. */
41
77
  makeMany(count: number): Record<string, unknown>[];
42
78
  /**
43
79
  * Build an entity INSTANCE without persisting it (Lucid's `makeStubbed`).
44
- * Useful when you need a `new User()` object but want to avoid the DB.
80
+ * The instance is marked persisted and, unless the build already supplied a
81
+ * primary key, given a process-unique stub id — so it looks like a saved row
82
+ * (relations, serialization, `$isPersisted`) without touching the DB.
45
83
  */
46
84
  makeStubbed(): T;
85
+ /** Build many stubbed instances, each with its own stub id ({@link makeStubbed}). */
86
+ makeStubbedMany(count: number): T[];
87
+ /**
88
+ * Declare which factory builds a relation (Adonis Lucid `.relation`), so
89
+ * `.with(name)` can create related rows. `name` must match a relation
90
+ * property declared with `@HasMany`/`@HasOne`/`@BelongsTo`/`@ManyToMany`.
91
+ */
92
+ relation(name: string, resolver: RelationResolver): FactoryBuilder<T>;
93
+ /**
94
+ * Queue related rows to create together with the next `create`/`createMany`
95
+ * (Adonis Lucid `.with`). `count` defaults to 1 (ignored past 1 for hasOne).
96
+ * The callback receives the related factory to customize it (`merge`/`apply`).
97
+ *
98
+ * One level deep: the related rows are persisted through the parent's
99
+ * relation proxy (which wires the FK / pivot), so a nested `.with()` on the
100
+ * callback's factory is not itself applied. `.with()` runs on persistence
101
+ * only — `make`/`makeStubbed` ignore it.
102
+ */
103
+ with(name: string, count?: number, callback?: (factory: FactoryBuilder<BaseEntity>) => void): FactoryBuilder<T>;
104
+ /**
105
+ * Register a callback that runs on the built entity INSTANCE before it is
106
+ * persisted (Adonis Lucid `.tap`). Multiple taps run in order. Applies to the
107
+ * instance-producing paths — `create`/`createMany`/`makeStubbed`/
108
+ * `makeStubbedMany` — and is reset after consumption; `make`/`makeMany`
109
+ * return plain data, so they ignore it.
110
+ */
111
+ tap(fn: (entity: T) => void): FactoryBuilder<T>;
47
112
  }
48
113
  /**
49
114
  * Define a model factory.
@@ -53,6 +118,6 @@ export interface FactoryBuilder<T extends BaseEntity> {
53
118
  * name: 'Test User',
54
119
  * }))
55
120
  */
56
- export declare function factory<T extends BaseEntity>(entityClass: EntityConstructor<T>, defaults: () => Record<string, unknown>): FactoryBuilder<T>;
121
+ export declare function factory<T extends BaseEntity>(entityClass: EntityConstructor<T>, defaults: DefaultsFn): FactoryBuilder<T>;
57
122
  export {};
58
123
  //# sourceMappingURL=Factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Factory.d.ts","sourceRoot":"","sources":["../../src/testing/Factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/E,KAAK,iBAAiB,CAAC,CAAC,SAAS,UAAU,IAAI,UAAU,CAAC,CAAC;AAE3D,mEAAmE;AACnE,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;AAEpC,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU;IACnD,4EAA4E;IAC5E,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAEtE,oFAAoF;IACpF,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAE7E;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,UAAU,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAElD,oFAAoF;IACpF,MAAM,CAAC,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE3C,kEAAkE;IAClE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAEhE,oFAAoF;IACpF,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhC,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAEnD;;;OAGG;IACH,WAAW,IAAI,CAAC,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,UAAU,EAC3C,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC,EACjC,QAAQ,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,cAAc,CAAC,CAAC,CAAC,CA0FnB"}
1
+ {"version":3,"file":"Factory.d.ts","sourceRoot":"","sources":["../../src/testing/Factory.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,43 @@
13
13
  *
14
14
  * @implements MISS-18, Story 32.13
15
15
  */
16
+ import { faker } from "@faker-js/faker";
16
17
  import { BaseRepository } from "../BaseRepository.js";
18
+ import { getPrimaryKey, getRelationMetadata } from "../decorators/entity.js";
19
+ import { getConnection } from "../services/db.js";
20
+ /**
21
+ * Process-wide counter for stubbed primary keys (Lucid's stub id). Every
22
+ * `makeStubbed*` build with no explicit PK gets the next value, so stubbed
23
+ * instances have stable, distinct, DB-free identifiers — enough to look
24
+ * persisted and to key relations/serialization without a round trip.
25
+ */
26
+ let stubIdCounter = 0;
27
+ /** Keys that must never be written through a recursive merge — prototype-pollution guard. */
28
+ const FORBIDDEN_MERGE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
29
+ function isPlainObject(value) {
30
+ if (value === null || typeof value !== "object")
31
+ return false;
32
+ const proto = Object.getPrototypeOf(value);
33
+ return proto === Object.prototype || proto === null;
34
+ }
35
+ /**
36
+ * Deep-merge `source` into `target` (Lucid `mergeRecursive`). Plain objects are
37
+ * merged key by key; arrays and every non-plain value replace wholesale. Returns
38
+ * a fresh object — neither argument is mutated. Skips prototype-pollution keys.
39
+ */
40
+ function deepMerge(target, source) {
41
+ const out = { ...target };
42
+ for (const [key, value] of Object.entries(source)) {
43
+ if (FORBIDDEN_MERGE_KEYS.has(key))
44
+ continue;
45
+ const existing = out[key];
46
+ out[key] =
47
+ isPlainObject(existing) && isPlainObject(value)
48
+ ? deepMerge(existing, value)
49
+ : value;
50
+ }
51
+ return out;
52
+ }
17
53
  /**
18
54
  * Define a model factory.
19
55
  *
@@ -27,12 +63,61 @@ export function factory(entityClass, defaults) {
27
63
  const states = new Map();
28
64
  // Transient state — resets after every `make`/`create`.
29
65
  let pendingOverrides = {};
66
+ // Recursive overrides are kept apart from the shallow `pendingOverrides`
67
+ // because they must deep-merge into `defaults()`, which only exists at build
68
+ // time — a shallow spread here would clobber a whole nested object.
69
+ let pendingRecursive = {};
30
70
  let pendingStates = [];
71
+ // Relations: persistent factory resolvers keyed by relation name, plus the
72
+ // transient `.with()` queue consumed by create()/createMany().
73
+ const relations = new Map();
74
+ let pendingWith = [];
75
+ // Transient `.tap()` callbacks run on the built instance before persistence.
76
+ let pendingTap = [];
77
+ // Persistent connection bound via `.client()`/`.connection()`, used when
78
+ // create()/createMany() are called without an explicit `db`.
79
+ let boundClient;
80
+ /** The connection to persist through: the explicit arg wins over the bound one. */
81
+ const resolveConnection = (db) => {
82
+ const conn = db ?? boundClient;
83
+ if (!conn) {
84
+ throw new Error(`Factory ${entityClass.name}: no connection — pass one to create()/createMany() or bind one with .client()/.connection().`);
85
+ }
86
+ return conn;
87
+ };
88
+ // Primary-key property, resolved once, for stub-id assignment. Same fallback
89
+ // as BaseRepository so a model without an explicit `@PrimaryKey` uses `id`.
90
+ const primaryKey = getPrimaryKey(entityClass) ?? "id";
91
+ /** Hydrate a fresh (not-yet-persisted) instance from a data object. */
92
+ const newInstance = (data) => {
93
+ const entity = new entityClass();
94
+ for (const [key, value] of Object.entries(data)) {
95
+ entity.setProp(key, value);
96
+ }
97
+ return entity;
98
+ };
99
+ /** Build a stubbed instance: hydrate, run taps, give it a stub id if none was
100
+ * supplied, then mark persisted so it mirrors a fetched row. Taps run before
101
+ * `markAsPersisted` so tapped fields land in the clean snapshot. */
102
+ const stub = (data, taps) => {
103
+ const entity = newInstance(data);
104
+ for (const tap of taps)
105
+ tap(entity);
106
+ if (data[primaryKey] === undefined) {
107
+ entity.setProp(primaryKey, ++stubIdCounter);
108
+ }
109
+ entity.markAsPersisted();
110
+ return entity;
111
+ };
31
112
  const buildData = () => {
32
- const data = {
33
- ...defaults(),
113
+ let data = {
114
+ ...defaults({ faker }),
34
115
  ...pendingOverrides,
35
116
  };
117
+ // Deep overrides layer on top of the shallow one so nested defaults survive.
118
+ if (Object.keys(pendingRecursive).length > 0) {
119
+ data = deepMerge(data, pendingRecursive);
120
+ }
36
121
  for (const name of pendingStates) {
37
122
  const fn = states.get(name);
38
123
  if (!fn)
@@ -43,13 +128,77 @@ export function factory(entityClass, defaults) {
43
128
  };
44
129
  const resetPending = () => {
45
130
  pendingOverrides = {};
131
+ pendingRecursive = {};
46
132
  pendingStates = [];
133
+ pendingTap = [];
134
+ };
135
+ /**
136
+ * Persist one row. With no taps this is the plain `repo.create(data)` path,
137
+ * untouched. With taps, the instance is built, tapped, then saved — so a tap
138
+ * can mutate the model before the INSERT (Adonis Lucid `.tap`). `save` on a
139
+ * fresh instance runs the same create hooks as `create`.
140
+ */
141
+ const persist = async (repo, data, taps) => {
142
+ if (taps.length === 0)
143
+ return repo.create(data);
144
+ const entity = newInstance(data);
145
+ for (const tap of taps)
146
+ tap(entity);
147
+ await repo.save(entity);
148
+ return entity;
149
+ };
150
+ /**
151
+ * Persist the queued `.with()` relations for one just-created parent, routing
152
+ * through the parent's relation proxy so the FK / pivot is wired by the
153
+ * already-tested relation-write code (not re-derived here).
154
+ */
155
+ const applyRelations = async (parent, reqs, db) => {
156
+ const meta = getRelationMetadata(entityClass);
157
+ for (const req of reqs) {
158
+ const relMeta = meta.find((r) => r.propertyKey === req.name);
159
+ if (!relMeta) {
160
+ throw new Error(`Factory .with('${req.name}'): '${req.name}' is not a declared relation on ${entityClass.name}`);
161
+ }
162
+ const resolver = relations.get(req.name);
163
+ if (!resolver) {
164
+ throw new Error(`Factory .with('${req.name}'): no related factory — declare it with .relation('${req.name}', () => XFactory)`);
165
+ }
166
+ const childFactory = resolver();
167
+ if (req.callback)
168
+ req.callback(childFactory);
169
+ const proxy = parent.related(req.name);
170
+ if (proxy.type === "hasMany") {
171
+ await proxy.createMany(childFactory.makeMany(req.count));
172
+ }
173
+ else if (proxy.type === "hasOne") {
174
+ await proxy.create(childFactory.make());
175
+ }
176
+ else if (proxy.type === "manyToMany") {
177
+ // The m2m proxy's create() inserts the related row AND the pivot link.
178
+ for (const row of childFactory.makeMany(req.count)) {
179
+ await proxy.create(row);
180
+ }
181
+ }
182
+ else if (proxy.type === "belongsTo") {
183
+ // FK lives on the parent: create the owner, then associate re-saves
184
+ // the parent with the FK set.
185
+ const owner = await childFactory.create(db);
186
+ await proxy.associate(owner);
187
+ }
188
+ else {
189
+ throw new Error(`Factory .with('${req.name}'): '${relMeta.type}' relations are not supported`);
190
+ }
191
+ }
47
192
  };
48
193
  const builder = {
49
194
  merge(overrides) {
50
195
  pendingOverrides = { ...pendingOverrides, ...overrides };
51
196
  return builder;
52
197
  },
198
+ mergeRecursive(overrides) {
199
+ pendingRecursive = deepMerge(pendingRecursive, overrides);
200
+ return builder;
201
+ },
53
202
  state(name, fn) {
54
203
  states.set(name, fn);
55
204
  return builder;
@@ -58,6 +207,30 @@ export function factory(entityClass, defaults) {
58
207
  pendingStates.push(...names);
59
208
  return builder;
60
209
  },
210
+ relation(name, resolver) {
211
+ relations.set(name, resolver);
212
+ return builder;
213
+ },
214
+ with(name, count = 1, callback) {
215
+ pendingWith.push({ name, count, callback });
216
+ return builder;
217
+ },
218
+ tap(fn) {
219
+ pendingTap.push(fn);
220
+ return builder;
221
+ },
222
+ client(connection) {
223
+ boundClient = connection;
224
+ return builder;
225
+ },
226
+ connection(name) {
227
+ const conn = getConnection(name);
228
+ if (!conn) {
229
+ throw new Error(`Factory ${entityClass.name}: no connection registered under '${name}'.`);
230
+ }
231
+ boundClient = conn;
232
+ return builder;
233
+ },
61
234
  make() {
62
235
  const data = buildData();
63
236
  resetPending();
@@ -75,25 +248,51 @@ export function factory(entityClass, defaults) {
75
248
  return rows;
76
249
  },
77
250
  makeStubbed() {
251
+ const taps = pendingTap;
78
252
  const data = buildData();
253
+ const entity = stub(data, taps);
79
254
  resetPending();
80
- const entity = new entityClass();
81
- for (const [key, value] of Object.entries(data)) {
82
- entity.setProp(key, value);
83
- }
84
255
  return entity;
85
256
  },
257
+ makeStubbedMany(count) {
258
+ // Re-evaluate defaults per row (distinct Date.now()/faker values), same
259
+ // as makeMany; each row then gets its own stub id + taps via `stub`.
260
+ const taps = pendingTap;
261
+ const rows = [];
262
+ for (let i = 0; i < count; i++) {
263
+ rows.push(stub(buildData(), taps));
264
+ }
265
+ resetPending();
266
+ return rows;
267
+ },
86
268
  async create(db) {
269
+ const conn = resolveConnection(db);
270
+ // Capture + clear the relation/tap queues before make() (which resets
271
+ // pending state) so they aren't lost and a later create() starts clean.
272
+ const withReqs = pendingWith;
273
+ pendingWith = [];
274
+ const taps = pendingTap;
87
275
  const data = builder.make();
88
- const repo = new BaseRepository(entityClass, db);
89
- return repo.create(data);
276
+ const repo = new BaseRepository(entityClass, conn);
277
+ const entity = await persist(repo, data, taps);
278
+ if (withReqs.length > 0)
279
+ await applyRelations(entity, withReqs, conn);
280
+ return entity;
90
281
  },
91
282
  async createMany(count, db) {
283
+ const conn = resolveConnection(db);
284
+ const withReqs = pendingWith;
285
+ pendingWith = [];
286
+ const taps = pendingTap;
92
287
  const rows = builder.makeMany(count);
93
- const repo = new BaseRepository(entityClass, db);
288
+ const repo = new BaseRepository(entityClass, conn);
94
289
  const created = [];
95
290
  for (const data of rows) {
96
- created.push(await repo.create(data));
291
+ const entity = await persist(repo, data, taps);
292
+ // Each created parent gets its own related rows (Lucid semantics).
293
+ if (withReqs.length > 0)
294
+ await applyRelations(entity, withReqs, conn);
295
+ created.push(entity);
97
296
  }
98
297
  return created;
99
298
  },
@@ -1 +1 @@
1
- {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../../src/testing/Factory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../../src/testing/Factory.ts"],"names":[],"mappings":"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"}
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c9up/atlas",
3
- "version": "0.1.18",
3
+ "version": "0.2.0",
4
4
  "description": "Atlas — Data Mapper ORM for the Ream framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,9 +39,12 @@
39
39
  }
40
40
  },
41
41
  "dependencies": {
42
+ "@c9up/chronos": "^0.1.6",
43
+ "@faker-js/faker": "^9",
42
44
  "reflect-metadata": "^0.2"
43
45
  },
44
46
  "devDependencies": {
47
+ "@biomejs/biome": "^2.4.10",
45
48
  "typescript": "^6.0.2",
46
49
  "vitest": "^4.1.2",
47
50
  "@types/node": "^22"
@@ -0,0 +1,15 @@
1
+ // Refuse a LOCAL `npm/pnpm publish`. Atlas ships prebuilt multi-platform NAPI
2
+ // binaries (index.<suffix>.node / db.<suffix>.node) that only the CI matrix
3
+ // assembles + downloads before publishing. A local publish would package
4
+ // whatever single-platform / stale `.node` files happen to be on disk, so the
5
+ // published tarball would be broken on every other platform. Publishing MUST go
6
+ // through the workflow_dispatch CI job (which sets CI=true).
7
+ if (!process.env.CI) {
8
+ console.error(
9
+ "@c9up/atlas: refusing a local publish.\n" +
10
+ "Publish only via the CI workflow — it builds and bundles the NAPI\n" +
11
+ "binaries for every platform. A local `npm publish` would ship\n" +
12
+ "incomplete/stale `.node` files. Run the publish workflow instead.",
13
+ );
14
+ process.exit(1);
15
+ }
@@ -81,6 +81,12 @@ export interface ConnectionConfig {
81
81
  poolMin?: number;
82
82
  /** Maximum pool connections (default: 10) */
83
83
  poolMax?: number;
84
+ /**
85
+ * Emit a `db:query` event for every statement on this connection (Lucid's
86
+ * `debug` connection option). Off by default — subscribe with `onDbQuery`
87
+ * to receive them; with no subscriber nothing is emitted either way.
88
+ */
89
+ debug?: boolean;
84
90
  /**
85
91
  * Connection-level pragmas (sqlite only). Each entry becomes a
86
92
  * `PRAGMA <key> = <value>;` issued before the first query.
@@ -163,7 +169,7 @@ export default class AtlasProvider {
163
169
  // leaks pools/sockets.
164
170
  const entries = Object.entries(connections);
165
171
  const results = await Promise.allSettled(
166
- entries.map(([, settings]) =>
172
+ entries.map(([name, settings]) =>
167
173
  createNapiConnection(
168
174
  settings.url,
169
175
  settings.poolMin ?? 1,
@@ -174,6 +180,7 @@ export default class AtlasProvider {
174
180
  backoffMs: settings.connectBackoffMs,
175
181
  timeoutMs: settings.connectTimeoutMs,
176
182
  },
183
+ { debug: settings.debug ?? false, connectionName: name },
177
184
  ),
178
185
  ),
179
186
  );