@rangka/core 0.1.1 → 0.1.3
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/package.json +6 -2
- package/.claude/skills/extend-core/SKILL.md +0 -133
- package/.turbo/turbo-build.log +0 -4
- package/CHANGELOG.md +0 -25
- package/CLAUDE.md +0 -180
- package/src/__tests__/coerce.test.ts +0 -154
- package/src/__tests__/context.test.ts +0 -111
- package/src/__tests__/helpers.ts +0 -21
- package/src/__tests__/index.test.ts +0 -7
- package/src/__tests__/widgets.test.ts +0 -197
- package/src/api/__tests__/handlers.test.ts +0 -389
- package/src/api/__tests__/include-resolver.test.ts +0 -393
- package/src/api/__tests__/middleware.test.ts +0 -100
- package/src/api/__tests__/openapi-schema.test.ts +0 -210
- package/src/api/__tests__/query-parser.test.ts +0 -291
- package/src/api/__tests__/route-generator.test.ts +0 -137
- package/src/api/__tests__/server.test.ts +0 -73
- package/src/api/__tests__/swagger.test.ts +0 -166
- package/src/api/handlers.ts +0 -274
- package/src/api/include-resolver.ts +0 -27
- package/src/api/index.ts +0 -4
- package/src/api/meta-handler.ts +0 -254
- package/src/api/openapi-schema.ts +0 -99
- package/src/api/query-parser.ts +0 -315
- package/src/api/route-generator.ts +0 -448
- package/src/api/server.ts +0 -147
- package/src/api/types.ts +0 -16
- package/src/audit/__tests__/audit.test.ts +0 -144
- package/src/audit/index.ts +0 -3
- package/src/audit/record.ts +0 -69
- package/src/audit/tables.ts +0 -48
- package/src/audit/types.ts +0 -26
- package/src/auth/__tests__/core-module.test.ts +0 -54
- package/src/auth/__tests__/debug.test.ts +0 -47
- package/src/auth/__tests__/field-permissions.test.ts +0 -245
- package/src/auth/__tests__/integration.test.ts +0 -208
- package/src/auth/__tests__/meta-boot.test.ts +0 -538
- package/src/auth/__tests__/model-permissions.test.ts +0 -205
- package/src/auth/__tests__/password.test.ts +0 -29
- package/src/auth/__tests__/permission-registry.test.ts +0 -313
- package/src/auth/__tests__/scope-hook.test.ts +0 -509
- package/src/auth/__tests__/scope-registry.test.ts +0 -297
- package/src/auth/__tests__/scopes.test.ts +0 -66
- package/src/auth/__tests__/session.test.ts +0 -214
- package/src/auth/core-models.ts +0 -52
- package/src/auth/core-module.ts +0 -59
- package/src/auth/debug.ts +0 -157
- package/src/auth/field-permissions.ts +0 -116
- package/src/auth/index.ts +0 -37
- package/src/auth/model-permissions.ts +0 -59
- package/src/auth/password.ts +0 -22
- package/src/auth/permission-registry.ts +0 -171
- package/src/auth/scope-filters.ts +0 -11
- package/src/auth/scope-registry.ts +0 -121
- package/src/auth/scopes.ts +0 -146
- package/src/auth/seed.ts +0 -44
- package/src/auth/session.ts +0 -178
- package/src/auth/types.ts +0 -50
- package/src/boot/__tests__/page-scanning.test.ts +0 -170
- package/src/boot/__tests__/page-utils.test.ts +0 -225
- package/src/boot/__tests__/project-scanner.test.ts +0 -88
- package/src/boot/dependency-sort.ts +0 -82
- package/src/boot/discovery.ts +0 -85
- package/src/boot/index.ts +0 -457
- package/src/boot/page-utils.ts +0 -110
- package/src/boot/project-scanner.ts +0 -397
- package/src/boot/schema-loader.ts +0 -26
- package/src/boot/schema-merger.ts +0 -125
- package/src/boot/traits.ts +0 -25
- package/src/boot/types.ts +0 -73
- package/src/context.ts +0 -105
- package/src/db/__tests__/cascade-delete.test.ts +0 -182
- package/src/db/__tests__/desired-state.test.ts +0 -136
- package/src/db/__tests__/diff-engine.test.ts +0 -635
- package/src/db/__tests__/field-mapper.test.ts +0 -355
- package/src/db/__tests__/introspect.test.ts +0 -70
- package/src/db/__tests__/search-filter.test.ts +0 -45
- package/src/db/__tests__/sequence.test.ts +0 -221
- package/src/db/auto-sync.ts +0 -133
- package/src/db/client.ts +0 -147
- package/src/db/desired-state.ts +0 -98
- package/src/db/diff-engine.ts +0 -305
- package/src/db/field-mapper.ts +0 -504
- package/src/db/filter-applier.ts +0 -89
- package/src/db/include-resolver.ts +0 -40
- package/src/db/index.ts +0 -23
- package/src/db/introspect.ts +0 -265
- package/src/db/model-include-resolver.ts +0 -327
- package/src/db/model-ops.ts +0 -281
- package/src/db/scope-enforcer.ts +0 -37
- package/src/db/types.ts +0 -98
- package/src/errors.ts +0 -41
- package/src/events/__tests__/bus.test.ts +0 -105
- package/src/events/bus.ts +0 -89
- package/src/events/index.ts +0 -2
- package/src/events/types.ts +0 -9
- package/src/external-model/__tests__/computed-fields.test.ts +0 -106
- package/src/external-model/__tests__/field-mapper.test.ts +0 -160
- package/src/external-model/__tests__/in-memory-ops.test.ts +0 -247
- package/src/external-model/__tests__/mutation-executor.test.ts +0 -160
- package/src/external-model/__tests__/query-executor.test.ts +0 -284
- package/src/external-model/__tests__/schema-converter.test.ts +0 -174
- package/src/external-model/computed-fields.ts +0 -15
- package/src/external-model/define.ts +0 -5
- package/src/external-model/external-model-ops.ts +0 -108
- package/src/external-model/field-mapper.ts +0 -66
- package/src/external-model/in-memory-ops.ts +0 -107
- package/src/external-model/index.ts +0 -7
- package/src/external-model/mutation-executor.ts +0 -71
- package/src/external-model/query-executor.ts +0 -100
- package/src/external-model/schema-converter.ts +0 -53
- package/src/external-model/types.ts +0 -32
- package/src/fixtures/__tests__/fixtures.test.ts +0 -203
- package/src/fixtures/index.ts +0 -10
- package/src/fixtures/loader.ts +0 -196
- package/src/fixtures/registry.ts +0 -125
- package/src/fixtures/types.ts +0 -33
- package/src/helpers/assert-ownership.ts +0 -19
- package/src/helpers/coerce.ts +0 -28
- package/src/helpers/stamping.ts +0 -28
- package/src/helpers/validation.ts +0 -14
- package/src/hooks/__tests__/context.test.ts +0 -73
- package/src/hooks/__tests__/executor.test.ts +0 -433
- package/src/hooks/__tests__/middleware.test.ts +0 -224
- package/src/hooks/__tests__/registry.test.ts +0 -50
- package/src/hooks/context.ts +0 -89
- package/src/hooks/errors.ts +0 -11
- package/src/hooks/executor.ts +0 -115
- package/src/hooks/index.ts +0 -10
- package/src/hooks/middleware.ts +0 -220
- package/src/hooks/registry.ts +0 -20
- package/src/hooks/types.ts +0 -32
- package/src/index.ts +0 -172
- package/src/jobs/__tests__/enqueue.test.ts +0 -77
- package/src/jobs/__tests__/integration.test.ts +0 -71
- package/src/jobs/__tests__/registry.test.ts +0 -103
- package/src/jobs/__tests__/scheduler.test.ts +0 -92
- package/src/jobs/__tests__/worker-execution.test.ts +0 -202
- package/src/jobs/__tests__/worker.test.ts +0 -119
- package/src/jobs/enqueue.ts +0 -93
- package/src/jobs/index.ts +0 -14
- package/src/jobs/registry.ts +0 -92
- package/src/jobs/scheduler.ts +0 -205
- package/src/jobs/tables.ts +0 -132
- package/src/jobs/types.ts +0 -62
- package/src/jobs/worker.ts +0 -272
- package/src/model-api/__tests__/cross-boundary-includes.test.ts +0 -366
- package/src/model-api/__tests__/extended-api.test.ts +0 -244
- package/src/model-api/__tests__/filter-applier.test.ts +0 -177
- package/src/model-api/__tests__/filter-translator.test.ts +0 -186
- package/src/model-api/__tests__/include-resolver.test.ts +0 -226
- package/src/model-api/__tests__/model-access.test.ts +0 -284
- package/src/model-api/__tests__/query-builder.test.ts +0 -224
- package/src/model-api/__tests__/scope-enforcer.test.ts +0 -268
- package/src/model-api/field-access.ts +0 -28
- package/src/model-api/filter-applier.ts +0 -1
- package/src/model-api/filter-translator.ts +0 -67
- package/src/model-api/include-resolver.ts +0 -2
- package/src/model-api/index.ts +0 -86
- package/src/model-api/query-builder.ts +0 -155
- package/src/model-api/scope-enforcer.ts +0 -3
- package/src/model-api/types.ts +0 -139
- package/src/plugins/__tests__/adapter-registry.test.ts +0 -92
- package/src/plugins/__tests__/lifecycle.test.ts +0 -96
- package/src/plugins/__tests__/loader.test.ts +0 -273
- package/src/plugins/__tests__/validator.test.ts +0 -275
- package/src/plugins/adapter-registry.ts +0 -42
- package/src/plugins/define.ts +0 -5
- package/src/plugins/index.ts +0 -28
- package/src/plugins/lifecycle.ts +0 -27
- package/src/plugins/loader.ts +0 -126
- package/src/plugins/types.ts +0 -76
- package/src/plugins/validator.ts +0 -141
- package/src/schema/__tests__/registry-models-by-module.test.ts +0 -58
- package/src/schema/registry.ts +0 -93
- package/src/schema/relationships.ts +0 -93
- package/src/schema/types.ts +0 -43
- package/src/services/__tests__/integration.test.ts +0 -63
- package/src/services/__tests__/registry.test.ts +0 -175
- package/src/services/index.ts +0 -13
- package/src/services/registry.ts +0 -156
- package/src/services/types.ts +0 -27
- package/src/validation/__tests__/field-validator.test.ts +0 -195
- package/src/validation/field-validator.ts +0 -113
- package/src/validation/index.ts +0 -1
- package/src/widgets/index.ts +0 -3
- package/src/widgets/slot-validator.ts +0 -87
- package/src/widgets/widget-registry.ts +0 -32
- package/tests/boot.test.ts +0 -323
- package/tests/dependency-sort.test.ts +0 -99
- package/tests/discovery.test.ts +0 -126
- package/tests/registry.test.ts +0 -216
- package/tests/schema-loader.test.ts +0 -52
- package/tests/schema-merger.test.ts +0 -180
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -14
package/src/db/auto-sync.ts
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import type { Kysely } from 'kysely';
|
|
2
|
-
import { sql } from 'kysely';
|
|
3
|
-
import type { SchemaRegistry } from '../schema/registry.js';
|
|
4
|
-
import type { DdlOperation } from './types.js';
|
|
5
|
-
import { SchemaToDesired } from './desired-state.js';
|
|
6
|
-
import { DiffEngine } from './diff-engine.js';
|
|
7
|
-
import { introspect } from './introspect.js';
|
|
8
|
-
|
|
9
|
-
export interface AutoSyncOptions {
|
|
10
|
-
allowDestructive?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface AutoSyncResult {
|
|
14
|
-
applied: DdlOperation[];
|
|
15
|
-
warnings: DdlOperation[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Compares the desired schema (from the registry) against the actual database state
|
|
20
|
-
* and applies any DDL operations needed to bring the database in sync.
|
|
21
|
-
*/
|
|
22
|
-
export async function autoSync(
|
|
23
|
-
registry: SchemaRegistry,
|
|
24
|
-
db: Kysely<unknown>,
|
|
25
|
-
options: AutoSyncOptions = {},
|
|
26
|
-
): Promise<AutoSyncResult> {
|
|
27
|
-
const desired = new SchemaToDesired().convert(registry);
|
|
28
|
-
const actual = await introspect(db);
|
|
29
|
-
const operations = new DiffEngine().diff(desired, actual);
|
|
30
|
-
|
|
31
|
-
if (operations.length === 0) {
|
|
32
|
-
console.log(`[rangka:sync] Schema is up to date`);
|
|
33
|
-
return { applied: [], warnings: [] };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const applied: DdlOperation[] = [];
|
|
37
|
-
const warnings: DdlOperation[] = [];
|
|
38
|
-
|
|
39
|
-
for (const op of operations) {
|
|
40
|
-
if (op.destructive) {
|
|
41
|
-
await handleDestructiveOp(op, options, applied, warnings, db);
|
|
42
|
-
} else {
|
|
43
|
-
await applyOperation(op, db);
|
|
44
|
-
applied.push(op);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (applied.length > 0) {
|
|
49
|
-
console.log(`[rangka:sync] Applied ${applied.length} operation(s):`);
|
|
50
|
-
for (const op of applied) {
|
|
51
|
-
console.log(` ${formatOperation(op)}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (warnings.length > 0) {
|
|
56
|
-
console.warn(`[rangka:sync] ${warnings.length} skipped (destructive):`);
|
|
57
|
-
for (const op of warnings) {
|
|
58
|
-
console.warn(` ${formatOperation(op)}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return { applied, warnings };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Apply a non-destructive DDL operation. */
|
|
66
|
-
async function applyOperation(op: DdlOperation, db: Kysely<unknown>): Promise<void> {
|
|
67
|
-
await sql.raw(op.sql).execute(db);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Handle a destructive operation: apply it if allowed, otherwise log a warning.
|
|
72
|
-
*/
|
|
73
|
-
async function handleDestructiveOp(
|
|
74
|
-
op: DdlOperation,
|
|
75
|
-
options: AutoSyncOptions,
|
|
76
|
-
applied: DdlOperation[],
|
|
77
|
-
warnings: DdlOperation[],
|
|
78
|
-
db: Kysely<unknown>,
|
|
79
|
-
): Promise<void> {
|
|
80
|
-
if (options.allowDestructive) {
|
|
81
|
-
const ddlSql = generateDestructiveSql(op);
|
|
82
|
-
await sql.raw(ddlSql).execute(db);
|
|
83
|
-
applied.push(op);
|
|
84
|
-
} else {
|
|
85
|
-
warnings.push(op);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function formatOperation(op: DdlOperation): string {
|
|
90
|
-
switch (op.type) {
|
|
91
|
-
case 'CREATE_TABLE':
|
|
92
|
-
return `+ table ${op.table}`;
|
|
93
|
-
case 'ADD_COLUMN':
|
|
94
|
-
return `+ column ${op.table}.${extractColumnFromSql(op.sql)}`;
|
|
95
|
-
case 'ALTER_COLUMN_TYPE':
|
|
96
|
-
return `~ column type ${op.table}.${extractColumnFromSql(op.sql)}`;
|
|
97
|
-
case 'CREATE_INDEX':
|
|
98
|
-
return `+ index on ${op.table}`;
|
|
99
|
-
case 'ADD_FOREIGN_KEY':
|
|
100
|
-
return `+ foreign key on ${op.table}`;
|
|
101
|
-
case 'ADD_CHECK_CONSTRAINT':
|
|
102
|
-
return `+ check constraint on ${op.table}`;
|
|
103
|
-
case 'DROP_TABLE':
|
|
104
|
-
return `- table ${op.table} (orphaned)`;
|
|
105
|
-
case 'DROP_COLUMN':
|
|
106
|
-
return `- column ${op.detail ?? op.table} (orphaned)`;
|
|
107
|
-
default:
|
|
108
|
-
return `${op.type} on ${op.table}`;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function extractColumnFromSql(s: string): string {
|
|
113
|
-
const match = s.match(/COLUMN "([^"]+)"/i) ?? s.match(/COLUMN\s+(\S+)/i);
|
|
114
|
-
return match ? match[1] : 'unknown';
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Generate the raw SQL for a destructive operation (DROP COLUMN / DROP TABLE). */
|
|
118
|
-
function generateDestructiveSql(op: DdlOperation): string {
|
|
119
|
-
switch (op.type) {
|
|
120
|
-
case 'DROP_COLUMN': {
|
|
121
|
-
const match = op.detail?.match(/Orphaned column: (.+)\.(.+)/);
|
|
122
|
-
if (match) return `ALTER TABLE "${match[1]}" DROP COLUMN "${match[2]}"`;
|
|
123
|
-
return op.sql;
|
|
124
|
-
}
|
|
125
|
-
case 'DROP_TABLE': {
|
|
126
|
-
const match = op.detail?.match(/Orphaned table: (.+)/);
|
|
127
|
-
if (match) return `DROP TABLE "${match[1]}"`;
|
|
128
|
-
return op.sql;
|
|
129
|
-
}
|
|
130
|
-
default:
|
|
131
|
-
return op.sql;
|
|
132
|
-
}
|
|
133
|
-
}
|
package/src/db/client.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { Kysely, PostgresDialect } from 'kysely';
|
|
2
|
-
import pg from 'pg';
|
|
3
|
-
import type { SchemaRegistry } from '../schema/registry.js';
|
|
4
|
-
import { modelToTableName } from './field-mapper.js';
|
|
5
|
-
|
|
6
|
-
export interface DatabaseClientConfig {
|
|
7
|
-
host: string;
|
|
8
|
-
port?: number;
|
|
9
|
-
database: string;
|
|
10
|
-
user: string;
|
|
11
|
-
password: string;
|
|
12
|
-
pool?: {
|
|
13
|
-
min?: number;
|
|
14
|
-
max?: number;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Thin wrapper around Kysely that resolves qualified model names
|
|
20
|
-
* (e.g. "sales.Invoice") to their underlying table names automatically.
|
|
21
|
-
*/
|
|
22
|
-
export class DatabaseClient {
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
-
private readonly db: Kysely<any>;
|
|
25
|
-
private readonly pool: pg.Pool;
|
|
26
|
-
private readonly qualifiedNameToTable: Map<string, string>;
|
|
27
|
-
|
|
28
|
-
constructor(config: DatabaseClientConfig, registry?: SchemaRegistry) {
|
|
29
|
-
this.pool = this.createPool(config);
|
|
30
|
-
this.db = new Kysely({ dialect: new PostgresDialect({ pool: this.pool }) });
|
|
31
|
-
this.qualifiedNameToTable = this.buildTableNameMap(registry);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Verify the database connection is reachable.
|
|
36
|
-
* Throws with actionable diagnostics if it cannot connect.
|
|
37
|
-
*/
|
|
38
|
-
async verifyConnection(): Promise<void> {
|
|
39
|
-
let client: pg.PoolClient | undefined;
|
|
40
|
-
try {
|
|
41
|
-
client = await this.pool.connect();
|
|
42
|
-
await client.query('SELECT 1');
|
|
43
|
-
} catch (err: unknown) {
|
|
44
|
-
const pgErr = err as { code?: string; message?: string };
|
|
45
|
-
const detail = formatConnectionError(pgErr, this.pool);
|
|
46
|
-
throw new Error(`Database connection failed: ${detail}`, { cause: err });
|
|
47
|
-
} finally {
|
|
48
|
-
client?.release();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// --- Query builders (resolve model name to table name automatically) ---
|
|
53
|
-
|
|
54
|
-
selectFrom(model: string) {
|
|
55
|
-
return this.db.selectFrom(this.resolveTable(model));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
insertInto(model: string) {
|
|
59
|
-
return this.db.insertInto(this.resolveTable(model));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
updateTable(model: string) {
|
|
63
|
-
return this.db.updateTable(this.resolveTable(model));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
deleteFrom(model: string) {
|
|
67
|
-
return this.db.deleteFrom(this.resolveTable(model));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Execute a callback within a database transaction. */
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
-
async transaction<T>(callback: (trx: Kysely<any>) => Promise<T>): Promise<T> {
|
|
73
|
-
return this.db.transaction().execute(callback);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Direct access to the underlying Kysely instance. */
|
|
77
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
-
get kysely(): Kysely<any> {
|
|
79
|
-
return this.db;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** Close the connection pool and release resources. */
|
|
83
|
-
async destroy(): Promise<void> {
|
|
84
|
-
await this.db.destroy();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// --- Private helpers ---
|
|
88
|
-
|
|
89
|
-
/** Maps a qualified model name to its table name, falling back to the raw input. */
|
|
90
|
-
private resolveTable(nameOrQualified: string): string {
|
|
91
|
-
return this.qualifiedNameToTable.get(nameOrQualified) ?? nameOrQualified;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Create the underlying pg connection pool. */
|
|
95
|
-
private createPool(config: DatabaseClientConfig): pg.Pool {
|
|
96
|
-
return new pg.Pool({
|
|
97
|
-
host: config.host,
|
|
98
|
-
port: config.port ?? 5432,
|
|
99
|
-
database: config.database,
|
|
100
|
-
user: config.user,
|
|
101
|
-
password: config.password,
|
|
102
|
-
min: config.pool?.min ?? 2,
|
|
103
|
-
max: config.pool?.max ?? 10,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Pre-compute the mapping from qualified model names to SQL table names. */
|
|
108
|
-
private buildTableNameMap(registry?: SchemaRegistry): Map<string, string> {
|
|
109
|
-
const map = new Map<string, string>();
|
|
110
|
-
if (registry) {
|
|
111
|
-
for (const model of registry.getAllModels()) {
|
|
112
|
-
map.set(model.qualifiedName, modelToTableName(model.qualifiedName));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return map;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function formatConnectionError(err: { code?: string; message?: string }, pool: pg.Pool): string {
|
|
120
|
-
const opts = (pool as unknown as { options: pg.PoolConfig }).options;
|
|
121
|
-
const host = opts.host ?? 'localhost';
|
|
122
|
-
const port = opts.port ?? 5432;
|
|
123
|
-
const database = opts.database ?? '(unknown)';
|
|
124
|
-
|
|
125
|
-
switch (err.code) {
|
|
126
|
-
case 'ECONNREFUSED':
|
|
127
|
-
return (
|
|
128
|
-
`Could not connect to PostgreSQL at ${host}:${port}. ` +
|
|
129
|
-
`Is the server running and accepting connections?`
|
|
130
|
-
);
|
|
131
|
-
case 'ENOTFOUND':
|
|
132
|
-
return `Host "${host}" not found. Check the database host configuration.`;
|
|
133
|
-
case 'ETIMEDOUT':
|
|
134
|
-
return (
|
|
135
|
-
`Connection to ${host}:${port} timed out. ` +
|
|
136
|
-
`Check network connectivity and firewall rules.`
|
|
137
|
-
);
|
|
138
|
-
case '3D000':
|
|
139
|
-
return `Database "${database}" does not exist on ${host}:${port}.`;
|
|
140
|
-
case '28P01':
|
|
141
|
-
return `Authentication failed for user "${opts.user ?? '(unknown)'}". Check credentials.`;
|
|
142
|
-
case '28000':
|
|
143
|
-
return `Authorization failed for user "${opts.user ?? '(unknown)'}". Check pg_hba.conf or user permissions.`;
|
|
144
|
-
default:
|
|
145
|
-
return err.message ?? 'Unknown error';
|
|
146
|
-
}
|
|
147
|
-
}
|
package/src/db/desired-state.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type { SchemaRegistry } from '../schema/registry.js';
|
|
2
|
-
import type { ResolvedModel } from '../schema/types.js';
|
|
3
|
-
import type { DesiredState, TableDefinition, IndexDefinition } from './types.js';
|
|
4
|
-
import { mapFieldsToColumns, modelToTableName } from './field-mapper.js';
|
|
5
|
-
import { getJobTables } from '../jobs/tables.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Converts the in-memory schema registry into the desired database state
|
|
9
|
-
* (list of table definitions) used by the diff engine for migrations.
|
|
10
|
-
*/
|
|
11
|
-
export class SchemaToDesired {
|
|
12
|
-
convert(registry: SchemaRegistry): DesiredState {
|
|
13
|
-
const tables: TableDefinition[] = [];
|
|
14
|
-
const models = registry.getAllModels();
|
|
15
|
-
let hasSequenceField = false;
|
|
16
|
-
|
|
17
|
-
for (const model of models) {
|
|
18
|
-
const { primaryTable, joinTables } = this.modelToTables(model);
|
|
19
|
-
tables.push(primaryTable);
|
|
20
|
-
tables.push(...joinTables);
|
|
21
|
-
|
|
22
|
-
if (model.fields.some((f) => f.config.type === 'sequence')) {
|
|
23
|
-
hasSequenceField = true;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Add the naming_sequence helper table if any model uses sequence fields
|
|
28
|
-
if (hasSequenceField) {
|
|
29
|
-
tables.push(this.buildNamingSequenceTable());
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Always include the internal job queue tables
|
|
33
|
-
tables.push(...getJobTables());
|
|
34
|
-
|
|
35
|
-
return { tables };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Converts a single model into its primary table definition plus any
|
|
40
|
-
* extra join/pivot tables required by its fields.
|
|
41
|
-
*/
|
|
42
|
-
private modelToTables(model: ResolvedModel): {
|
|
43
|
-
primaryTable: TableDefinition;
|
|
44
|
-
joinTables: TableDefinition[];
|
|
45
|
-
} {
|
|
46
|
-
const tableName = modelToTableName(model.qualifiedName);
|
|
47
|
-
const mapping = mapFieldsToColumns(model);
|
|
48
|
-
const indexes = this.buildIndexes(model, tableName);
|
|
49
|
-
|
|
50
|
-
const primaryTable: TableDefinition = {
|
|
51
|
-
name: tableName,
|
|
52
|
-
columns: mapping.columns,
|
|
53
|
-
foreignKeys: mapping.foreignKeys,
|
|
54
|
-
checkConstraints: mapping.checkConstraints,
|
|
55
|
-
indexes,
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
return { primaryTable, joinTables: mapping.extraTables };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Generates index definitions from a model's declared indexes. */
|
|
62
|
-
private buildIndexes(model: ResolvedModel, tableName: string): IndexDefinition[] {
|
|
63
|
-
if (!model.indexes || model.indexes.length === 0) return [];
|
|
64
|
-
|
|
65
|
-
return model.indexes.map((idx) => {
|
|
66
|
-
const prefix = idx.unique ? 'uidx' : 'idx';
|
|
67
|
-
const indexName = `${prefix}_${tableName}_${idx.fields.join('_')}`;
|
|
68
|
-
return {
|
|
69
|
-
name: indexName,
|
|
70
|
-
table: tableName,
|
|
71
|
-
columns: idx.fields,
|
|
72
|
-
unique: idx.unique ?? false,
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Table definition for the naming_sequence counter table. */
|
|
78
|
-
private buildNamingSequenceTable(): TableDefinition {
|
|
79
|
-
return {
|
|
80
|
-
name: 'naming_sequence',
|
|
81
|
-
columns: [
|
|
82
|
-
{ name: 'model', type: 'VARCHAR(255)', nullable: false },
|
|
83
|
-
{ name: 'field', type: 'VARCHAR(255)', nullable: false },
|
|
84
|
-
{ name: 'next_val', type: 'BIGINT', nullable: false, defaultValue: '1' },
|
|
85
|
-
],
|
|
86
|
-
foreignKeys: [],
|
|
87
|
-
checkConstraints: [],
|
|
88
|
-
indexes: [
|
|
89
|
-
{
|
|
90
|
-
name: 'uidx_naming_sequence_key',
|
|
91
|
-
table: 'naming_sequence',
|
|
92
|
-
columns: ['model', 'field'],
|
|
93
|
-
unique: true,
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
package/src/db/diff-engine.ts
DELETED
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
DesiredState,
|
|
3
|
-
ActualState,
|
|
4
|
-
DdlOperation,
|
|
5
|
-
TableDefinition,
|
|
6
|
-
ActualTable,
|
|
7
|
-
ColumnDefinition,
|
|
8
|
-
ActualColumn,
|
|
9
|
-
IndexDefinition,
|
|
10
|
-
ActualIndex,
|
|
11
|
-
ForeignKeyDefinition,
|
|
12
|
-
ActualForeignKey,
|
|
13
|
-
CheckConstraintDefinition,
|
|
14
|
-
ActualCheckConstraint,
|
|
15
|
-
} from './types.js';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Compares a desired schema state against the actual database state and
|
|
19
|
-
* produces a sorted list of DDL operations needed to reconcile the two.
|
|
20
|
-
*/
|
|
21
|
-
export class DiffEngine {
|
|
22
|
-
// --- Public API ---
|
|
23
|
-
|
|
24
|
-
/** Compute the full set of DDL operations to bring `actual` in line with `desired`. */
|
|
25
|
-
diff(desired: DesiredState, actual: ActualState): DdlOperation[] {
|
|
26
|
-
const operations: DdlOperation[] = [];
|
|
27
|
-
const actualTablesByName = new Map(actual.tables.map((t) => [t.name, t]));
|
|
28
|
-
|
|
29
|
-
// Generate operations for each desired table (create or update)
|
|
30
|
-
for (const desiredTable of desired.tables) {
|
|
31
|
-
const existingTable = actualTablesByName.get(desiredTable.name);
|
|
32
|
-
if (!existingTable) {
|
|
33
|
-
operations.push(...this.buildNewTableOperations(desiredTable));
|
|
34
|
-
} else {
|
|
35
|
-
operations.push(...this.diffTable(desiredTable, existingTable));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Flag tables that exist in the database but not in the schema
|
|
40
|
-
for (const existingTable of actual.tables) {
|
|
41
|
-
const stillDesired = desired.tables.find((t) => t.name === existingTable.name);
|
|
42
|
-
if (!stillDesired) {
|
|
43
|
-
operations.push(this.warnOrphanedTable(existingTable.name));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return this.sortByDependencyOrder(operations);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// --- Table creation ---
|
|
51
|
-
|
|
52
|
-
/** Build all operations needed to create a brand new table (table + indexes + foreign keys + check constraints). */
|
|
53
|
-
private buildNewTableOperations(table: TableDefinition): DdlOperation[] {
|
|
54
|
-
return [
|
|
55
|
-
this.buildCreateTableOp(table),
|
|
56
|
-
...table.indexes.map((idx) => this.buildCreateIndexOp(idx)),
|
|
57
|
-
...table.foreignKeys.map((fk) => this.buildAddForeignKeyOp(table.name, fk)),
|
|
58
|
-
];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Generate the CREATE TABLE DDL for a table definition. */
|
|
62
|
-
private buildCreateTableOp(table: TableDefinition): DdlOperation {
|
|
63
|
-
const columnClauses = table.columns.map((col) => this.columnToSql(col));
|
|
64
|
-
|
|
65
|
-
const primaryKeyColumn = table.columns.find((c) => c.primaryKey);
|
|
66
|
-
if (primaryKeyColumn) {
|
|
67
|
-
columnClauses.push(`PRIMARY KEY (${primaryKeyColumn.name})`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
for (const chk of table.checkConstraints) {
|
|
71
|
-
columnClauses.push(`CONSTRAINT "${chk.name}" CHECK (${chk.expression})`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const sql = `CREATE TABLE "${table.name}" (\n ${columnClauses.join(',\n ')}\n)`;
|
|
75
|
-
return {
|
|
76
|
-
type: 'CREATE_TABLE',
|
|
77
|
-
table: table.name,
|
|
78
|
-
sql,
|
|
79
|
-
destructive: false,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// --- Table diffing (existing table vs desired) ---
|
|
84
|
-
|
|
85
|
-
/** Compare an existing table against its desired definition and produce update operations. */
|
|
86
|
-
private diffTable(desired: TableDefinition, actual: ActualTable): DdlOperation[] {
|
|
87
|
-
const operations: DdlOperation[] = [];
|
|
88
|
-
|
|
89
|
-
operations.push(...this.diffColumns(desired.name, desired.columns, actual.columns));
|
|
90
|
-
operations.push(...this.diffIndexes(desired.name, desired.indexes, actual.indexes));
|
|
91
|
-
operations.push(...this.diffForeignKeys(desired.name, desired.foreignKeys, actual.foreignKeys));
|
|
92
|
-
operations.push(
|
|
93
|
-
...this.diffCheckConstraints(desired.name, desired.checkConstraints, actual.checkConstraints),
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
return operations;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** Detect added columns, type changes, and orphaned columns. */
|
|
100
|
-
private diffColumns(
|
|
101
|
-
tableName: string,
|
|
102
|
-
desiredColumns: ColumnDefinition[],
|
|
103
|
-
actualColumns: ActualColumn[],
|
|
104
|
-
): DdlOperation[] {
|
|
105
|
-
const operations: DdlOperation[] = [];
|
|
106
|
-
const actualColumnsByName = new Map(actualColumns.map((c) => [c.name, c]));
|
|
107
|
-
const desiredColumnNames = new Set(desiredColumns.map((c) => c.name));
|
|
108
|
-
|
|
109
|
-
for (const desiredCol of desiredColumns) {
|
|
110
|
-
const existingCol = actualColumnsByName.get(desiredCol.name);
|
|
111
|
-
if (!existingCol) {
|
|
112
|
-
operations.push(this.buildAddColumnOp(tableName, desiredCol));
|
|
113
|
-
} else if (this.hasColumnTypeChanged(desiredCol, existingCol)) {
|
|
114
|
-
operations.push(this.buildAlterColumnTypeOp(tableName, desiredCol));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Flag columns that exist in the DB but are no longer in the schema
|
|
119
|
-
for (const existingCol of actualColumns) {
|
|
120
|
-
if (!desiredColumnNames.has(existingCol.name)) {
|
|
121
|
-
operations.push(this.warnOrphanedColumn(tableName, existingCol.name));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return operations;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Detect indexes that need to be created. */
|
|
129
|
-
private diffIndexes(
|
|
130
|
-
tableName: string,
|
|
131
|
-
desiredIndexes: IndexDefinition[],
|
|
132
|
-
actualIndexes: ActualIndex[],
|
|
133
|
-
): DdlOperation[] {
|
|
134
|
-
const existingIndexNames = new Set(actualIndexes.map((i) => i.name));
|
|
135
|
-
|
|
136
|
-
return desiredIndexes
|
|
137
|
-
.filter((idx) => !existingIndexNames.has(idx.name))
|
|
138
|
-
.map((idx) => this.buildCreateIndexOp(idx));
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Detect foreign keys that need to be added. */
|
|
142
|
-
private diffForeignKeys(
|
|
143
|
-
tableName: string,
|
|
144
|
-
desiredForeignKeys: ForeignKeyDefinition[],
|
|
145
|
-
actualForeignKeys: ActualForeignKey[],
|
|
146
|
-
): DdlOperation[] {
|
|
147
|
-
const existingFkNames = new Set(actualForeignKeys.map((fk) => fk.name));
|
|
148
|
-
|
|
149
|
-
return desiredForeignKeys
|
|
150
|
-
.filter((fk) => !existingFkNames.has(fk.name))
|
|
151
|
-
.map((fk) => this.buildAddForeignKeyOp(tableName, fk));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/** Detect CHECK constraints that need to be added. */
|
|
155
|
-
private diffCheckConstraints(
|
|
156
|
-
tableName: string,
|
|
157
|
-
desiredConstraints: CheckConstraintDefinition[],
|
|
158
|
-
actualConstraints: ActualCheckConstraint[],
|
|
159
|
-
): DdlOperation[] {
|
|
160
|
-
const existingNames = new Set(actualConstraints.map((c) => c.name));
|
|
161
|
-
|
|
162
|
-
return desiredConstraints
|
|
163
|
-
.filter((chk) => !existingNames.has(chk.name))
|
|
164
|
-
.map((chk) => this.buildAddCheckConstraintOp(tableName, chk));
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// --- DDL operation builders ---
|
|
168
|
-
|
|
169
|
-
/** Generate an ADD COLUMN operation. */
|
|
170
|
-
private buildAddColumnOp(tableName: string, col: ColumnDefinition): DdlOperation {
|
|
171
|
-
const colSql = this.columnToSql(col);
|
|
172
|
-
const sql = `ALTER TABLE "${tableName}" ADD COLUMN ${colSql}`;
|
|
173
|
-
return { type: 'ADD_COLUMN', table: tableName, sql, destructive: false };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** Generate an ALTER COLUMN TYPE operation. */
|
|
177
|
-
private buildAlterColumnTypeOp(tableName: string, col: ColumnDefinition): DdlOperation {
|
|
178
|
-
const sql = `ALTER TABLE "${tableName}" ALTER COLUMN "${col.name}" TYPE ${col.type}`;
|
|
179
|
-
return { type: 'ALTER_COLUMN_TYPE', table: tableName, sql, destructive: false };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** Generate a CREATE INDEX operation. */
|
|
183
|
-
private buildCreateIndexOp(idx: IndexDefinition): DdlOperation {
|
|
184
|
-
const uniquePrefix = idx.unique ? 'UNIQUE ' : '';
|
|
185
|
-
const quotedColumns = idx.columns.map((c) => `"${c}"`).join(', ');
|
|
186
|
-
const whereClause = idx.where ? ` WHERE ${idx.where}` : '';
|
|
187
|
-
const sql = `CREATE ${uniquePrefix}INDEX "${idx.name}" ON "${idx.table}" (${quotedColumns})${whereClause}`;
|
|
188
|
-
return { type: 'CREATE_INDEX', table: idx.table, sql, destructive: false };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** Generate an ADD FOREIGN KEY constraint operation. */
|
|
192
|
-
private buildAddForeignKeyOp(tableName: string, fk: ForeignKeyDefinition): DdlOperation {
|
|
193
|
-
const sql = `ALTER TABLE "${tableName}" ADD CONSTRAINT "${fk.name}" FOREIGN KEY ("${fk.column}") REFERENCES "${fk.referencedTable}" ("${fk.referencedColumn}")`;
|
|
194
|
-
return { type: 'ADD_FOREIGN_KEY', table: tableName, sql, destructive: false };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Generate an ADD CHECK CONSTRAINT operation. */
|
|
198
|
-
private buildAddCheckConstraintOp(
|
|
199
|
-
tableName: string,
|
|
200
|
-
chk: CheckConstraintDefinition,
|
|
201
|
-
): DdlOperation {
|
|
202
|
-
const sql = `ALTER TABLE "${tableName}" ADD CONSTRAINT "${chk.name}" CHECK (${chk.expression})`;
|
|
203
|
-
return { type: 'ADD_CHECK_CONSTRAINT', table: tableName, sql, destructive: false };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// --- Orphan warnings (destructive markers) ---
|
|
207
|
-
|
|
208
|
-
/** Produce a warning operation for a table that exists in the DB but not in the schema. */
|
|
209
|
-
private warnOrphanedTable(tableName: string): DdlOperation {
|
|
210
|
-
return {
|
|
211
|
-
type: 'DROP_TABLE',
|
|
212
|
-
table: tableName,
|
|
213
|
-
sql: `-- WARNING: Table "${tableName}" exists in database but not in schema (not dropped without --allow-destructive)`,
|
|
214
|
-
destructive: true,
|
|
215
|
-
detail: `Orphaned table: ${tableName}`,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** Produce a warning operation for a column that exists in the DB but not in the schema. */
|
|
220
|
-
private warnOrphanedColumn(tableName: string, columnName: string): DdlOperation {
|
|
221
|
-
return {
|
|
222
|
-
type: 'DROP_COLUMN',
|
|
223
|
-
table: tableName,
|
|
224
|
-
sql: `-- WARNING: Column "${tableName}"."${columnName}" exists in database but not in schema (not dropped without --allow-destructive)`,
|
|
225
|
-
destructive: true,
|
|
226
|
-
detail: `Orphaned column: ${tableName}.${columnName}`,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// --- SQL helpers ---
|
|
231
|
-
|
|
232
|
-
/** Convert a column definition to its SQL fragment (e.g. `"name" TEXT NOT NULL DEFAULT 'x'`). */
|
|
233
|
-
private columnToSql(col: ColumnDefinition): string {
|
|
234
|
-
let sql = `"${col.name}" ${col.type}`;
|
|
235
|
-
if (!col.nullable && !col.primaryKey) sql += ' NOT NULL';
|
|
236
|
-
if (col.defaultValue !== undefined) sql += ` DEFAULT ${col.defaultValue}`;
|
|
237
|
-
return sql;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/** Check whether the column type has changed (case-insensitive comparison). */
|
|
241
|
-
private hasColumnTypeChanged(desired: ColumnDefinition, actual: ActualColumn): boolean {
|
|
242
|
-
return desired.type.toUpperCase() !== actual.type.toUpperCase();
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// --- Operation ordering ---
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Sort operations so they execute in a safe dependency order:
|
|
249
|
-
* 1. CREATE TABLE (tables must exist before columns/indexes reference them)
|
|
250
|
-
* 2. ADD COLUMN
|
|
251
|
-
* 3. ALTER COLUMN TYPE
|
|
252
|
-
* 4. CREATE INDEX (columns must exist first)
|
|
253
|
-
* 5. ADD FOREIGN KEY (referenced tables must exist first)
|
|
254
|
-
* 6. ADD CHECK CONSTRAINT (columns must exist first)
|
|
255
|
-
* 7. Destructive operations (always last, require explicit opt-in)
|
|
256
|
-
*/
|
|
257
|
-
private sortByDependencyOrder(operations: DdlOperation[]): DdlOperation[] {
|
|
258
|
-
const createTables: DdlOperation[] = [];
|
|
259
|
-
const addColumns: DdlOperation[] = [];
|
|
260
|
-
const alterColumns: DdlOperation[] = [];
|
|
261
|
-
const createIndexes: DdlOperation[] = [];
|
|
262
|
-
const addForeignKeys: DdlOperation[] = [];
|
|
263
|
-
const addCheckConstraints: DdlOperation[] = [];
|
|
264
|
-
const destructive: DdlOperation[] = [];
|
|
265
|
-
|
|
266
|
-
for (const op of operations) {
|
|
267
|
-
if (op.destructive) {
|
|
268
|
-
destructive.push(op);
|
|
269
|
-
} else {
|
|
270
|
-
switch (op.type) {
|
|
271
|
-
case 'CREATE_TABLE':
|
|
272
|
-
createTables.push(op);
|
|
273
|
-
break;
|
|
274
|
-
case 'ADD_COLUMN':
|
|
275
|
-
addColumns.push(op);
|
|
276
|
-
break;
|
|
277
|
-
case 'ALTER_COLUMN_TYPE':
|
|
278
|
-
alterColumns.push(op);
|
|
279
|
-
break;
|
|
280
|
-
case 'CREATE_INDEX':
|
|
281
|
-
createIndexes.push(op);
|
|
282
|
-
break;
|
|
283
|
-
case 'ADD_FOREIGN_KEY':
|
|
284
|
-
addForeignKeys.push(op);
|
|
285
|
-
break;
|
|
286
|
-
case 'ADD_CHECK_CONSTRAINT':
|
|
287
|
-
addCheckConstraints.push(op);
|
|
288
|
-
break;
|
|
289
|
-
default:
|
|
290
|
-
addColumns.push(op);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return [
|
|
296
|
-
...createTables,
|
|
297
|
-
...addColumns,
|
|
298
|
-
...alterColumns,
|
|
299
|
-
...createIndexes,
|
|
300
|
-
...addForeignKeys,
|
|
301
|
-
...addCheckConstraints,
|
|
302
|
-
...destructive,
|
|
303
|
-
];
|
|
304
|
-
}
|
|
305
|
-
}
|