@rudderjs/database 1.1.0 → 1.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 +70 -0
- package/dist/db.d.ts +21 -3
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +27 -5
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -4
- package/dist/index.js.map +1 -1
- package/dist/native/adapter.d.ts +202 -0
- package/dist/native/adapter.d.ts.map +1 -0
- package/dist/native/adapter.js +440 -0
- package/dist/native/adapter.js.map +1 -0
- package/dist/native/compiler.d.ts +371 -0
- package/dist/native/compiler.d.ts.map +1 -0
- package/dist/native/compiler.js +978 -0
- package/dist/native/compiler.js.map +1 -0
- package/dist/native/dialect-mysql.d.ts +26 -0
- package/dist/native/dialect-mysql.d.ts.map +1 -0
- package/dist/native/dialect-mysql.js +188 -0
- package/dist/native/dialect-mysql.js.map +1 -0
- package/dist/native/dialect-pg.d.ts +26 -0
- package/dist/native/dialect-pg.d.ts.map +1 -0
- package/dist/native/dialect-pg.js +192 -0
- package/dist/native/dialect-pg.js.map +1 -0
- package/dist/native/dialect.d.ts +255 -0
- package/dist/native/dialect.d.ts.map +1 -0
- package/dist/native/dialect.js +237 -0
- package/dist/native/dialect.js.map +1 -0
- package/dist/native/driver.d.ts +37 -0
- package/dist/native/driver.d.ts.map +1 -0
- package/dist/native/driver.js +19 -0
- package/dist/native/driver.js.map +1 -0
- package/dist/native/drivers/better-sqlite3.d.ts +56 -0
- package/dist/native/drivers/better-sqlite3.d.ts.map +1 -0
- package/dist/native/drivers/better-sqlite3.js +171 -0
- package/dist/native/drivers/better-sqlite3.js.map +1 -0
- package/dist/native/drivers/mysql.d.ts +30 -0
- package/dist/native/drivers/mysql.d.ts.map +1 -0
- package/dist/native/drivers/mysql.js +176 -0
- package/dist/native/drivers/mysql.js.map +1 -0
- package/dist/native/drivers/postgres.d.ts +57 -0
- package/dist/native/drivers/postgres.d.ts.map +1 -0
- package/dist/native/drivers/postgres.js +155 -0
- package/dist/native/drivers/postgres.js.map +1 -0
- package/dist/native/errors.d.ts +43 -0
- package/dist/native/errors.d.ts.map +1 -0
- package/dist/native/errors.js +64 -0
- package/dist/native/errors.js.map +1 -0
- package/dist/native/index.d.ts +27 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +55 -0
- package/dist/native/index.js.map +1 -0
- package/dist/native/isolation.d.ts +14 -0
- package/dist/native/isolation.d.ts.map +1 -0
- package/dist/native/isolation.js +37 -0
- package/dist/native/isolation.js.map +1 -0
- package/dist/native/query-builder.d.ts +303 -0
- package/dist/native/query-builder.d.ts.map +1 -0
- package/dist/native/query-builder.js +984 -0
- package/dist/native/query-builder.js.map +1 -0
- package/dist/native/replica-picker.d.ts +22 -0
- package/dist/native/replica-picker.d.ts.map +1 -0
- package/dist/native/replica-picker.js +65 -0
- package/dist/native/replica-picker.js.map +1 -0
- package/dist/native/schema/alter-blueprint.d.ts +37 -0
- package/dist/native/schema/alter-blueprint.d.ts.map +1 -0
- package/dist/native/schema/alter-blueprint.js +56 -0
- package/dist/native/schema/alter-blueprint.js.map +1 -0
- package/dist/native/schema/blueprint.d.ts +151 -0
- package/dist/native/schema/blueprint.d.ts.map +1 -0
- package/dist/native/schema/blueprint.js +286 -0
- package/dist/native/schema/blueprint.js.map +1 -0
- package/dist/native/schema/column.d.ts +168 -0
- package/dist/native/schema/column.d.ts.map +1 -0
- package/dist/native/schema/column.js +190 -0
- package/dist/native/schema/column.js.map +1 -0
- package/dist/native/schema/ddl-compiler.d.ts +34 -0
- package/dist/native/schema/ddl-compiler.d.ts.map +1 -0
- package/dist/native/schema/ddl-compiler.js +352 -0
- package/dist/native/schema/ddl-compiler.js.map +1 -0
- package/dist/native/schema/inspect.d.ts +67 -0
- package/dist/native/schema/inspect.d.ts.map +1 -0
- package/dist/native/schema/inspect.js +312 -0
- package/dist/native/schema/inspect.js.map +1 -0
- package/dist/native/schema/introspect.d.ts +34 -0
- package/dist/native/schema/introspect.d.ts.map +1 -0
- package/dist/native/schema/introspect.js +101 -0
- package/dist/native/schema/introspect.js.map +1 -0
- package/dist/native/schema/migration.d.ts +8 -0
- package/dist/native/schema/migration.d.ts.map +1 -0
- package/dist/native/schema/migration.js +19 -0
- package/dist/native/schema/migration.js.map +1 -0
- package/dist/native/schema/migrator.d.ts +144 -0
- package/dist/native/schema/migrator.d.ts.map +1 -0
- package/dist/native/schema/migrator.js +239 -0
- package/dist/native/schema/migrator.js.map +1 -0
- package/dist/native/schema/rebuild.d.ts +11 -0
- package/dist/native/schema/rebuild.d.ts.map +1 -0
- package/dist/native/schema/rebuild.js +92 -0
- package/dist/native/schema/rebuild.js.map +1 -0
- package/dist/native/schema/schema-builder.d.ts +46 -0
- package/dist/native/schema/schema-builder.d.ts.map +1 -0
- package/dist/native/schema/schema-builder.js +153 -0
- package/dist/native/schema/schema-builder.js.map +1 -0
- package/dist/native/schema/schema-facade.d.ts +63 -0
- package/dist/native/schema/schema-facade.d.ts.map +1 -0
- package/dist/native/schema/schema-facade.js +124 -0
- package/dist/native/schema/schema-facade.js.map +1 -0
- package/dist/native/schema/schema-types.d.ts +27 -0
- package/dist/native/schema/schema-types.d.ts.map +1 -0
- package/dist/native/schema/schema-types.js +52 -0
- package/dist/native/schema/schema-types.js.map +1 -0
- package/dist/native/schema/types-generator.d.ts +73 -0
- package/dist/native/schema/types-generator.d.ts.map +1 -0
- package/dist/native/schema/types-generator.js +181 -0
- package/dist/native/schema/types-generator.js.map +1 -0
- package/dist/registry-bridge.d.ts +24 -4
- package/dist/registry-bridge.d.ts.map +1 -1
- package/dist/registry-bridge.js +20 -0
- package/dist/registry-bridge.js.map +1 -1
- package/dist/sticky.d.ts +22 -0
- package/dist/sticky.d.ts.map +1 -0
- package/dist/sticky.js +61 -0
- package/dist/sticky.js.map +1 -0
- package/package.json +32 -2
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// ─── SchemaBuilder (executor-bound) ────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Node-capable: the runtime entry point for the schema builder. Holds an
|
|
4
|
+
// {@link Executor} (a connection or a transaction scope) plus a {@link Dialect},
|
|
5
|
+
// compiles a {@link Blueprint} via the pure DDL compiler, and runs the resulting
|
|
6
|
+
// statements. This is what a migration's `up()` / `down()` drives — directly in
|
|
7
|
+
// 7.1, and behind the static `Schema` facade once the migration runner (7.2)
|
|
8
|
+
// binds a builder to the active connection.
|
|
9
|
+
//
|
|
10
|
+
// `hasTable` / `hasColumn` introspect the live database — SQLite via the
|
|
11
|
+
// PRAGMA/`sqlite_master` catalog, Postgres + MySQL via `information_schema`
|
|
12
|
+
// (scoped to `current_schema()` / `DATABASE()` respectively). They branch on
|
|
13
|
+
// `dialect.name`.
|
|
14
|
+
import { NativeNotImplementedError } from '../errors.js';
|
|
15
|
+
import { Blueprint } from './blueprint.js';
|
|
16
|
+
import { AlterBlueprint } from './alter-blueprint.js';
|
|
17
|
+
import { compileCreateTable, compileDropTable, compileAlterTable, compileRenameTable } from './ddl-compiler.js';
|
|
18
|
+
import { rebuildTable } from './rebuild.js';
|
|
19
|
+
export class SchemaBuilder {
|
|
20
|
+
executor;
|
|
21
|
+
dialect;
|
|
22
|
+
constructor(executor, dialect) {
|
|
23
|
+
this.executor = executor;
|
|
24
|
+
this.dialect = dialect;
|
|
25
|
+
}
|
|
26
|
+
/** `CREATE TABLE` (+ any indexes) from a `Blueprint` callback. */
|
|
27
|
+
async create(table, build) {
|
|
28
|
+
const blueprint = new Blueprint(table);
|
|
29
|
+
build(blueprint);
|
|
30
|
+
for (const stmt of compileCreateTable(blueprint, this.dialect)) {
|
|
31
|
+
await this.executor.execute(stmt.sql, stmt.bindings);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** `ALTER TABLE` — add/drop/rename/change columns + add/drop indexes and
|
|
35
|
+
* foreign keys via a callback. A `.change()` (column type/constraint
|
|
36
|
+
* change) is a native `ALTER COLUMN` / `MODIFY` on pg/mysql (7.4b); SQLite
|
|
37
|
+
* can't change a column in place, so it routes through the table-rebuild
|
|
38
|
+
* dance instead (which requires `change()` to be the only op in the call). */
|
|
39
|
+
async table(table, build) {
|
|
40
|
+
const blueprint = new AlterBlueprint(table);
|
|
41
|
+
build(blueprint);
|
|
42
|
+
if (this.dialect.name === 'sqlite' && blueprint.columns.some(c => c.change)) {
|
|
43
|
+
await rebuildTable(this.executor, this.dialect, blueprint);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for (const stmt of compileAlterTable(blueprint, this.dialect)) {
|
|
47
|
+
await this.executor.execute(stmt.sql, stmt.bindings);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Rename a table. */
|
|
51
|
+
async rename(from, to) {
|
|
52
|
+
const stmt = compileRenameTable(from, to, this.dialect);
|
|
53
|
+
await this.executor.execute(stmt.sql, stmt.bindings);
|
|
54
|
+
}
|
|
55
|
+
/** `DROP TABLE`. */
|
|
56
|
+
async drop(table) {
|
|
57
|
+
const stmt = compileDropTable(table, {}, this.dialect);
|
|
58
|
+
await this.executor.execute(stmt.sql, stmt.bindings);
|
|
59
|
+
}
|
|
60
|
+
/** `DROP TABLE IF EXISTS`. */
|
|
61
|
+
async dropIfExists(table) {
|
|
62
|
+
const stmt = compileDropTable(table, { ifExists: true }, this.dialect);
|
|
63
|
+
await this.executor.execute(stmt.sql, stmt.bindings);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Names of every user table (catalog lookup) — includes the `migrations`
|
|
67
|
+
* state table, excludes the engine's internal tables (`sqlite_*`).
|
|
68
|
+
* `information_schema` on pg/mysql, `sqlite_master` on sqlite.
|
|
69
|
+
*/
|
|
70
|
+
async allTables() {
|
|
71
|
+
const schemaFn = this.currentSchemaSql();
|
|
72
|
+
if (schemaFn) {
|
|
73
|
+
const rows = await this.executor.execute(`SELECT table_name AS name FROM information_schema.tables WHERE table_schema = ${schemaFn} AND table_type = 'BASE TABLE' ORDER BY table_name`, []);
|
|
74
|
+
// mysql2 may surface the alias upper-cased depending on server config.
|
|
75
|
+
return rows.map(r => String(r['name'] ?? r['NAME'] ?? r['TABLE_NAME']));
|
|
76
|
+
}
|
|
77
|
+
this.requireSqlite('allTables');
|
|
78
|
+
const rows = await this.executor.execute(`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name`, []);
|
|
79
|
+
return rows.map(r => String(r['name']));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Drop every user table — the `migrate:fresh` sweep (Laravel's
|
|
83
|
+
* `Schema::dropAllTables()` analog). Dialect-aware so foreign keys can't
|
|
84
|
+
* block it: pg drops with `CASCADE`, mysql disables `FOREIGN_KEY_CHECKS`
|
|
85
|
+
* for the batch (restored after, even on failure), sqlite drops in catalog
|
|
86
|
+
* order exactly as before.
|
|
87
|
+
*/
|
|
88
|
+
async dropAllTables() {
|
|
89
|
+
const tables = await this.allTables();
|
|
90
|
+
if (tables.length === 0)
|
|
91
|
+
return;
|
|
92
|
+
if (this.dialect.name === 'pg') {
|
|
93
|
+
for (const t of tables) {
|
|
94
|
+
// A table dropped by an earlier CASCADE is covered by IF EXISTS.
|
|
95
|
+
await this.executor.execute(`DROP TABLE IF EXISTS ${this.dialect.quoteId(t)} CASCADE`, []);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (this.dialect.name === 'mysql') {
|
|
100
|
+
await this.executor.execute('SET FOREIGN_KEY_CHECKS = 0', []);
|
|
101
|
+
try {
|
|
102
|
+
for (const t of tables) {
|
|
103
|
+
await this.executor.execute(`DROP TABLE IF EXISTS ${this.dialect.quoteId(t)}`, []);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await this.executor.execute('SET FOREIGN_KEY_CHECKS = 1', []);
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
for (const t of tables) {
|
|
112
|
+
await this.drop(t);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Whether `table` exists (catalog lookup). */
|
|
116
|
+
async hasTable(table) {
|
|
117
|
+
const schemaFn = this.currentSchemaSql();
|
|
118
|
+
if (schemaFn) {
|
|
119
|
+
const rows = await this.executor.execute(`SELECT 1 FROM information_schema.tables WHERE table_schema = ${schemaFn} AND table_name = ${this.dialect.placeholder(0)}`, [table]);
|
|
120
|
+
return rows.length > 0;
|
|
121
|
+
}
|
|
122
|
+
this.requireSqlite('hasTable');
|
|
123
|
+
const rows = await this.executor.execute(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`, [table]);
|
|
124
|
+
return rows.length > 0;
|
|
125
|
+
}
|
|
126
|
+
/** Whether `table` has a `column` (catalog lookup). */
|
|
127
|
+
async hasColumn(table, column) {
|
|
128
|
+
const schemaFn = this.currentSchemaSql();
|
|
129
|
+
if (schemaFn) {
|
|
130
|
+
const rows = await this.executor.execute(`SELECT 1 FROM information_schema.columns WHERE table_schema = ${schemaFn} AND table_name = ${this.dialect.placeholder(0)} AND column_name = ${this.dialect.placeholder(1)}`, [table, column]);
|
|
131
|
+
return rows.length > 0;
|
|
132
|
+
}
|
|
133
|
+
this.requireSqlite('hasColumn');
|
|
134
|
+
// PRAGMA takes an identifier, not a bound value — quote+validate the name.
|
|
135
|
+
const rows = await this.executor.execute(`PRAGMA table_info(${this.dialect.quoteId(table)})`, []);
|
|
136
|
+
return rows.some(r => r['name'] === column);
|
|
137
|
+
}
|
|
138
|
+
/** The SQL expression naming the active schema/database for dialects that
|
|
139
|
+
* introspect via `information_schema`, or `null` for the sqlite path. */
|
|
140
|
+
currentSchemaSql() {
|
|
141
|
+
if (this.dialect.name === 'pg')
|
|
142
|
+
return 'current_schema()';
|
|
143
|
+
if (this.dialect.name === 'mysql')
|
|
144
|
+
return 'DATABASE()';
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
requireSqlite(method) {
|
|
148
|
+
if (this.dialect.name !== 'sqlite') {
|
|
149
|
+
throw new NativeNotImplementedError(`SchemaBuilder.${method} on the "${this.dialect.name}" dialect`, 'a later phase');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=schema-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-builder.js","sourceRoot":"","sources":["../../../src/native/schema/schema-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Blueprint } from './blueprint.js';
|
|
2
|
+
import type { AlterBlueprint } from './alter-blueprint.js';
|
|
3
|
+
import { SchemaBuilder } from './schema-builder.js';
|
|
4
|
+
/**
|
|
5
|
+
* The Laravel-style `Schema` facade. Static methods delegate to the bound
|
|
6
|
+
* {@link SchemaBuilder}. The {@link Migrator} owns binding via {@link Schema.use}
|
|
7
|
+
* / {@link Schema.reset}; migration authors only call the table operations.
|
|
8
|
+
*/
|
|
9
|
+
export declare const Schema: {
|
|
10
|
+
/** Bind the connection-scoped builder. Called by the migrator. `pretend`
|
|
11
|
+
* marks a recording (dry-run) bind — see {@link Schema.connection}. */
|
|
12
|
+
readonly use: (builder: SchemaBuilder, opts?: {
|
|
13
|
+
pretend?: boolean;
|
|
14
|
+
}) => void;
|
|
15
|
+
/** Unbind. Called by the migrator after each migration. */
|
|
16
|
+
readonly reset: () => void;
|
|
17
|
+
/**
|
|
18
|
+
* The same table operations, scoped to a NAMED connection from
|
|
19
|
+
* `config/database.ts` (Laravel `Schema::connection('reporting')->create(…)`):
|
|
20
|
+
*
|
|
21
|
+
* await Schema.connection('reporting').create('events', (t) => { … })
|
|
22
|
+
*
|
|
23
|
+
* Resolves through the same named-connection seam as `DB.connection()` —
|
|
24
|
+
* lazy (the connection opens at the first operation) and available wherever
|
|
25
|
+
* the app has booted, not only inside a migration run. The connection must
|
|
26
|
+
* use the native engine (`engine: 'native'`); prisma/drizzle connections
|
|
27
|
+
* throw a clear error.
|
|
28
|
+
*
|
|
29
|
+
* Two boundaries:
|
|
30
|
+
* - Inside a migration, DDL on another connection runs OUTSIDE the
|
|
31
|
+
* migrator's batch transaction (a transaction can't span connections) —
|
|
32
|
+
* a failed batch rolls back the bound connection only.
|
|
33
|
+
* - Throws under `migrate --pretend`: the dry-run records the bound
|
|
34
|
+
* connection's SQL without executing, and there is no recording seam for
|
|
35
|
+
* a second connection — executing its DDL for real would betray the dry run.
|
|
36
|
+
*/
|
|
37
|
+
readonly connection: (name: string) => ConnectionSchemaOps;
|
|
38
|
+
readonly create: (table: string, build: (table: Blueprint) => void) => Promise<void>;
|
|
39
|
+
readonly table: (table: string, build: (table: AlterBlueprint) => void) => Promise<void>;
|
|
40
|
+
readonly rename: (from: string, to: string) => Promise<void>;
|
|
41
|
+
readonly drop: (table: string) => Promise<void>;
|
|
42
|
+
readonly dropIfExists: (table: string) => Promise<void>;
|
|
43
|
+
readonly hasTable: (table: string) => Promise<boolean>;
|
|
44
|
+
readonly hasColumn: (table: string, column: string) => Promise<boolean>;
|
|
45
|
+
};
|
|
46
|
+
/** The named-connection table operations returned by {@link Schema.connection}
|
|
47
|
+
* — the facade's surface minus the migrator-owned bind/unbind. */
|
|
48
|
+
export interface ConnectionSchemaOps {
|
|
49
|
+
create(table: string, build: (table: Blueprint) => void): Promise<void>;
|
|
50
|
+
table(table: string, build: (table: AlterBlueprint) => void): Promise<void>;
|
|
51
|
+
rename(from: string, to: string): Promise<void>;
|
|
52
|
+
drop(table: string): Promise<void>;
|
|
53
|
+
dropIfExists(table: string): Promise<void>;
|
|
54
|
+
hasTable(table: string): Promise<boolean>;
|
|
55
|
+
hasColumn(table: string, column: string): Promise<boolean>;
|
|
56
|
+
}
|
|
57
|
+
/** Run `fn` with `builder` bound to {@link Schema}, unbinding afterwards even on
|
|
58
|
+
* throw. The migrator wraps each migration's up()/down() in this — `pretend`
|
|
59
|
+
* marks the recording (dry-run) bind so `Schema.connection()` can refuse. */
|
|
60
|
+
export declare function withSchema<T>(builder: SchemaBuilder, fn: () => Promise<T> | T, opts?: {
|
|
61
|
+
pretend?: boolean;
|
|
62
|
+
}): Promise<T>;
|
|
63
|
+
//# sourceMappingURL=schema-facade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-facade.d.ts","sourceRoot":"","sources":["../../../src/native/schema/schema-facade.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAqBnD;;;;GAIG;AACH,eAAO,MAAM,MAAM;IACjB;4EACwE;4BAC3D,aAAa,SAAQ;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAQ,IAAI;IAInE,2DAA2D;0BAClD,IAAI;IAKb;;;;;;;;;;;;;;;;;;;OAmBG;gCACc,MAAM,KAAG,mBAAmB;6BAiCzB,MAAM,SAAS,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;4BAG1D,MAAM,SAAS,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,KAAG,OAAO,CAAC,IAAI,CAAC;4BAG9D,MAAM,MAAM,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;2BAGnC,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;mCAGd,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;+BAG1B,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;gCAGxB,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;CAGxD,CAAA;AAEV;mEACmE;AACnE,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACzC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC3D;AAED;;8EAE8E;AAC9E,wBAAsB,UAAU,CAAC,CAAC,EAChC,OAAO,EAAE,aAAa,EACtB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EACxB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/B,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// ─── Schema facade (Laravel `Schema::`) ────────────────────
|
|
2
|
+
//
|
|
3
|
+
// The static entry point migration files call: `Schema.create(...)`,
|
|
4
|
+
// `Schema.dropIfExists(...)`, etc. It delegates to a {@link SchemaBuilder} the
|
|
5
|
+
// {@link Migrator} binds (`Schema.use(builder)`) for the duration of each
|
|
6
|
+
// migration's `up()` / `down()`, then unbinds (`Schema.reset()`). This mirrors
|
|
7
|
+
// Laravel's facade resolving the schema builder from the active connection.
|
|
8
|
+
//
|
|
9
|
+
// Migrations run serially in a single CLI process, so a module-level "current
|
|
10
|
+
// builder" is sufficient — there's no concurrency to guard. Calling a `Schema`
|
|
11
|
+
// method with nothing bound (e.g. importing `Schema` and using it outside a
|
|
12
|
+
// migration run) throws a clear error rather than a null-deref.
|
|
13
|
+
import { NativeOrmError } from '../errors.js';
|
|
14
|
+
import { resolveConnectionResolver } from '../../registry-bridge.js';
|
|
15
|
+
let current = null;
|
|
16
|
+
// `--pretend` runs bind a RECORDING builder — a `Schema.connection()` call in
|
|
17
|
+
// that window would execute REAL DDL on the named connection while the bound
|
|
18
|
+
// connection only records. Tracked here so connection() can refuse instead.
|
|
19
|
+
let pretending = false;
|
|
20
|
+
function active() {
|
|
21
|
+
if (!current) {
|
|
22
|
+
throw new NativeOrmError('NATIVE_SCHEMA_UNBOUND', `[RudderJS ORM native] Schema is not bound to a connection. ` +
|
|
23
|
+
`Schema methods may only be called from a migration's up()/down() run by the migrator.`);
|
|
24
|
+
}
|
|
25
|
+
return current;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The Laravel-style `Schema` facade. Static methods delegate to the bound
|
|
29
|
+
* {@link SchemaBuilder}. The {@link Migrator} owns binding via {@link Schema.use}
|
|
30
|
+
* / {@link Schema.reset}; migration authors only call the table operations.
|
|
31
|
+
*/
|
|
32
|
+
export const Schema = {
|
|
33
|
+
/** Bind the connection-scoped builder. Called by the migrator. `pretend`
|
|
34
|
+
* marks a recording (dry-run) bind — see {@link Schema.connection}. */
|
|
35
|
+
use(builder, opts = {}) {
|
|
36
|
+
current = builder;
|
|
37
|
+
pretending = opts.pretend === true;
|
|
38
|
+
},
|
|
39
|
+
/** Unbind. Called by the migrator after each migration. */
|
|
40
|
+
reset() {
|
|
41
|
+
current = null;
|
|
42
|
+
pretending = false;
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* The same table operations, scoped to a NAMED connection from
|
|
46
|
+
* `config/database.ts` (Laravel `Schema::connection('reporting')->create(…)`):
|
|
47
|
+
*
|
|
48
|
+
* await Schema.connection('reporting').create('events', (t) => { … })
|
|
49
|
+
*
|
|
50
|
+
* Resolves through the same named-connection seam as `DB.connection()` —
|
|
51
|
+
* lazy (the connection opens at the first operation) and available wherever
|
|
52
|
+
* the app has booted, not only inside a migration run. The connection must
|
|
53
|
+
* use the native engine (`engine: 'native'`); prisma/drizzle connections
|
|
54
|
+
* throw a clear error.
|
|
55
|
+
*
|
|
56
|
+
* Two boundaries:
|
|
57
|
+
* - Inside a migration, DDL on another connection runs OUTSIDE the
|
|
58
|
+
* migrator's batch transaction (a transaction can't span connections) —
|
|
59
|
+
* a failed batch rolls back the bound connection only.
|
|
60
|
+
* - Throws under `migrate --pretend`: the dry-run records the bound
|
|
61
|
+
* connection's SQL without executing, and there is no recording seam for
|
|
62
|
+
* a second connection — executing its DDL for real would betray the dry run.
|
|
63
|
+
*/
|
|
64
|
+
connection(name) {
|
|
65
|
+
if (pretending) {
|
|
66
|
+
throw new NativeOrmError('NATIVE_SCHEMA_PRETEND_CONNECTION', `[RudderJS ORM native] Schema.connection("${name}") is not supported under --pretend — ` +
|
|
67
|
+
`the dry run records the bound connection only; DDL on a second connection would execute for real.`);
|
|
68
|
+
}
|
|
69
|
+
const builder = async () => {
|
|
70
|
+
const adapter = await resolveConnectionResolver()(name);
|
|
71
|
+
if (typeof adapter.schemaBuilder !== 'function') {
|
|
72
|
+
throw new NativeOrmError('NATIVE_SCHEMA_CONNECTION_ENGINE', `[RudderJS ORM native] Connection "${name}" does not use the native engine — ` +
|
|
73
|
+
`Schema.connection() needs an \`engine: 'native'\` connection (prisma/drizzle manage schema with their own tooling).`);
|
|
74
|
+
}
|
|
75
|
+
return adapter.schemaBuilder();
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
async create(table, build) { return (await builder()).create(table, build); },
|
|
79
|
+
async table(table, build) { return (await builder()).table(table, build); },
|
|
80
|
+
async rename(from, to) { return (await builder()).rename(from, to); },
|
|
81
|
+
async drop(table) { return (await builder()).drop(table); },
|
|
82
|
+
async dropIfExists(table) { return (await builder()).dropIfExists(table); },
|
|
83
|
+
async hasTable(table) { return (await builder()).hasTable(table); },
|
|
84
|
+
async hasColumn(table, column) { return (await builder()).hasColumn(table, column); },
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
// These are `async` (not just Promise-returning) so an unbound call — where
|
|
88
|
+
// `active()` throws — surfaces as a rejected promise, not a synchronous throw.
|
|
89
|
+
// Callers `await` them, so a uniform rejection is the least surprising contract.
|
|
90
|
+
async create(table, build) {
|
|
91
|
+
return active().create(table, build);
|
|
92
|
+
},
|
|
93
|
+
async table(table, build) {
|
|
94
|
+
return active().table(table, build);
|
|
95
|
+
},
|
|
96
|
+
async rename(from, to) {
|
|
97
|
+
return active().rename(from, to);
|
|
98
|
+
},
|
|
99
|
+
async drop(table) {
|
|
100
|
+
return active().drop(table);
|
|
101
|
+
},
|
|
102
|
+
async dropIfExists(table) {
|
|
103
|
+
return active().dropIfExists(table);
|
|
104
|
+
},
|
|
105
|
+
async hasTable(table) {
|
|
106
|
+
return active().hasTable(table);
|
|
107
|
+
},
|
|
108
|
+
async hasColumn(table, column) {
|
|
109
|
+
return active().hasColumn(table, column);
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
/** Run `fn` with `builder` bound to {@link Schema}, unbinding afterwards even on
|
|
113
|
+
* throw. The migrator wraps each migration's up()/down() in this — `pretend`
|
|
114
|
+
* marks the recording (dry-run) bind so `Schema.connection()` can refuse. */
|
|
115
|
+
export async function withSchema(builder, fn, opts = {}) {
|
|
116
|
+
Schema.use(builder, opts);
|
|
117
|
+
try {
|
|
118
|
+
return await fn();
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
Schema.reset();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=schema-facade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-facade.js","sourceRoot":"","sources":["../../../src/native/schema/schema-facade.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Executor } from '../driver.js';
|
|
2
|
+
import type { Dialect } from '../dialect.js';
|
|
3
|
+
import { type TableTypes } from './types-generator.js';
|
|
4
|
+
/** A model's contribution to type resolution: its table name + declared casts. */
|
|
5
|
+
export interface ModelCastInfo {
|
|
6
|
+
table: string;
|
|
7
|
+
casts: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Introspect every user table and build the {@link TableTypes} for each, folding
|
|
11
|
+
* in `casts` for any table a model declares them on (matched by table name).
|
|
12
|
+
* Pure-ish: only reads the DB, returns data — the file write is separate so this
|
|
13
|
+
* is unit-testable against an in-memory connection.
|
|
14
|
+
*/
|
|
15
|
+
export declare function collectSchemaTypes(executor: Executor, dialect: Dialect, models?: ModelCastInfo[]): Promise<TableTypes[]>;
|
|
16
|
+
/** Default output path for the generated registry, relative to an app root. */
|
|
17
|
+
export declare function registryDtsPath(cwd: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Full `schema:types` run: introspect → emit → write
|
|
20
|
+
* `app/Models/__schema/registry.d.ts`. Returns the written path + table count
|
|
21
|
+
* for the CLI to report. Creating the dir is idempotent.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateSchemaTypes(executor: Executor, dialect: Dialect, cwd: string, models?: ModelCastInfo[]): Promise<{
|
|
24
|
+
path: string;
|
|
25
|
+
tableCount: number;
|
|
26
|
+
}>;
|
|
27
|
+
//# sourceMappingURL=schema-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-types.d.ts","sourceRoot":"","sources":["../../../src/native/schema/schema-types.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,OAAO,EAA+E,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEnI,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9B;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,MAAM,GAAE,aAAa,EAAO,GAC3B,OAAO,CAAC,UAAU,EAAE,CAAC,CAgBvB;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,aAAa,EAAO,GAC3B,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAO/C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// ─── schema:types orchestration (GATE 7-types, node-only) ──
|
|
2
|
+
//
|
|
3
|
+
// Node-only glue: introspect every user table on a connection, fold in each
|
|
4
|
+
// model's declared `casts` (so a `boolean` cast surfaces as `boolean`, not the
|
|
5
|
+
// raw stored `number`), and write `app/Models/__schema/registry.d.ts`. The pure
|
|
6
|
+
// column→TS mapping lives in `types-generator.ts`; the live catalog reads in
|
|
7
|
+
// `introspect.ts`. Run automatically after `migrate` / `migrate:fresh`, and on
|
|
8
|
+
// demand via `rudder schema:types`.
|
|
9
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { readTables, readColumns } from './introspect.js';
|
|
12
|
+
import { buildTableTypes, emitRegistryDts, sqliteTypeToTs, pgTypeToTs, mysqlTypeToTs } from './types-generator.js';
|
|
13
|
+
/**
|
|
14
|
+
* Introspect every user table and build the {@link TableTypes} for each, folding
|
|
15
|
+
* in `casts` for any table a model declares them on (matched by table name).
|
|
16
|
+
* Pure-ish: only reads the DB, returns data — the file write is separate so this
|
|
17
|
+
* is unit-testable against an in-memory connection.
|
|
18
|
+
*/
|
|
19
|
+
export async function collectSchemaTypes(executor, dialect, models = []) {
|
|
20
|
+
const castsByTable = new Map(models.map((m) => [m.table, m.casts]));
|
|
21
|
+
// Per-dialect storage→TS mapper: Postgres / MySQL data types differ from
|
|
22
|
+
// SQLite's fuzzy affinities (e.g. `jsonb`, `timestamptz`, `numeric`/`decimal`
|
|
23
|
+
// as string, `tinyint`→number).
|
|
24
|
+
const typeToTs = dialect.name === 'pg' ? pgTypeToTs :
|
|
25
|
+
dialect.name === 'mysql' ? mysqlTypeToTs :
|
|
26
|
+
sqliteTypeToTs;
|
|
27
|
+
const tables = await readTables(executor, dialect);
|
|
28
|
+
const out = [];
|
|
29
|
+
for (const table of tables) {
|
|
30
|
+
const columns = await readColumns(executor, dialect, table);
|
|
31
|
+
out.push(buildTableTypes(table, columns, castsByTable.get(table) ?? {}, typeToTs));
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
/** Default output path for the generated registry, relative to an app root. */
|
|
36
|
+
export function registryDtsPath(cwd) {
|
|
37
|
+
return join(cwd, 'app', 'Models', '__schema', 'registry.d.ts');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Full `schema:types` run: introspect → emit → write
|
|
41
|
+
* `app/Models/__schema/registry.d.ts`. Returns the written path + table count
|
|
42
|
+
* for the CLI to report. Creating the dir is idempotent.
|
|
43
|
+
*/
|
|
44
|
+
export async function generateSchemaTypes(executor, dialect, cwd, models = []) {
|
|
45
|
+
const tables = await collectSchemaTypes(executor, dialect, models);
|
|
46
|
+
const contents = emitRegistryDts(tables);
|
|
47
|
+
const path = registryDtsPath(cwd);
|
|
48
|
+
await mkdir(dirname(path), { recursive: true });
|
|
49
|
+
await writeFile(path, contents, 'utf8');
|
|
50
|
+
return { path, tableCount: tables.length };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=schema-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-types.js","sourceRoot":"","sources":["../../../src/native/schema/schema-types.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,gFAAgF;AAChF,6EAA6E;AAC7E,+EAA+E;AAC/E,oCAAoC;AAEpC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGzC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAmB,MAAM,sBAAsB,CAAA;AAQnI;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,QAAkB,EAClB,OAAgB,EAChB,SAA0B,EAAE;IAE5B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACnE,yEAAyE;IACzE,8EAA8E;IAC9E,gCAAgC;IAChC,MAAM,QAAQ,GACZ,OAAO,CAAC,IAAI,KAAK,IAAI,CAAI,CAAC,CAAC,UAAU,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;YAC1C,cAAc,CAAA;IAChB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAClD,MAAM,GAAG,GAAiB,EAAE,CAAA;IAC5B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;QAC3D,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAA;IACpF,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAA;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,QAAkB,EAClB,OAAgB,EAChB,GAAW,EACX,SAA0B,EAAE;IAE5B,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IAClE,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAA;IACxC,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;IACjC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/C,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IACvC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;AAC5C,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { RawColumn } from './introspect.js';
|
|
2
|
+
import type { BuiltInCast } from '@rudderjs/contracts';
|
|
3
|
+
/** A column's resolved TypeScript type plus whether it's optional on read. */
|
|
4
|
+
export interface GeneratedColumnType {
|
|
5
|
+
/** The TS type expression, e.g. `string`, `number | null`, `Date`. */
|
|
6
|
+
ts: string;
|
|
7
|
+
/** Column name (carried through for the emitter). */
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
/** One table's generated shape: name → ordered column types. */
|
|
11
|
+
export interface TableTypes {
|
|
12
|
+
table: string;
|
|
13
|
+
columns: GeneratedColumnType[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Map a SQLite declared type (`PRAGMA table_info.type`) to a base TS type. The
|
|
17
|
+
* mapping is by affinity rules — SQLite types are fuzzy, so we match on
|
|
18
|
+
* substrings the same way SQLite's own affinity algorithm does (INT→integer,
|
|
19
|
+
* CHAR/CLOB/TEXT→text, BLOB→blob, REAL/FLOA/DOUB→real, else numeric).
|
|
20
|
+
*/
|
|
21
|
+
export declare function sqliteTypeToTs(declared: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Map a Postgres `information_schema.columns.data_type` to a base TS type. The
|
|
24
|
+
* mapping reflects what the porsager driver actually returns on READ (so the
|
|
25
|
+
* generated type matches runtime), which `casts` then refine:
|
|
26
|
+
* - `int8`/`bigint` → `number` (the pg driver parses OID 20 to a JS number);
|
|
27
|
+
* - `numeric`/`money` → `string` (porsager keeps these as strings for
|
|
28
|
+
* precision safety — a `float`/`decimal` cast refines them to `number`);
|
|
29
|
+
* - `json`/`jsonb` → `unknown` (a `json` cast or a typed `casts` entry refines);
|
|
30
|
+
* - `timestamp*`/`date` → `Date`; `bytea` → `Uint8Array`; the rest → `string`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function pgTypeToTs(declared: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Map a MySQL `information_schema.columns.data_type` to a base TS type. The
|
|
35
|
+
* `data_type` is the base type WITHOUT length/precision, which `casts` then
|
|
36
|
+
* refine:
|
|
37
|
+
* - integer family (`tinyint`/`smallint`/`mediumint`/`int`/`bigint`) → `number`.
|
|
38
|
+
* Note `tinyint(1)` (MySQL's BOOLEAN alias) surfaces as `tinyint` here → `number`;
|
|
39
|
+
* a declared `boolean` cast refines it to `boolean` (same treatment as pg);
|
|
40
|
+
* - `decimal`/`numeric` → `string` (precision safety, matching pg — a
|
|
41
|
+
* `float`/`decimal` cast refines to `number`);
|
|
42
|
+
* - `float`/`double`/`real` → `number`;
|
|
43
|
+
* - `json` → `unknown` (a `json` cast or typed `casts` entry refines);
|
|
44
|
+
* - `date`/`datetime`/`timestamp` → `Date`; `blob`/`binary` → `Uint8Array`;
|
|
45
|
+
* - char/text families → `string`; everything else → `unknown`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function mysqlTypeToTs(declared: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* The TS type a declared cast produces on READ (`toJSON`/hydration). Casts
|
|
50
|
+
* OVERRIDE the storage type — a `boolean` cast turns an INTEGER column into
|
|
51
|
+
* `boolean`, a `json` cast turns TEXT into `unknown`, etc. Returns null for
|
|
52
|
+
* casts that don't change the read type (or unknown cast names), so the caller
|
|
53
|
+
* falls back to the storage mapping.
|
|
54
|
+
*/
|
|
55
|
+
export declare function castToTs(cast: BuiltInCast | string): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve one column's TS type: a declared cast wins over the storage mapping,
|
|
58
|
+
* and a nullable column widens with `| null`. The primary key and NOT NULL
|
|
59
|
+
* columns stay non-null. `typeToTs` is the per-dialect storage mapper
|
|
60
|
+
* ({@link sqliteTypeToTs} by default; {@link pgTypeToTs} for Postgres).
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveColumnType(col: RawColumn, casts: Record<string, string>, typeToTs?: (declared: string) => string): GeneratedColumnType;
|
|
63
|
+
/** Build one table's {@link TableTypes} from its columns + casts, using the
|
|
64
|
+
* given per-dialect storage mapper (defaults to the SQLite mapping). */
|
|
65
|
+
export declare function buildTableTypes(table: string, columns: RawColumn[], casts?: Record<string, string>, typeToTs?: (declared: string) => string): TableTypes;
|
|
66
|
+
/**
|
|
67
|
+
* Emit the full `registry.d.ts` contents: a `declare module '@rudderjs/orm'`
|
|
68
|
+
* augmentation extending `SchemaRegistry` with one entry per table. Tables are
|
|
69
|
+
* sorted for deterministic output (stable diffs / git). An empty schema still
|
|
70
|
+
* emits a valid (empty) augmentation so a stale file is always overwritten.
|
|
71
|
+
*/
|
|
72
|
+
export declare function emitRegistryDts(tables: TableTypes[]): string;
|
|
73
|
+
//# sourceMappingURL=types-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-generator.d.ts","sourceRoot":"","sources":["../../../src/native/schema/types-generator.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,EAAE,EAAQ,MAAM,CAAA;IAChB,qDAAqD;IACrD,IAAI,EAAM,MAAM,CAAA;CACjB;AAED,gEAAgE;AAChE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAI,MAAM,CAAA;IACf,OAAO,EAAE,mBAAmB,EAAE,CAAA;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAsBnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAyBtD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBlE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,QAAQ,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAuB,GACtD,mBAAmB,CAMrB;AAED;yEACyE;AACzE,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,EAAE,EACpB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAClC,QAAQ,GAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAuB,GACtD,UAAU,CAEZ;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAiB5D"}
|