@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.
- package/README.md +55 -14
- package/db.darwin-arm64.node +0 -0
- package/db.darwin-x64.node +0 -0
- package/db.linux-arm64-gnu.node +0 -0
- package/db.linux-x64-gnu.node +0 -0
- package/db.win32-x64-msvc.node +0 -0
- package/dist/AtlasProvider.d.ts +6 -0
- package/dist/AtlasProvider.d.ts.map +1 -1
- package/dist/AtlasProvider.js +2 -2
- package/dist/AtlasProvider.js.map +1 -1
- package/dist/BaseEntity.d.ts +171 -7
- package/dist/BaseEntity.d.ts.map +1 -1
- package/dist/BaseEntity.js +339 -31
- package/dist/BaseEntity.js.map +1 -1
- package/dist/BaseModel.d.ts +91 -0
- package/dist/BaseModel.d.ts.map +1 -0
- package/dist/BaseModel.js +193 -0
- package/dist/BaseModel.js.map +1 -0
- package/dist/BaseRepository.d.ts +77 -15
- package/dist/BaseRepository.d.ts.map +1 -1
- package/dist/BaseRepository.js +1423 -354
- package/dist/BaseRepository.js.map +1 -1
- package/dist/ModelQuery.d.ts +429 -11
- package/dist/ModelQuery.d.ts.map +1 -1
- package/dist/ModelQuery.js +1733 -145
- package/dist/ModelQuery.js.map +1 -1
- package/dist/Transaction.d.ts +17 -0
- package/dist/Transaction.d.ts.map +1 -1
- package/dist/Transaction.js +57 -5
- package/dist/Transaction.js.map +1 -1
- package/dist/adapters/NapiDbAdapter.d.ts +33 -4
- package/dist/adapters/NapiDbAdapter.d.ts.map +1 -1
- package/dist/adapters/NapiDbAdapter.js +101 -11
- package/dist/adapters/NapiDbAdapter.js.map +1 -1
- package/dist/console/migrationCommands.d.ts +48 -0
- package/dist/console/migrationCommands.d.ts.map +1 -0
- package/dist/console/migrationCommands.js +220 -0
- package/dist/console/migrationCommands.js.map +1 -0
- package/dist/decorators/entity.d.ts +37 -6
- package/dist/decorators/entity.d.ts.map +1 -1
- package/dist/decorators/entity.js +32 -2
- package/dist/decorators/entity.js.map +1 -1
- package/dist/events.d.ts +64 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +82 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/metadata-keys.d.ts +3 -2
- package/dist/metadata-keys.d.ts.map +1 -1
- package/dist/naming/NamingStrategy.d.ts +7 -0
- package/dist/naming/NamingStrategy.d.ts.map +1 -1
- package/dist/naming/NamingStrategy.js +16 -0
- package/dist/naming/NamingStrategy.js.map +1 -1
- package/dist/schema/Migration.d.ts +26 -3
- package/dist/schema/Migration.d.ts.map +1 -1
- package/dist/schema/Migration.js +33 -24
- package/dist/schema/Migration.js.map +1 -1
- package/dist/schema/MigrationRunner.d.ts +43 -32
- package/dist/schema/MigrationRunner.d.ts.map +1 -1
- package/dist/schema/MigrationRunner.js +211 -26
- package/dist/schema/MigrationRunner.js.map +1 -1
- package/dist/schema/Schema.d.ts +57 -0
- package/dist/schema/Schema.d.ts.map +1 -1
- package/dist/schema/Schema.js +138 -3
- package/dist/schema/Schema.js.map +1 -1
- package/dist/schema/SchemaCheck.d.ts.map +1 -1
- package/dist/schema/SchemaCheck.js +3 -1
- package/dist/schema/SchemaCheck.js.map +1 -1
- package/dist/schema/TableBuilder.d.ts +247 -8
- package/dist/schema/TableBuilder.d.ts.map +1 -1
- package/dist/schema/TableBuilder.js +607 -41
- package/dist/schema/TableBuilder.js.map +1 -1
- package/dist/schema/catalog.d.ts +47 -0
- package/dist/schema/catalog.d.ts.map +1 -0
- package/dist/schema/catalog.js +111 -0
- package/dist/schema/catalog.js.map +1 -0
- package/dist/schema/introspect.js.map +1 -1
- package/dist/schema/types.d.ts +150 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +11 -0
- package/dist/schema/types.js.map +1 -1
- package/dist/services/db.d.ts +6 -0
- package/dist/services/db.d.ts.map +1 -1
- package/dist/services/db.js +17 -0
- package/dist/services/db.js.map +1 -1
- package/dist/testing/DatabaseCleanup.d.ts +7 -4
- package/dist/testing/DatabaseCleanup.d.ts.map +1 -1
- package/dist/testing/DatabaseCleanup.js +21 -18
- package/dist/testing/DatabaseCleanup.js.map +1 -1
- package/dist/testing/Factory.d.ts +70 -5
- package/dist/testing/Factory.d.ts.map +1 -1
- package/dist/testing/Factory.js +209 -10
- package/dist/testing/Factory.js.map +1 -1
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +4 -1
- package/scripts/guard-publish.mjs +15 -0
- package/src/AtlasProvider.ts +8 -1
- package/src/BaseEntity.ts +449 -40
- package/src/BaseModel.ts +324 -0
- package/src/BaseRepository.ts +1659 -371
- package/src/ModelQuery.ts +2290 -203
- package/src/Transaction.ts +68 -5
- package/src/adapters/NapiDbAdapter.ts +159 -10
- package/src/console/migrationCommands.ts +258 -0
- package/src/decorators/entity.ts +53 -6
- package/src/events.ts +112 -0
- package/src/index.ts +19 -0
- package/src/metadata-keys.ts +3 -2
- package/src/naming/NamingStrategy.ts +23 -0
- package/src/schema/Migration.ts +42 -3
- package/src/schema/MigrationRunner.ts +270 -27
- package/src/schema/Schema.ts +210 -3
- package/src/schema/SchemaCheck.ts +7 -2
- package/src/schema/TableBuilder.ts +735 -41
- package/src/schema/catalog.ts +166 -0
- package/src/schema/introspect.ts +3 -4
- package/src/schema/types.ts +137 -2
- package/src/services/db.ts +28 -0
- package/src/testing/DatabaseCleanup.ts +23 -22
- package/src/testing/Factory.ts +332 -15
package/src/decorators/entity.ts
CHANGED
|
@@ -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;
|
|
@@ -202,6 +221,7 @@ export function Column(options?: ColumnOptions): PropertyDecorator {
|
|
|
202
221
|
serialize: options?.serialize,
|
|
203
222
|
prepare: options?.prepare,
|
|
204
223
|
consume: options?.consume,
|
|
224
|
+
columnName: options?.columnName,
|
|
205
225
|
});
|
|
206
226
|
Reflect.defineMetadata(COLUMNS_KEY, columns, target.constructor);
|
|
207
227
|
}
|
|
@@ -445,7 +465,12 @@ export function HasMany(
|
|
|
445
465
|
};
|
|
446
466
|
}
|
|
447
467
|
|
|
448
|
-
/**
|
|
468
|
+
/**
|
|
469
|
+
* `@HasOneThrough(() => Related, () => Through, { firstKey, secondKey, localKey, secondLocalKey, onQuery })`
|
|
470
|
+
*
|
|
471
|
+
* An atlas addition — Lucid has no hasOneThrough relation. Same two-hop
|
|
472
|
+
* traversal as {@link HasManyThrough}, returning a single row.
|
|
473
|
+
*/
|
|
449
474
|
export function HasOneThrough(
|
|
450
475
|
target: () => Constructor,
|
|
451
476
|
through: () => Constructor,
|
|
@@ -490,6 +515,10 @@ export function ManyToMany(
|
|
|
490
515
|
type: "manyToMany",
|
|
491
516
|
target,
|
|
492
517
|
pivot: options,
|
|
518
|
+
// `localKey` selects which parent column the pivot FK references
|
|
519
|
+
// (default: the parent PK). Without copying it here the value the type
|
|
520
|
+
// accepts is silently dropped and the pivot always targets the PK.
|
|
521
|
+
localKey: options.localKey,
|
|
493
522
|
onQuery: options.onQuery,
|
|
494
523
|
serializeAs: options.serializeAs,
|
|
495
524
|
});
|
|
@@ -510,13 +539,31 @@ export function getEntityMetadata(
|
|
|
510
539
|
return Reflect.getMetadata(ENTITY_KEY, target);
|
|
511
540
|
}
|
|
512
541
|
|
|
542
|
+
/**
|
|
543
|
+
* Return the class's `@Entity` metadata, SYNTHESIZING it when the decorator is
|
|
544
|
+
* absent: the table name is inferred from the class name via the naming strategy
|
|
545
|
+
* (or an explicit `static table`). AdonisJS Lucid parity — a model needs no
|
|
546
|
+
* explicit `@Entity('table')`. Used by both `BaseModel` and the `BaseRepository`
|
|
547
|
+
* constructor so the Data-Mapper and Active-Record paths share one convention.
|
|
548
|
+
*/
|
|
549
|
+
export function ensureEntityMetadata(target: Constructor): EntityMetadata {
|
|
550
|
+
const existing = getEntityMetadata(target);
|
|
551
|
+
if (existing) return existing;
|
|
552
|
+
const staticTable = (target as { table?: string }).table;
|
|
553
|
+
const table = staticTable ?? getNamingStrategy(target).tableName(target.name);
|
|
554
|
+
Entity(table)(target);
|
|
555
|
+
return getEntityMetadata(target) ?? { tableName: table };
|
|
556
|
+
}
|
|
557
|
+
|
|
513
558
|
/** Get column metadata for a class (returns a copy). */
|
|
514
559
|
export function getColumnMetadata(target: Constructor): ColumnMetadata[] {
|
|
515
560
|
return [...(Reflect.getMetadata(COLUMNS_KEY, target) ?? [])];
|
|
516
561
|
}
|
|
517
562
|
|
|
518
|
-
/** Get primary key property name. */
|
|
563
|
+
/** Get primary key property name. A `static primaryKey` (AdonisJS Lucid) wins over the `@PrimaryKey()` decorator. */
|
|
519
564
|
export function getPrimaryKey(target: Constructor): string | undefined {
|
|
565
|
+
const staticPk = (target as { primaryKey?: string }).primaryKey;
|
|
566
|
+
if (staticPk) return staticPk;
|
|
520
567
|
return Reflect.getMetadata(PRIMARY_KEY, target);
|
|
521
568
|
}
|
|
522
569
|
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
|
|
40
|
+
export type DbQueryListener = (event: DbQueryEvent) => void;
|
|
41
|
+
|
|
42
|
+
const listeners = new Set<DbQueryListener>();
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe to every observed query. Returns an unsubscribe function.
|
|
46
|
+
*
|
|
47
|
+
* A listener that throws would otherwise take down the query that triggered it,
|
|
48
|
+
* so throws are swallowed — observability must never change behaviour.
|
|
49
|
+
*/
|
|
50
|
+
export function onDbQuery(listener: DbQueryListener): () => void {
|
|
51
|
+
listeners.add(listener);
|
|
52
|
+
return () => {
|
|
53
|
+
listeners.delete(listener);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Remove every listener. Intended for test teardown. */
|
|
58
|
+
export function clearDbQueryListeners(): void {
|
|
59
|
+
listeners.clear();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether anyone is listening. Checked before timing a query so the
|
|
64
|
+
* instrumentation costs nothing when unused.
|
|
65
|
+
*/
|
|
66
|
+
export function hasDbQueryListeners(): boolean {
|
|
67
|
+
return listeners.size > 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Emit to every listener. Package-internal. */
|
|
71
|
+
export function emitDbQuery(event: DbQueryEvent): void {
|
|
72
|
+
for (const listener of listeners) {
|
|
73
|
+
try {
|
|
74
|
+
listener(event);
|
|
75
|
+
} catch {
|
|
76
|
+
// A broken listener must not fail the query it is reporting on.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Render a query event as a single log line (Lucid's `prettyPrint`).
|
|
83
|
+
*
|
|
84
|
+
* The bindings are appended as JSON, NOT interpolated into the SQL: an
|
|
85
|
+
* interpolated line reads like runnable SQL while having none of the escaping
|
|
86
|
+
* that made the real statement safe, and it is exactly the string someone
|
|
87
|
+
* copies into a console later.
|
|
88
|
+
*/
|
|
89
|
+
export function prettyPrintQuery(event: DbQueryEvent): string {
|
|
90
|
+
const parts = [`${event.duration.toFixed(2)}ms`];
|
|
91
|
+
if (event.connection) parts.push(event.connection);
|
|
92
|
+
if (event.model) parts.push(event.model);
|
|
93
|
+
if (event.method) parts.push(event.method);
|
|
94
|
+
if (event.inTransaction) parts.push("trx");
|
|
95
|
+
if (event.error) parts.push(`ERROR: ${event.error.message}`);
|
|
96
|
+
|
|
97
|
+
const head = `[atlas] ${parts.join(" ")}`;
|
|
98
|
+
const bindings =
|
|
99
|
+
event.bindings.length > 0 ? ` -- ${safeJson(event.bindings)}` : "";
|
|
100
|
+
return `${head} ${event.sql}${bindings}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** JSON that can't throw on a circular / non-serialisable binding. */
|
|
104
|
+
function safeJson(value: unknown): string {
|
|
105
|
+
try {
|
|
106
|
+
return JSON.stringify(value, (_k, v) =>
|
|
107
|
+
typeof v === "bigint" ? `${v}n` : v,
|
|
108
|
+
);
|
|
109
|
+
} catch {
|
|
110
|
+
return "[unserialisable bindings]";
|
|
111
|
+
}
|
|
112
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,14 +10,27 @@ 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";
|
|
24
|
+
export {
|
|
25
|
+
dbWipeCommand,
|
|
26
|
+
type MigrationCommandOptions,
|
|
27
|
+
makeMigrationCommand,
|
|
28
|
+
migrationRefreshCommand,
|
|
29
|
+
migrationResetCommand,
|
|
30
|
+
migrationRollbackCommand,
|
|
31
|
+
migrationRunCommand,
|
|
32
|
+
migrationStatusCommand,
|
|
33
|
+
} from "./console/migrationCommands.js";
|
|
21
34
|
export {
|
|
22
35
|
type AtlasCommand,
|
|
23
36
|
schemaCheckCommand,
|
|
@@ -77,6 +90,12 @@ export {
|
|
|
77
90
|
OptimisticLockError,
|
|
78
91
|
RelationNotLoadedError,
|
|
79
92
|
} from "./errors.js";
|
|
93
|
+
export type { DbQueryEvent, DbQueryListener } from "./events.js";
|
|
94
|
+
export {
|
|
95
|
+
clearDbQueryListeners,
|
|
96
|
+
onDbQuery,
|
|
97
|
+
prettyPrintQuery,
|
|
98
|
+
} from "./events.js";
|
|
80
99
|
export {
|
|
81
100
|
isAtlasStrictMode,
|
|
82
101
|
ModelQuery,
|
package/src/metadata-keys.ts
CHANGED
|
@@ -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
|
-
|
|
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/schema/Migration.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { AtlasDialect } from "../query/native.js";
|
|
8
8
|
import { RawSql } from "../query/QueryBuilder.js";
|
|
9
|
+
import type { CatalogConnection } from "./catalog.js";
|
|
9
10
|
import { Schema } from "./SchemaBuilder.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -29,9 +30,18 @@ import { Schema } from "./SchemaBuilder.js";
|
|
|
29
30
|
* }
|
|
30
31
|
* }
|
|
31
32
|
*/
|
|
33
|
+
/**
|
|
34
|
+
* A deferred operation registered with `this.defer()`. Runs after the
|
|
35
|
+
* migration's schema statements, with a live connection handle (execute/query).
|
|
36
|
+
*/
|
|
37
|
+
export type DeferredMigrationCallback = (
|
|
38
|
+
db: CatalogConnection,
|
|
39
|
+
) => Promise<void> | void;
|
|
40
|
+
|
|
32
41
|
export abstract class Migration {
|
|
33
42
|
readonly schema: Schema;
|
|
34
43
|
readonly #dialect: AtlasDialect;
|
|
44
|
+
#deferred: DeferredMigrationCallback[] = [];
|
|
35
45
|
|
|
36
46
|
constructor(dialect?: AtlasDialect) {
|
|
37
47
|
this.schema = new Schema(dialect);
|
|
@@ -41,6 +51,27 @@ export abstract class Migration {
|
|
|
41
51
|
this.#dialect = dialect ?? "sqlite";
|
|
42
52
|
}
|
|
43
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Register a callback to run AFTER this migration's schema statements execute
|
|
56
|
+
* (Adonis Lucid `this.defer`). Typically used to seed data into a table the
|
|
57
|
+
* same migration just created. The callback gets a live connection handle.
|
|
58
|
+
*
|
|
59
|
+
* Note: deferred callbacks run after the schema statements have committed, so
|
|
60
|
+
* they are outside the schema transaction (atlas builds schema as a batch);
|
|
61
|
+
* the migration is only recorded as applied once every deferred callback
|
|
62
|
+
* succeeds. Make deferred work idempotent.
|
|
63
|
+
*/
|
|
64
|
+
defer(callback: DeferredMigrationCallback): void {
|
|
65
|
+
this.#deferred.push(callback);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** @internal Return and clear the deferred callbacks — called by the runner. */
|
|
69
|
+
consumeDeferred(): DeferredMigrationCallback[] {
|
|
70
|
+
const deferred = this.#deferred;
|
|
71
|
+
this.#deferred = [];
|
|
72
|
+
return deferred;
|
|
73
|
+
}
|
|
74
|
+
|
|
44
75
|
/** Apply the migration. */
|
|
45
76
|
abstract up(): Promise<void> | void;
|
|
46
77
|
|
|
@@ -69,15 +100,23 @@ export abstract class Migration {
|
|
|
69
100
|
);
|
|
70
101
|
}
|
|
71
102
|
|
|
72
|
-
/**
|
|
73
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Get the SQL statements generated by this migration. A `connection` (passed
|
|
105
|
+
* by the runner) is bound to the schema so `this.schema.hasTable()` /
|
|
106
|
+
* `hasColumn()` can query the live catalog from inside `up`/`down`.
|
|
107
|
+
*/
|
|
108
|
+
async getUpSQL(connection?: CatalogConnection): Promise<string[]> {
|
|
109
|
+
if (connection) this.schema.bindConnection(connection);
|
|
74
110
|
this.schema.reset();
|
|
111
|
+
this.#deferred = [];
|
|
75
112
|
await this.up();
|
|
76
113
|
return this.schema.toSQL();
|
|
77
114
|
}
|
|
78
115
|
|
|
79
|
-
async getDownSQL(): Promise<string[]> {
|
|
116
|
+
async getDownSQL(connection?: CatalogConnection): Promise<string[]> {
|
|
117
|
+
if (connection) this.schema.bindConnection(connection);
|
|
80
118
|
this.schema.reset();
|
|
119
|
+
this.#deferred = [];
|
|
81
120
|
await this.down();
|
|
82
121
|
return this.schema.toSQL();
|
|
83
122
|
}
|