@c9up/atlas 0.1.19 → 0.2.1

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 (250) hide show
  1. package/README.md +63 -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 +66 -7
  8. package/dist/AtlasProvider.d.ts.map +1 -1
  9. package/dist/AtlasProvider.js +205 -39
  10. package/dist/AtlasProvider.js.map +1 -1
  11. package/dist/BaseEntity.d.ts +181 -5
  12. package/dist/BaseEntity.d.ts.map +1 -1
  13. package/dist/BaseEntity.js +339 -33
  14. package/dist/BaseEntity.js.map +1 -1
  15. package/dist/BaseModel.d.ts +127 -0
  16. package/dist/BaseModel.d.ts.map +1 -0
  17. package/dist/BaseModel.js +250 -0
  18. package/dist/BaseModel.js.map +1 -0
  19. package/dist/BaseRepository.d.ts +81 -15
  20. package/dist/BaseRepository.d.ts.map +1 -1
  21. package/dist/BaseRepository.js +1433 -387
  22. package/dist/BaseRepository.js.map +1 -1
  23. package/dist/ConnectionManager.d.ts +106 -0
  24. package/dist/ConnectionManager.d.ts.map +1 -0
  25. package/dist/ConnectionManager.js +228 -0
  26. package/dist/ConnectionManager.js.map +1 -0
  27. package/dist/ModelQuery.d.ts +577 -41
  28. package/dist/ModelQuery.d.ts.map +1 -1
  29. package/dist/ModelQuery.js +2188 -291
  30. package/dist/ModelQuery.js.map +1 -1
  31. package/dist/Transaction.d.ts +71 -1
  32. package/dist/Transaction.d.ts.map +1 -1
  33. package/dist/Transaction.js +138 -24
  34. package/dist/Transaction.js.map +1 -1
  35. package/dist/adapters/NapiDbAdapter.d.ts +44 -4
  36. package/dist/adapters/NapiDbAdapter.d.ts.map +1 -1
  37. package/dist/adapters/NapiDbAdapter.js +131 -13
  38. package/dist/adapters/NapiDbAdapter.js.map +1 -1
  39. package/dist/console/contract.d.ts +62 -0
  40. package/dist/console/contract.d.ts.map +1 -0
  41. package/dist/console/contract.js +38 -0
  42. package/dist/console/contract.js.map +1 -0
  43. package/dist/console/factoryCommands.d.ts +23 -0
  44. package/dist/console/factoryCommands.d.ts.map +1 -0
  45. package/dist/console/factoryCommands.js +62 -0
  46. package/dist/console/factoryCommands.js.map +1 -0
  47. package/dist/console/migrationCommands.d.ts +78 -0
  48. package/dist/console/migrationCommands.d.ts.map +1 -0
  49. package/dist/console/migrationCommands.js +327 -0
  50. package/dist/console/migrationCommands.js.map +1 -0
  51. package/dist/console/schemaCheckCommand.d.ts +11 -17
  52. package/dist/console/schemaCheckCommand.d.ts.map +1 -1
  53. package/dist/console/schemaCheckCommand.js +21 -15
  54. package/dist/console/schemaCheckCommand.js.map +1 -1
  55. package/dist/console/schemaDumpCommand.d.ts +30 -0
  56. package/dist/console/schemaDumpCommand.d.ts.map +1 -0
  57. package/dist/console/schemaDumpCommand.js +69 -0
  58. package/dist/console/schemaDumpCommand.js.map +1 -0
  59. package/dist/console/schemaGenerateCommand.d.ts +100 -0
  60. package/dist/console/schemaGenerateCommand.d.ts.map +1 -0
  61. package/dist/console/schemaGenerateCommand.js +246 -0
  62. package/dist/console/schemaGenerateCommand.js.map +1 -0
  63. package/dist/console/seederCommands.d.ts +46 -0
  64. package/dist/console/seederCommands.d.ts.map +1 -0
  65. package/dist/console/seederCommands.js +136 -0
  66. package/dist/console/seederCommands.js.map +1 -0
  67. package/dist/decorators/entity.d.ts +50 -8
  68. package/dist/decorators/entity.d.ts.map +1 -1
  69. package/dist/decorators/entity.js +42 -2
  70. package/dist/decorators/entity.js.map +1 -1
  71. package/dist/decorators/hooks.d.ts +3 -3
  72. package/dist/decorators/hooks.d.ts.map +1 -1
  73. package/dist/decorators/hooks.js.map +1 -1
  74. package/dist/events.d.ts +69 -0
  75. package/dist/events.d.ts.map +1 -0
  76. package/dist/events.js +82 -0
  77. package/dist/events.js.map +1 -0
  78. package/dist/factories.d.ts +8 -0
  79. package/dist/factories.d.ts.map +1 -0
  80. package/dist/factories.js +8 -0
  81. package/dist/factories.js.map +1 -0
  82. package/dist/index.d.ts +15 -5
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +14 -3
  85. package/dist/index.js.map +1 -1
  86. package/dist/lucid-schema.d.ts +15 -0
  87. package/dist/lucid-schema.d.ts.map +1 -0
  88. package/dist/lucid-schema.js +16 -0
  89. package/dist/lucid-schema.js.map +1 -0
  90. package/dist/metadata-keys.d.ts +3 -2
  91. package/dist/metadata-keys.d.ts.map +1 -1
  92. package/dist/naming/NamingStrategy.d.ts +7 -0
  93. package/dist/naming/NamingStrategy.d.ts.map +1 -1
  94. package/dist/naming/NamingStrategy.js +16 -0
  95. package/dist/naming/NamingStrategy.js.map +1 -1
  96. package/dist/orm.d.ts +14 -0
  97. package/dist/orm.d.ts.map +1 -0
  98. package/dist/orm.js +14 -0
  99. package/dist/orm.js.map +1 -0
  100. package/dist/query/DatabaseQueryBuilder.d.ts +557 -0
  101. package/dist/query/DatabaseQueryBuilder.d.ts.map +1 -0
  102. package/dist/query/DatabaseQueryBuilder.js +1798 -0
  103. package/dist/query/DatabaseQueryBuilder.js.map +1 -0
  104. package/dist/query/DmlBuilder.d.ts +62 -0
  105. package/dist/query/DmlBuilder.d.ts.map +1 -0
  106. package/dist/query/DmlBuilder.js +84 -0
  107. package/dist/query/DmlBuilder.js.map +1 -0
  108. package/dist/query/QueryBuilder.d.ts +5 -0
  109. package/dist/query/QueryBuilder.d.ts.map +1 -1
  110. package/dist/query/QueryBuilder.js +7 -0
  111. package/dist/query/QueryBuilder.js.map +1 -1
  112. package/dist/query/RawQueryBuilder.d.ts +47 -0
  113. package/dist/query/RawQueryBuilder.d.ts.map +1 -0
  114. package/dist/query/RawQueryBuilder.js +138 -0
  115. package/dist/query/RawQueryBuilder.js.map +1 -0
  116. package/dist/query/interpolate.d.ts +45 -0
  117. package/dist/query/interpolate.d.ts.map +1 -0
  118. package/dist/query/interpolate.js +51 -0
  119. package/dist/query/interpolate.js.map +1 -0
  120. package/dist/query/native.d.ts +8 -0
  121. package/dist/query/native.d.ts.map +1 -1
  122. package/dist/query/native.js +14 -0
  123. package/dist/query/native.js.map +1 -1
  124. package/dist/query/operators.d.ts +4 -0
  125. package/dist/query/operators.d.ts.map +1 -0
  126. package/dist/query/operators.js +27 -0
  127. package/dist/query/operators.js.map +1 -0
  128. package/dist/schema/Migration.d.ts +50 -3
  129. package/dist/schema/Migration.d.ts.map +1 -1
  130. package/dist/schema/Migration.js +60 -24
  131. package/dist/schema/Migration.js.map +1 -1
  132. package/dist/schema/MigrationRunner.d.ts +95 -34
  133. package/dist/schema/MigrationRunner.d.ts.map +1 -1
  134. package/dist/schema/MigrationRunner.js +523 -68
  135. package/dist/schema/MigrationRunner.js.map +1 -1
  136. package/dist/schema/Schema.d.ts +114 -0
  137. package/dist/schema/Schema.d.ts.map +1 -1
  138. package/dist/schema/Schema.js +272 -3
  139. package/dist/schema/Schema.js.map +1 -1
  140. package/dist/schema/SchemaDumper.d.ts +121 -0
  141. package/dist/schema/SchemaDumper.d.ts.map +1 -0
  142. package/dist/schema/SchemaDumper.js +365 -0
  143. package/dist/schema/SchemaDumper.js.map +1 -0
  144. package/dist/schema/Seeder.d.ts +13 -0
  145. package/dist/schema/Seeder.d.ts.map +1 -1
  146. package/dist/schema/Seeder.js +28 -5
  147. package/dist/schema/Seeder.js.map +1 -1
  148. package/dist/schema/TableBuilder.d.ts +274 -16
  149. package/dist/schema/TableBuilder.d.ts.map +1 -1
  150. package/dist/schema/TableBuilder.js +656 -52
  151. package/dist/schema/TableBuilder.js.map +1 -1
  152. package/dist/schema/catalog.d.ts +71 -0
  153. package/dist/schema/catalog.d.ts.map +1 -0
  154. package/dist/schema/catalog.js +158 -0
  155. package/dist/schema/catalog.js.map +1 -0
  156. package/dist/schema/introspect.d.ts +1 -1
  157. package/dist/schema/introspect.d.ts.map +1 -1
  158. package/dist/schema/introspect.js +26 -11
  159. package/dist/schema/introspect.js.map +1 -1
  160. package/dist/schema/types.d.ts +150 -1
  161. package/dist/schema/types.d.ts.map +1 -1
  162. package/dist/schema/types.js +11 -0
  163. package/dist/schema/types.js.map +1 -1
  164. package/dist/seeders.d.ts +7 -0
  165. package/dist/seeders.d.ts.map +1 -0
  166. package/dist/seeders.js +7 -0
  167. package/dist/seeders.js.map +1 -0
  168. package/dist/services/db.d.ts +122 -5
  169. package/dist/services/db.d.ts.map +1 -1
  170. package/dist/services/db.js +187 -14
  171. package/dist/services/db.js.map +1 -1
  172. package/dist/testing/DatabaseCleanup.d.ts +33 -7
  173. package/dist/testing/DatabaseCleanup.d.ts.map +1 -1
  174. package/dist/testing/DatabaseCleanup.js +54 -24
  175. package/dist/testing/DatabaseCleanup.js.map +1 -1
  176. package/dist/testing/DbAssertions.d.ts +48 -0
  177. package/dist/testing/DbAssertions.d.ts.map +1 -0
  178. package/dist/testing/DbAssertions.js +80 -0
  179. package/dist/testing/DbAssertions.js.map +1 -0
  180. package/dist/testing/Factory.d.ts +166 -15
  181. package/dist/testing/Factory.d.ts.map +1 -1
  182. package/dist/testing/Factory.js +497 -23
  183. package/dist/testing/Factory.js.map +1 -1
  184. package/dist/testing/TestUtils.d.ts +49 -0
  185. package/dist/testing/TestUtils.d.ts.map +1 -0
  186. package/dist/testing/TestUtils.js +79 -0
  187. package/dist/testing/TestUtils.js.map +1 -0
  188. package/dist/testing/index.d.ts +3 -1
  189. package/dist/testing/index.d.ts.map +1 -1
  190. package/dist/testing/index.js +3 -1
  191. package/dist/testing/index.js.map +1 -1
  192. package/dist/types/relations.d.ts +31 -0
  193. package/dist/types/relations.d.ts.map +1 -0
  194. package/dist/types/relations.js +20 -0
  195. package/dist/types/relations.js.map +1 -0
  196. package/index.darwin-arm64.node +0 -0
  197. package/index.darwin-x64.node +0 -0
  198. package/index.linux-arm64-gnu.node +0 -0
  199. package/index.linux-x64-gnu.node +0 -0
  200. package/index.win32-x64-msvc.node +0 -0
  201. package/package.json +32 -2
  202. package/scripts/guard-publish.mjs +15 -0
  203. package/src/AtlasProvider.ts +305 -52
  204. package/src/BaseEntity.ts +460 -41
  205. package/src/BaseModel.ts +461 -0
  206. package/src/BaseRepository.ts +1710 -421
  207. package/src/ConnectionManager.ts +298 -0
  208. package/src/ModelQuery.ts +2998 -335
  209. package/src/Transaction.ts +227 -30
  210. package/src/adapters/NapiDbAdapter.ts +224 -17
  211. package/src/console/contract.ts +96 -0
  212. package/src/console/factoryCommands.ts +73 -0
  213. package/src/console/migrationCommands.ts +448 -0
  214. package/src/console/schemaCheckCommand.ts +26 -22
  215. package/src/console/schemaDumpCommand.ts +96 -0
  216. package/src/console/schemaGenerateCommand.ts +380 -0
  217. package/src/console/seederCommands.ts +174 -0
  218. package/src/decorators/entity.ts +71 -8
  219. package/src/decorators/hooks.ts +5 -3
  220. package/src/events.ts +117 -0
  221. package/src/factories.ts +10 -0
  222. package/src/index.ts +69 -3
  223. package/src/lucid-schema.ts +22 -0
  224. package/src/metadata-keys.ts +3 -2
  225. package/src/naming/NamingStrategy.ts +23 -0
  226. package/src/orm.ts +13 -0
  227. package/src/query/DatabaseQueryBuilder.ts +2724 -0
  228. package/src/query/DmlBuilder.ts +131 -0
  229. package/src/query/QueryBuilder.ts +8 -0
  230. package/src/query/RawQueryBuilder.ts +173 -0
  231. package/src/query/interpolate.ts +73 -0
  232. package/src/query/native.ts +29 -0
  233. package/src/query/operators.ts +27 -0
  234. package/src/schema/Migration.ts +71 -3
  235. package/src/schema/MigrationRunner.ts +676 -69
  236. package/src/schema/Schema.ts +359 -3
  237. package/src/schema/SchemaDumper.ts +518 -0
  238. package/src/schema/Seeder.ts +47 -10
  239. package/src/schema/TableBuilder.ts +783 -52
  240. package/src/schema/catalog.ts +236 -0
  241. package/src/schema/introspect.ts +26 -9
  242. package/src/schema/types.ts +137 -2
  243. package/src/seeders.ts +16 -0
  244. package/src/services/db.ts +361 -21
  245. package/src/testing/DatabaseCleanup.ts +78 -25
  246. package/src/testing/DbAssertions.ts +127 -0
  247. package/src/testing/Factory.ts +837 -40
  248. package/src/testing/TestUtils.ts +123 -0
  249. package/src/testing/index.ts +12 -1
  250. package/src/types/relations.ts +43 -0
@@ -10,6 +10,7 @@ import {
10
10
  COMPUTED_KEY,
11
11
  type ColumnSerializeConfig,
12
12
  } from "../metadata-keys.js";
13
+ import { getNamingStrategy } from "../naming/NamingStrategy.js";
13
14
 
14
15
  const ENTITY_KEY = Symbol("atlas:entity");
15
16
  const COLUMNS_KEY = Symbol("atlas:columns");
@@ -62,8 +63,14 @@ export interface ColumnAdapter {
62
63
  *
63
64
  * MUST stay synchronous; the bind layer cannot await before handing values
64
65
  * to the Rust DML compiler. Returning a Promise throws.
66
+ *
67
+ * Signature mirrors Adonis Lucid: `(value, attribute, model)`. `attribute` is
68
+ * the model property key; `model` is the entity instance being persisted, or
69
+ * `undefined` on query-builder paths that carry no instance (e.g.
70
+ * `updateWhere`). Both extra args are optional — a one-argument adapter keeps
71
+ * working unchanged.
65
72
  */
66
- prepare?: (value: unknown) => unknown;
73
+ prepare?: (value: unknown, attribute?: string, model?: unknown) => unknown;
67
74
  /**
68
75
  * Transform the raw DB value into the model attribute (DB → model). Mirror
69
76
  * of Adonis Lucid's `@column.consume`. For entity columns, runs in
@@ -76,8 +83,12 @@ export interface ColumnAdapter {
76
83
  * null/undefined inputs to preserve nullable semantics.
77
84
  *
78
85
  * MUST stay synchronous (same constraint as `prepare`).
86
+ *
87
+ * Signature mirrors Adonis Lucid: `(value, attribute, model)`. `attribute` is
88
+ * the model property key; `model` is the entity being hydrated. Both extra
89
+ * args are optional — a one-argument adapter keeps working unchanged.
79
90
  */
80
- consume?: (value: unknown) => unknown;
91
+ consume?: (value: unknown, attribute?: string, model?: unknown) => unknown;
81
92
  }
82
93
 
83
94
  export interface ColumnMetadata extends ColumnAdapter {
@@ -86,17 +97,25 @@ export interface ColumnMetadata extends ColumnAdapter {
86
97
  nullable?: boolean;
87
98
  default?: unknown;
88
99
  serializeAs?: string | null;
89
- serialize?: (value: unknown) => unknown;
100
+ serialize?: (value: unknown, attribute?: string, model?: unknown) => unknown;
101
+ /** Explicit DB column name override (AdonisJS Lucid `columnName`). */
102
+ columnName?: string;
90
103
  }
91
104
 
92
105
  export interface ColumnOptions extends ColumnAdapter {
93
106
  type?: string;
94
107
  nullable?: boolean;
95
108
  default?: unknown;
109
+ /**
110
+ * Map this property to an explicitly-named DB column (AdonisJS Lucid
111
+ * `columnName`), instead of the default `camelCase → snake_case` convention.
112
+ * For legacy/non-conventional schemas, e.g. `@Column({ columnName: 'USR_MAIL' })`.
113
+ */
114
+ columnName?: string;
96
115
  /** Rename this column at `toJSON` time. Use `null` to hide it entirely. */
97
116
  serializeAs?: string | null;
98
117
  /** Transform the value at `toJSON` time (e.g. mask a phone number, coerce a Date). */
99
- serialize?: (value: unknown) => unknown;
118
+ serialize?: (value: unknown, attribute?: string, model?: unknown) => unknown;
100
119
  }
101
120
 
102
121
  type Constructor = new (...args: unknown[]) => unknown;
@@ -108,6 +127,12 @@ export interface ManyToManyOptions {
108
127
  foreignKey?: string;
109
128
  /** Foreign key in the pivot table pointing to the RELATED entity (default: `${relatedTable}_id`). */
110
129
  otherKey?: string;
130
+ /**
131
+ * The column ON THE RELATED model that `otherKey` references (Adonis Lucid
132
+ * `relatedKey`). Defaults to the related model's primary key; override when the
133
+ * pivot points at a non-PK column of the related side.
134
+ */
135
+ relatedKey?: string;
111
136
  /** Pivot extra columns to project into `$extras.pivot_<col>` on loaded relations (Story 31.8). */
112
137
  pivotColumns?: string[];
113
138
  /** Auto-write `created_at`/`updated_at` on `attach`/`sync` (Story 31.8). */
@@ -119,8 +144,7 @@ export interface ManyToManyOptions {
119
144
  * `@Column({ prepare, consume })` for entity columns: `prepare` runs on
120
145
  * every `attach()` / `sync()` write, before the value reaches the SQL bind
121
146
  * layer; `consume` runs on every load that projects the extra into
122
- * `$extras.pivot_<col>` (when the projection mechanism lands — currently
123
- * the load-side hook is dormant).
147
+ * `$extras.pivot_<col>` (Adonis Lucid signature `(value, attribute, model)`).
124
148
  *
125
149
  * Keys are pivot-row column names as written in the SQL (e.g. `amount`);
126
150
  * adapters are reused verbatim from `@c9up/atom/atlas` and friends so a
@@ -202,6 +226,7 @@ export function Column(options?: ColumnOptions): PropertyDecorator {
202
226
  serialize: options?.serialize,
203
227
  prepare: options?.prepare,
204
228
  consume: options?.consume,
229
+ columnName: options?.columnName,
205
230
  });
206
231
  Reflect.defineMetadata(COLUMNS_KEY, columns, target.constructor);
207
232
  }
@@ -445,7 +470,12 @@ export function HasMany(
445
470
  };
446
471
  }
447
472
 
448
- /** @HasOneThrough(() => Related, () => Through, { firstKey, secondKey, localKey, secondLocalKey, onQuery }) */
473
+ /**
474
+ * `@HasOneThrough(() => Related, () => Through, { firstKey, secondKey, localKey, secondLocalKey, onQuery })`
475
+ *
476
+ * An atlas addition — Lucid has no hasOneThrough relation. Same two-hop
477
+ * traversal as {@link HasManyThrough}, returning a single row.
478
+ */
449
479
  export function HasOneThrough(
450
480
  target: () => Constructor,
451
481
  through: () => Constructor,
@@ -490,12 +520,27 @@ export function ManyToMany(
490
520
  type: "manyToMany",
491
521
  target,
492
522
  pivot: options,
523
+ // `localKey` selects which parent column the pivot FK references
524
+ // (default: the parent PK). Without copying it here the value the type
525
+ // accepts is silently dropped and the pivot always targets the PK.
526
+ localKey: options.localKey,
493
527
  onQuery: options.onQuery,
494
528
  serializeAs: options.serializeAs,
495
529
  });
496
530
  };
497
531
  }
498
532
 
533
+ // camelCase aliases matching Adonis Lucid's decorator naming (`@belongsTo`,
534
+ // `@hasMany`, `@manyToMany`, …), alongside atlas's PascalCase originals — so a
535
+ // migration/model copied from Lucid resolves without renaming. Same decorators,
536
+ // same options; just the Lucid-idiomatic spelling.
537
+ export const belongsTo = BelongsTo;
538
+ export const hasOne = HasOne;
539
+ export const hasMany = HasMany;
540
+ export const hasOneThrough = HasOneThrough;
541
+ export const hasManyThrough = HasManyThrough;
542
+ export const manyToMany = ManyToMany;
543
+
499
544
  function addRelation(target: object, relation: RelationMetadata): void {
500
545
  const relations: RelationMetadata[] =
501
546
  Reflect.getOwnMetadata(RELATIONS_KEY, target) ?? [];
@@ -510,13 +555,31 @@ export function getEntityMetadata(
510
555
  return Reflect.getMetadata(ENTITY_KEY, target);
511
556
  }
512
557
 
558
+ /**
559
+ * Return the class's `@Entity` metadata, SYNTHESIZING it when the decorator is
560
+ * absent: the table name is inferred from the class name via the naming strategy
561
+ * (or an explicit `static table`). AdonisJS Lucid parity — a model needs no
562
+ * explicit `@Entity('table')`. Used by both `BaseModel` and the `BaseRepository`
563
+ * constructor so the Data-Mapper and Active-Record paths share one convention.
564
+ */
565
+ export function ensureEntityMetadata(target: Constructor): EntityMetadata {
566
+ const existing = getEntityMetadata(target);
567
+ if (existing) return existing;
568
+ const staticTable = (target as { table?: string }).table;
569
+ const table = staticTable ?? getNamingStrategy(target).tableName(target.name);
570
+ Entity(table)(target);
571
+ return getEntityMetadata(target) ?? { tableName: table };
572
+ }
573
+
513
574
  /** Get column metadata for a class (returns a copy). */
514
575
  export function getColumnMetadata(target: Constructor): ColumnMetadata[] {
515
576
  return [...(Reflect.getMetadata(COLUMNS_KEY, target) ?? [])];
516
577
  }
517
578
 
518
- /** Get primary key property name. */
579
+ /** Get primary key property name. A `static primaryKey` (AdonisJS Lucid) wins over the `@PrimaryKey()` decorator. */
519
580
  export function getPrimaryKey(target: Constructor): string | undefined {
581
+ const staticPk = (target as { primaryKey?: string }).primaryKey;
582
+ if (staticPk) return staticPk;
520
583
  return Reflect.getMetadata(PRIMARY_KEY, target);
521
584
  }
522
585
 
@@ -20,7 +20,7 @@
20
20
 
21
21
  import "reflect-metadata";
22
22
  import type { BaseEntity } from "../BaseEntity.js";
23
- import type { ModelQuery } from "../ModelQuery.js";
23
+ import type { ModelQuery, Paginator } from "../ModelQuery.js";
24
24
 
25
25
  /** All hook kinds Atlas supports — mirrors Lucid's surface. */
26
26
  export type HookKind =
@@ -53,8 +53,10 @@ export interface HookArgs {
53
53
  afterFind: BaseEntity | null;
54
54
  beforeFetch: ModelQuery<BaseEntity>;
55
55
  afterFetch: BaseEntity[];
56
- beforePaginate: ModelQuery<BaseEntity>;
57
- afterPaginate: BaseEntity[];
56
+ // Adonis Lucid: beforePaginate receives the [countQuery, query] tuple (mutate
57
+ // both to keep count and results in sync); afterPaginate receives the paginator.
58
+ beforePaginate: [ModelQuery<BaseEntity>, ModelQuery<BaseEntity>];
59
+ afterPaginate: Paginator<BaseEntity>;
58
60
  }
59
61
 
60
62
  /** A hook handler is a static function that receives the kind-specific arg. */
package/src/events.ts ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Query observability — atlas's equivalent of Lucid's `db:query` event.
3
+ *
4
+ * Agnostic by design: atlas cannot import the framework's emitter (it is a
5
+ * standalone package), so it owns a tiny listener registry instead. An
6
+ * integration package bridges it to whatever emitter the app uses:
7
+ *
8
+ * onDbQuery((event) => emitter.emit('db:query', event))
9
+ *
10
+ * Emission is opt-in. It costs a `performance.now()` pair per query, so it only
11
+ * happens when a connection is configured with `debug: true` or a single query
12
+ * asks via `.debug()` — and even then, only if someone is listening.
13
+ */
14
+
15
+ /**
16
+ * A single executed SQL statement. Mirrors Lucid's `db:query` payload, minus
17
+ * the fields that only make sense inside AdonisJS.
18
+ */
19
+ export interface DbQueryEvent {
20
+ /** The SQL as sent to the driver, placeholders included. */
21
+ sql: string;
22
+ /** The bound parameters. Never interpolated into `sql`. */
23
+ bindings: readonly unknown[];
24
+ /** Wall-clock duration in milliseconds, including the NAPI round-trip. */
25
+ duration: number;
26
+ /** Connection name, when the app named it. */
27
+ connection?: string;
28
+ /** Entity class name, when the query came from a repository/model. */
29
+ model?: string;
30
+ /** The call that produced it (`exec`, `first`, `paginate`, …). */
31
+ method?: string;
32
+ /** True for schema statements (migrations), false for DML/queries. */
33
+ ddl?: boolean;
34
+ /** True when the statement ran inside an interactive transaction. */
35
+ inTransaction?: boolean;
36
+ /** Set when the statement threw — the event is emitted either way. */
37
+ error?: Error;
38
+ /**
39
+ * Arbitrary metadata a caller attached via `query.reporterData({...})`
40
+ * (Adonis Lucid `reporterData`) — request id, user id, feature flag, …
41
+ */
42
+ reporterData?: Record<string, unknown>;
43
+ }
44
+
45
+ export type DbQueryListener = (event: DbQueryEvent) => void;
46
+
47
+ const listeners = new Set<DbQueryListener>();
48
+
49
+ /**
50
+ * Subscribe to every observed query. Returns an unsubscribe function.
51
+ *
52
+ * A listener that throws would otherwise take down the query that triggered it,
53
+ * so throws are swallowed — observability must never change behaviour.
54
+ */
55
+ export function onDbQuery(listener: DbQueryListener): () => void {
56
+ listeners.add(listener);
57
+ return () => {
58
+ listeners.delete(listener);
59
+ };
60
+ }
61
+
62
+ /** Remove every listener. Intended for test teardown. */
63
+ export function clearDbQueryListeners(): void {
64
+ listeners.clear();
65
+ }
66
+
67
+ /**
68
+ * Whether anyone is listening. Checked before timing a query so the
69
+ * instrumentation costs nothing when unused.
70
+ */
71
+ export function hasDbQueryListeners(): boolean {
72
+ return listeners.size > 0;
73
+ }
74
+
75
+ /** Emit to every listener. Package-internal. */
76
+ export function emitDbQuery(event: DbQueryEvent): void {
77
+ for (const listener of listeners) {
78
+ try {
79
+ listener(event);
80
+ } catch {
81
+ // A broken listener must not fail the query it is reporting on.
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Render a query event as a single log line (Lucid's `prettyPrint`).
88
+ *
89
+ * The bindings are appended as JSON, NOT interpolated into the SQL: an
90
+ * interpolated line reads like runnable SQL while having none of the escaping
91
+ * that made the real statement safe, and it is exactly the string someone
92
+ * copies into a console later.
93
+ */
94
+ export function prettyPrintQuery(event: DbQueryEvent): string {
95
+ const parts = [`${event.duration.toFixed(2)}ms`];
96
+ if (event.connection) parts.push(event.connection);
97
+ if (event.model) parts.push(event.model);
98
+ if (event.method) parts.push(event.method);
99
+ if (event.inTransaction) parts.push("trx");
100
+ if (event.error) parts.push(`ERROR: ${event.error.message}`);
101
+
102
+ const head = `[atlas] ${parts.join(" ")}`;
103
+ const bindings =
104
+ event.bindings.length > 0 ? ` -- ${safeJson(event.bindings)}` : "";
105
+ return `${head} ${event.sql}${bindings}`;
106
+ }
107
+
108
+ /** JSON that can't throw on a circular / non-serialisable binding. */
109
+ function safeJson(value: unknown): string {
110
+ try {
111
+ return JSON.stringify(value, (_k, v) =>
112
+ typeof v === "bigint" ? `${v}n` : v,
113
+ );
114
+ } catch {
115
+ return "[unserialisable bindings]";
116
+ }
117
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `@c9up/atlas/factories` — Adonis Lucid `@adonisjs/lucid/factories` parity
3
+ * subpath. The model factory: `Factory.define(Model, cb).build()` (or the
4
+ * `factory()` shorthand) for generating and persisting test data.
5
+ */
6
+ export {
7
+ type FactoryCommandOptions,
8
+ makeFactoryCommand,
9
+ } from "./console/factoryCommands.js";
10
+ export { Factory, type FactoryBuilder, factory } from "./testing/Factory.js";
package/src/index.ts CHANGED
@@ -10,18 +10,59 @@ export { SQLITE_PROD_PRAGMAS } from "./AtlasProvider.js";
10
10
  export type {
11
11
  AsyncDatabaseConnection,
12
12
  ConnectRetryOptions,
13
+ ObservabilityOptions,
14
+ QueryMeta,
13
15
  } from "./adapters/NapiDbAdapter.js";
14
16
  export { createNapiConnection } from "./adapters/NapiDbAdapter.js";
15
17
  export type { DomainEvent } from "./BaseEntity.js";
16
18
  export { BaseEntity } from "./BaseEntity.js";
19
+ export { BaseModel } from "./BaseModel.js";
17
20
  export type { DatabaseConnection } from "./BaseRepository.js";
18
21
  export { BaseRepository } from "./BaseRepository.js";
19
22
  export { defineConfig } from "./config.js";
20
23
  export { configure } from "./configure.js";
21
24
  export {
22
- type AtlasCommand,
25
+ type ArgumentMetaData,
26
+ argument,
27
+ type CommandOptions,
28
+ type FlagMetaData,
29
+ flag,
30
+ } from "./console/contract.js";
31
+ export {
32
+ type FactoryCommandOptions,
33
+ makeFactoryCommand,
34
+ } from "./console/factoryCommands.js";
35
+ export {
36
+ dbWipeCommand,
37
+ type MigrationCommandOptions,
38
+ makeMigrationCommand,
39
+ migrationFreshCommand,
40
+ migrationRefreshCommand,
41
+ migrationResetCommand,
42
+ migrationRollbackCommand,
43
+ migrationRunCommand,
44
+ migrationStatusCommand,
45
+ migrationUnlockCommand,
46
+ } from "./console/migrationCommands.js";
47
+ export {
48
+ type AtlasCommandClass,
23
49
  schemaCheckCommand,
24
50
  } from "./console/schemaCheckCommand.js";
51
+ export {
52
+ type SchemaDumpCommandOptions,
53
+ schemaDumpCommand,
54
+ } from "./console/schemaDumpCommand.js";
55
+ export {
56
+ generateSchemaFile,
57
+ renderSchemaFile,
58
+ type SchemaGenerateOptions,
59
+ schemaGenerateCommand,
60
+ } from "./console/schemaGenerateCommand.js";
61
+ export {
62
+ dbSeedCommand,
63
+ makeSeederCommand,
64
+ type SeederCommandOptions,
65
+ } from "./console/seederCommands.js";
25
66
  export type {
26
67
  ColumnAdapter,
27
68
  ColumnMetadata,
@@ -34,6 +75,7 @@ export type {
34
75
  } from "./decorators/entity.js";
35
76
  export {
36
77
  BelongsTo,
78
+ belongsTo,
37
79
  Column,
38
80
  column,
39
81
  computed,
@@ -47,8 +89,13 @@ export {
47
89
  HasManyThrough,
48
90
  HasOne,
49
91
  HasOneThrough,
92
+ hasMany,
93
+ hasManyThrough,
94
+ hasOne,
95
+ hasOneThrough,
50
96
  hasSoftDeletes,
51
97
  ManyToMany,
98
+ manyToMany,
52
99
  PrimaryKey,
53
100
  SoftDeletes,
54
101
  } from "./decorators/entity.js";
@@ -77,6 +124,12 @@ export {
77
124
  OptimisticLockError,
78
125
  RelationNotLoadedError,
79
126
  } from "./errors.js";
127
+ export type { DbQueryEvent, DbQueryListener } from "./events.js";
128
+ export {
129
+ clearDbQueryListeners,
130
+ onDbQuery,
131
+ prettyPrintQuery,
132
+ } from "./events.js";
80
133
  export {
81
134
  isAtlasStrictMode,
82
135
  ModelQuery,
@@ -103,7 +156,9 @@ export {
103
156
  type IntrospectedColumn,
104
157
  introspectTable,
105
158
  } from "./schema/introspect.js";
106
- export { Migration } from "./schema/Migration.js";
159
+ // `BaseSchema` is Lucid's name for the migration base class; `Migration` is the
160
+ // atlas alias kept for back-compat. Prefer `extends BaseSchema` (AdonisJS parity).
161
+ export { Migration, Migration as BaseSchema } from "./schema/Migration.js";
107
162
  export type {
108
163
  DatabaseAdapter,
109
164
  MigrationRecord,
@@ -124,6 +179,17 @@ export {
124
179
  typesCompatible,
125
180
  verifySchema,
126
181
  } from "./schema/SchemaCheck.js";
182
+ export {
183
+ type PgForeignKey,
184
+ readSchemaDumpManifest,
185
+ renderPgCreateTable,
186
+ renderPgForeignKeyDdl,
187
+ SchemaDumper,
188
+ type SchemaDumperOptions,
189
+ type SchemaDumpManifest,
190
+ type SchemaDumpResult,
191
+ schemaDumpManifestPath,
192
+ } from "./schema/SchemaDumper.js";
127
193
  export {
128
194
  BaseSeeder,
129
195
  runSeederDirectory,
@@ -133,4 +199,4 @@ export {
133
199
  export type { TransactionClient } from "./Transaction.js";
134
200
  export { transaction } from "./Transaction.js";
135
201
  export { truncateAll, useTransaction } from "./testing/DatabaseCleanup.js";
136
- export { factory } from "./testing/Factory.js";
202
+ export { Factory, factory } from "./testing/Factory.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@c9up/atlas/schema` — Adonis Lucid `@adonisjs/lucid/schema` parity subpath.
3
+ *
4
+ * The migration base class ({@link Migration}, Lucid's `BaseSchema`), the schema
5
+ * builder and the table builder — everything for authoring migrations. (The file
6
+ * is named `lucid-schema` to avoid colliding with the `schema/` source folder;
7
+ * the public subpath is `@c9up/atlas/schema`.)
8
+ */
9
+ export { RawSql } from "./query/QueryBuilder.js";
10
+ export * from "./schema/Migration.js";
11
+ // Lucid's name for the migration base class (`extends BaseSchema`).
12
+ export { Migration as BaseSchema } from "./schema/Migration.js";
13
+ export * from "./schema/Schema.js";
14
+ export {
15
+ readSchemaDumpManifest,
16
+ SchemaDumper,
17
+ type SchemaDumperOptions,
18
+ type SchemaDumpManifest,
19
+ type SchemaDumpResult,
20
+ schemaDumpManifestPath,
21
+ } from "./schema/SchemaDumper.js";
22
+ export * from "./schema/TableBuilder.js";
@@ -17,6 +17,7 @@ export const COLUMN_SERIALIZE_KEY = Symbol.for("atlas:columnSerialize");
17
17
  export interface ColumnSerializeConfig {
18
18
  /** Rename this column at toJSON time (e.g. `password` → `passwordHash`). Null = hidden. */
19
19
  serializeAs?: string | null;
20
- /** Transform function applied to the value at toJSON time. */
21
- serialize?: (value: unknown) => unknown;
20
+ /** Transform function applied to the value at toJSON time. Signature mirrors
21
+ * Adonis Lucid: `(value, attribute, model)`; extra args optional. */
22
+ serialize?: (value: unknown, attribute?: string, model?: unknown) => unknown;
22
23
  }
@@ -38,6 +38,12 @@ export interface NamingStrategy {
38
38
  ): string;
39
39
  /** Default pivot table name for a manyToMany relation. */
40
40
  relationPivotTable(aClass: string, bClass: string): string;
41
+ /**
42
+ * Remap the pagination `meta` key names emitted by `Paginator.toJSON()`
43
+ * (AdonisJS Lucid `paginationMetaKeys`), e.g. `{ total: 'count', perPage:
44
+ * 'per_page' }`. Optional — omitted keys keep their default name.
45
+ */
46
+ paginationMetaKeys?(): Record<string, string>;
41
47
  }
42
48
 
43
49
  /**
@@ -84,6 +90,23 @@ export class CamelCaseNamingStrategy implements NamingStrategy {
84
90
  const [x, y] = [camelToSnake(aClass), camelToSnake(bClass)].sort();
85
91
  return `${x}_${y}`;
86
92
  }
93
+
94
+ paginationMetaKeys(): Record<string, string> {
95
+ // snake_case, matching Adonis Lucid's default paginator meta shape — even
96
+ // though model attributes serialize as camelCase, Lucid emits pagination
97
+ // meta in snake_case. Override this method to customize.
98
+ return {
99
+ total: "total",
100
+ perPage: "per_page",
101
+ currentPage: "current_page",
102
+ lastPage: "last_page",
103
+ firstPage: "first_page",
104
+ firstPageUrl: "first_page_url",
105
+ lastPageUrl: "last_page_url",
106
+ nextPageUrl: "next_page_url",
107
+ previousPageUrl: "previous_page_url",
108
+ };
109
+ }
87
110
  }
88
111
 
89
112
  /** The default singleton — used when an entity doesn't override `static namingStrategy`. */
package/src/orm.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@c9up/atlas/orm` — Adonis Lucid `@adonisjs/lucid/orm` parity subpath.
3
+ *
4
+ * The Active Record model, the Data Mapper repository, the column/relation
5
+ * decorators and the model lifecycle hooks — everything for defining and
6
+ * querying models. Import organisation mirrors Lucid; the symbols are the same
7
+ * ones re-exported from the package root (`@c9up/atlas`).
8
+ */
9
+ export * from "./BaseEntity.js";
10
+ export * from "./BaseModel.js";
11
+ export * from "./BaseRepository.js";
12
+ export * from "./decorators/entity.js";
13
+ export * from "./decorators/hooks.js";