uql-orm 0.43.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +3 -2
  2. package/dist/browser/querier/httpQuerier.d.ts +6 -0
  3. package/dist/browser/querier/httpQuerier.js +1 -1
  4. package/dist/browser/uql-browser.min.js +2 -2
  5. package/dist/browser/uql-browser.min.js.map +4 -4
  6. package/dist/dialect/abstractDialect.js +5 -6
  7. package/dist/dialect/abstractSqlDialect.d.ts +2 -2
  8. package/dist/dialect/abstractSqlDialect.js +1 -1
  9. package/dist/dialect/mysqlLikeSqlDialect.d.ts +8 -1
  10. package/dist/dialect/mysqlLikeSqlDialect.js +8 -1
  11. package/dist/dialect/pgLikeSqlDialect.d.ts +1 -1
  12. package/dist/dialect/pgLikeSqlDialect.js +1 -1
  13. package/dist/entity/decorator/bag.d.ts +3 -0
  14. package/dist/entity/index.d.ts +1 -1
  15. package/dist/entity/index.js +1 -1
  16. package/dist/entity/metadata/definition.d.ts +15 -13
  17. package/dist/entity/metadata/definition.js +47 -16
  18. package/dist/http/handler.d.ts +8 -0
  19. package/dist/http/handler.js +5 -5
  20. package/dist/migrate/builder/migrationBuilder.js +1 -2
  21. package/dist/migrate/builder/tableBuilder.d.ts +1 -0
  22. package/dist/migrate/builder/tableBuilder.js +9 -10
  23. package/dist/migrate/builder/types.d.ts +3 -19
  24. package/dist/migrate/cli.d.ts +5 -0
  25. package/dist/migrate/cli.js +33 -16
  26. package/dist/migrate/codegen/entityTypes.d.ts +7 -0
  27. package/dist/migrate/codegen/entityTypes.js +69 -0
  28. package/dist/migrate/codegen/index.d.ts +1 -0
  29. package/dist/migrate/codegen/index.js +1 -0
  30. package/dist/migrate/generator/definitionToNode.js +2 -2
  31. package/dist/migrate/index.d.ts +1 -1
  32. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +2 -5
  33. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +2 -0
  34. package/dist/migrate/introspection/baseSqlIntrospector.js +4 -3
  35. package/dist/migrate/introspection/mysqlIntrospector.js +1 -2
  36. package/dist/migrate/introspection/postgresIntrospector.js +1 -2
  37. package/dist/migrate/introspection/sqliteIntrospector.js +1 -2
  38. package/dist/migrate/migrator.d.ts +34 -20
  39. package/dist/migrate/migrator.js +90 -35
  40. package/dist/migrate/schemaGenerator.d.ts +20 -5
  41. package/dist/migrate/schemaGenerator.js +105 -30
  42. package/dist/schema/schemaASTBuilder.d.ts +23 -2
  43. package/dist/schema/schemaASTBuilder.js +3 -2
  44. package/dist/schema/schemaASTDiffer.d.ts +11 -1
  45. package/dist/schema/schemaASTDiffer.js +23 -7
  46. package/dist/schema/types.d.ts +19 -4
  47. package/dist/sqlite/sqliteDialect.d.ts +1 -1
  48. package/dist/sqlite/sqliteDialect.js +1 -1
  49. package/dist/type/entity.d.ts +14 -1
  50. package/dist/type/migration.d.ts +55 -9
  51. package/dist/type/queryWhere.d.ts +13 -3
  52. package/dist/util/field.util.d.ts +7 -2
  53. package/dist/util/field.util.js +9 -12
  54. package/dist/util/object.util.d.ts +3 -3
  55. package/dist/util/object.util.js +3 -3
  56. package/package.json +3 -3
@@ -16,6 +16,7 @@ export declare class TableBuilder implements ITableBuilder {
16
16
  private _foreignKeyBuilders;
17
17
  private _comment?;
18
18
  constructor(name: string);
19
+ /** Big, matching an entity's `@Id`: a key is spelled from this type, so it has to state the real one. */
19
20
  id(name?: string, options?: BaseColumnOptions): IColumnBuilder;
20
21
  integer(name: string, options?: BaseColumnOptions): IColumnBuilder;
21
22
  smallint(name: string, options?: BaseColumnOptions): IColumnBuilder;
@@ -12,8 +12,8 @@ import { expr } from './expressions.js';
12
12
  */
13
13
  class TableForeignKeyBuilder {
14
14
  _columns;
15
- _referencesTable;
16
- _referencesColumns = [];
15
+ _referencedTable;
16
+ _referencedColumns = [];
17
17
  _onDelete = 'NO ACTION';
18
18
  _onUpdate = 'NO ACTION';
19
19
  _name;
@@ -21,8 +21,8 @@ class TableForeignKeyBuilder {
21
21
  this._columns = columns;
22
22
  }
23
23
  references(table, columns) {
24
- this._referencesTable = table;
25
- this._referencesColumns = columns;
24
+ this._referencedTable = table;
25
+ this._referencedColumns = columns;
26
26
  return this;
27
27
  }
28
28
  onDelete(action) {
@@ -41,13 +41,12 @@ class TableForeignKeyBuilder {
41
41
  * Build the foreign key definition.
42
42
  */
43
43
  build() {
44
- if (!this._referencesTable)
44
+ if (!this._referencedTable)
45
45
  return undefined;
46
46
  return {
47
47
  name: this._name,
48
48
  columns: this._columns,
49
- referencesTable: this._referencesTable,
50
- referencesColumns: this._referencesColumns,
49
+ references: { table: this._referencedTable, columns: this._referencedColumns },
51
50
  onDelete: this._onDelete,
52
51
  onUpdate: this._onUpdate,
53
52
  };
@@ -66,8 +65,9 @@ export class TableBuilder {
66
65
  constructor(name) {
67
66
  this._name = name;
68
67
  }
68
+ /** Big, matching an entity's `@Id`: a key is spelled from this type, so it has to state the real one. */
69
69
  id(name = 'id', options = {}) {
70
- return this.add(name, { category: 'integer' }, { ...options, primaryKey: true, autoIncrement: true });
70
+ return this.add(name, { category: 'integer', size: 'big' }, { ...options, primaryKey: true, autoIncrement: true });
71
71
  }
72
72
  integer(name, options) {
73
73
  return this.add(name, { category: 'integer' }, options);
@@ -217,8 +217,7 @@ export class TableBuilder {
217
217
  foreignKeys.push({
218
218
  name: col.foreignKey.name,
219
219
  columns: [col.name],
220
- referencesTable: col.foreignKey.table,
221
- referencesColumns: col.foreignKey.columns,
220
+ references: { table: col.foreignKey.table, columns: col.foreignKey.columns },
222
221
  onDelete: col.foreignKey.onDelete,
223
222
  onUpdate: col.foreignKey.onUpdate,
224
223
  });
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import type { CanonicalType, ForeignKeyAction } from '../../schema/types.js';
8
8
  import type { IndexColumnInput, IndexOptions, IndexSchema } from '../../type/index.js';
9
+ import type { ForeignKeySchema } from '../../type/migration.js';
9
10
  /**
10
11
  * Foreign key reference options.
11
12
  */
@@ -124,27 +125,10 @@ export interface TableDefinition {
124
125
  /** Index definitions */
125
126
  indexes: IndexSchema[];
126
127
  /** Foreign key definitions at table level */
127
- foreignKeys: TableForeignKeyDefinition[];
128
+ foreignKeys: ForeignKeySchema[];
128
129
  /** Table comment */
129
130
  comment?: string;
130
131
  }
131
- /**
132
- * Table-level foreign key (for composite FKs).
133
- */
134
- export interface TableForeignKeyDefinition {
135
- /** Constraint name */
136
- name?: string;
137
- /** Local columns */
138
- columns: string[];
139
- /** Referenced table */
140
- referencesTable: string;
141
- /** Referenced columns */
142
- referencesColumns: string[];
143
- /** Action on delete */
144
- onDelete: ForeignKeyAction;
145
- /** Action on update */
146
- onUpdate: ForeignKeyAction;
147
- }
148
132
  /**
149
133
  * Type of migration operation.
150
134
  */
@@ -238,7 +222,7 @@ export interface DropIndexOperation extends MigrationOperation {
238
222
  export interface AddForeignKeyOperation extends MigrationOperation {
239
223
  type: 'addForeignKey';
240
224
  tableName: string;
241
- foreignKey: TableForeignKeyDefinition;
225
+ foreignKey: ForeignKeySchema;
242
226
  }
243
227
  /**
244
228
  * Drop foreign key operation.
@@ -14,6 +14,11 @@ export declare function runStatus(migrator: Migrator): Promise<void>;
14
14
  export declare function runPending(migrator: Migrator): Promise<void>;
15
15
  export declare function runGenerate(migrator: Migrator, args: string[]): Promise<void>;
16
16
  export declare function runGenerateFromEntities(migrator: Migrator, args: string[]): Promise<void>;
17
+ /**
18
+ * Writes a `.d.ts` for the registered entities. The point is a schema defined at runtime: the same
19
+ * registration that made the tables is what the compiler then checks queries against.
20
+ */
21
+ export declare function runTypes(migrator: Migrator, args: string[]): void;
17
22
  export declare function runSync(migrator: Migrator, args: string[], config: Partial<Config>): Promise<void>;
18
23
  export declare function runGenerateFromDb(migrator: Migrator, args: string[], config: Partial<Config>): Promise<void>;
19
24
  export declare function runDriftCheck(migrator: Migrator, config: Partial<Config>): Promise<void>;
@@ -4,6 +4,7 @@ import * as path from 'node:path';
4
4
  import { assertCliConfig } from './assertCliConfig.js';
5
5
  import { loadConfig } from './cli-config.js';
6
6
  import { createEntityCodeGenerator } from './codegen/entityCodeGenerator.js';
7
+ import { entityTypesSource } from './codegen/entityTypes.js';
7
8
  import { detectDrift } from './drift/driftDetector.js';
8
9
  import { Migrator } from './migrator.js';
9
10
  import { buildEntityAST, createSchemaGenerator } from './schemaGenerator.js';
@@ -74,6 +75,9 @@ export async function main(args = process.argv.slice(2)) {
74
75
  case 'sync':
75
76
  await runSync(migrator, filteredArgs.slice(1), config);
76
77
  break;
78
+ case 'types':
79
+ runTypes(migrator, filteredArgs.slice(1));
80
+ break;
77
81
  case 'pending':
78
82
  await runPending(migrator);
79
83
  break;
@@ -189,35 +193,45 @@ export async function runGenerateFromEntities(migrator, args) {
189
193
  const filePath = await migrator.generateFromEntities(name);
190
194
  console.log(`\nCreated migration from entities: ${filePath}`);
191
195
  }
196
+ /**
197
+ * Writes a `.d.ts` for the registered entities. The point is a schema defined at runtime: the same
198
+ * registration that made the tables is what the compiler then checks queries against.
199
+ */
200
+ export function runTypes(migrator, args) {
201
+ const output = readOutput(args) ?? './uql-entities.d.ts';
202
+ fs.mkdirSync(path.dirname(output), { recursive: true });
203
+ fs.writeFileSync(output, entityTypesSource(migrator.entities), 'utf-8');
204
+ console.log(`Wrote ${migrator.entities.length} entities to ${output}`);
205
+ }
206
+ /** `--output`/`-o`, wherever a command takes one. */
207
+ function readOutput(args) {
208
+ const at = args.findIndex((arg) => arg === '--output' || arg === '-o');
209
+ return at === -1 ? undefined : args[at + 1];
210
+ }
192
211
  export async function runSync(migrator, args, config) {
193
- if (args.includes('--force')) {
194
- console.log('\n⚠️ WARNING: This will drop and recreate all tables!');
195
- console.log(' All data will be lost. This should only be used in development.\n');
196
- await migrator.sync({ force: true });
197
- console.log('\nSchema sync completed.');
198
- return;
199
- }
200
212
  // Pulling the database into entity files is what `generate:from-db` does; one implementation.
201
213
  if (args.includes('--pull')) {
202
214
  return runGenerateFromDb(migrator, args, config);
203
215
  }
216
+ const force = args.includes('--force');
204
217
  const safe = !args.includes('--unsafe');
218
+ const options = { force, safe, drop: !safe };
219
+ // Ahead of the warning as well as of the run: `--dry-run` means the same thing whatever else was
220
+ // asked for, and it used to be ignored beside `--force`.
205
221
  if (args.includes('--dry-run')) {
206
- const statements = await migrator.planSync({ safe, drop: !safe });
222
+ const statements = await migrator.planSync(options);
207
223
  console.log(statements.length ? `\n${statements.join('\n')}` : '\nSchema is already in sync.');
208
224
  return;
209
225
  }
210
- await migrator.autoSync({ safe, drop: !safe, logging: true });
226
+ if (force) {
227
+ console.log('\n⚠️ WARNING: This will drop and recreate all tables!');
228
+ console.log(' All data will be lost. This should only be used in development.\n');
229
+ }
230
+ await migrator.sync({ ...options, logging: true });
211
231
  console.log('\nSchema sync completed.');
212
232
  }
213
233
  export async function runGenerateFromDb(migrator, args, config) {
214
- // Parse output directory
215
- let outputDir = './src/entities';
216
- for (let i = 0; i < args.length; i++) {
217
- if ((args[i] === '--output' || args[i] === '-o') && args[i + 1]) {
218
- outputDir = args[++i];
219
- }
220
- }
234
+ const outputDir = readOutput(args) ?? './src/entities';
221
235
  if (!migrator.schemaIntrospector) {
222
236
  console.error('No introspector available. Check your pool configuration.');
223
237
  process.exit(1);
@@ -342,6 +356,9 @@ Commands:
342
356
  --pull Go the other way: generate entities from the database
343
357
  --force Drop and recreate all tables (dangerous!)
344
358
 
359
+ types Write a .d.ts for the registered entities
360
+ --output, -o <file> Output path (default: ./uql-entities.d.ts)
361
+
345
362
  drift:check Check for schema drift between entities and database
346
363
 
347
364
  Configuration:
@@ -0,0 +1,7 @@
1
+ import type { Type } from '../../type/index.js';
2
+ /**
3
+ * A `.d.ts` for the entities as registered, so a shape that only exists at runtime reaches the
4
+ * compiler too. Interfaces, not the entity classes `generate:from-db` writes: those are the source of
5
+ * a schema, these describe one already defined elsewhere.
6
+ */
7
+ export declare function entityTypesSource(entities: readonly Type<unknown>[]): string;
@@ -0,0 +1,69 @@
1
+ import { getMeta } from '../../entity/index.js';
2
+ import { canonicalToTypeScript } from '../../schema/canonicalType.js';
3
+ import { resolveColumnCanonicalType } from '../../schema/schemaASTBuilder.js';
4
+ import { isToManyRelation, upperFirst } from '../../util/index.js';
5
+ /**
6
+ * A `.d.ts` for the entities as registered, so a shape that only exists at runtime reaches the
7
+ * compiler too. Interfaces, not the entity classes `generate:from-db` writes: those are the source of
8
+ * a schema, these describe one already defined elsewhere.
9
+ */
10
+ export function entityTypesSource(entities) {
11
+ const metas = [...entities]
12
+ .map((entity) => getMeta(entity))
13
+ .sort((a, b) => a.entity.name.localeCompare(b.entity.name));
14
+ const names = interfaceNames(metas);
15
+ const interfaces = metas.map((meta) => {
16
+ const members = [
17
+ ...Object.entries(meta.fields).map(([key, field]) => member(key, fieldType(field))),
18
+ ...Object.entries(meta.relations).map(([key, rel]) => member(key, relationType(rel, names))),
19
+ ];
20
+ return `export interface ${names.get(meta.entity)} {\n${members.join('\n')}\n}`;
21
+ });
22
+ return ['// Generated by `uql-migrate types`. Do not edit.', '', ...interfaces, ''].join('\n');
23
+ }
24
+ /**
25
+ * One interface name per entity, settled before anything is written so a relation and its target
26
+ * agree. A class minted at runtime is named after the content type it came from, which nothing stops
27
+ * from carrying a dash or colliding with another once the dashes are gone - and either would emit a
28
+ * file that does not compile.
29
+ */
30
+ function interfaceNames(metas) {
31
+ const taken = new Set();
32
+ return new Map(metas.map((meta) => {
33
+ const base = identifier(upperFirst(meta.entity.name));
34
+ let name = base;
35
+ for (let n = 2; taken.has(name); n++) {
36
+ name = `${base}${n}`;
37
+ }
38
+ taken.add(name);
39
+ return [meta.entity, name];
40
+ }));
41
+ }
42
+ /** `text` as an identifier: what cannot be in one is dropped, and what cannot start one is prefixed. */
43
+ function identifier(text) {
44
+ const stripped = text.replace(/[^A-Za-z0-9_$]/g, '');
45
+ return /^[A-Za-z_$]/.test(stripped) ? stripped : `Entity${stripped}`;
46
+ }
47
+ /** A column name a property cannot hold - `hero-image` - is quoted rather than dropped. */
48
+ function member(key, type) {
49
+ const name = key === identifier(key) ? key : JSON.stringify(key);
50
+ return ` ${name}?: ${type};`;
51
+ }
52
+ /**
53
+ * The property type a column reads back as, resolved the way the DDL resolves it - so a foreign key
54
+ * reports the type of the key it points at rather than the fallback its own options carry.
55
+ */
56
+ function fieldType(field) {
57
+ return field ? canonicalToTypeScript(resolveColumnCanonicalType(field)) : 'unknown';
58
+ }
59
+ /**
60
+ * A relation is the related interface, a list where the cardinality says so - and `unknown` where the
61
+ * target is outside the set, since naming an interface the file does not declare would not compile.
62
+ */
63
+ function relationType(relation, names) {
64
+ const target = relation && names.get(relation.entity());
65
+ if (!target) {
66
+ return 'unknown';
67
+ }
68
+ return isToManyRelation(relation) ? `${target}[]` : target;
69
+ }
@@ -4,4 +4,5 @@
4
4
  * Generates TypeScript entity code from database schemas.
5
5
  */
6
6
  export { createEntityCodeGenerator, EntityCodeGenerator, type EntityCodeGeneratorOptions, type GeneratedEntity, } from './entityCodeGenerator.js';
7
+ export { entityTypesSource } from './entityTypes.js';
7
8
  export { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCall, emitSqlRunCalls, type SqlMigrationModuleOptions, } from './migrationFile.js';
@@ -5,4 +5,5 @@
5
5
  */
6
6
  // Entity code generator
7
7
  export { createEntityCodeGenerator, EntityCodeGenerator, } from './entityCodeGenerator.js';
8
+ export { entityTypesSource } from './entityTypes.js';
8
9
  export { buildSqlQuerierMigrationModule, EMPTY_MANUAL_MIGRATION_DOWN_INNER, EMPTY_MANUAL_MIGRATION_UP_INNER, emitSqlRunCall, emitSqlRunCalls, } from './migrationFile.js';
@@ -40,8 +40,8 @@ export function tableDefinitionToNode(def) {
40
40
  columns: fkDef.columns.map((name) => columns.get(name)).filter((c) => c !== undefined),
41
41
  },
42
42
  to: {
43
- table: { name: fkDef.referencesTable },
44
- columns: fkDef.referencesColumns.map((name) => ({ name })),
43
+ table: { name: fkDef.references.table },
44
+ columns: fkDef.references.columns.map((name) => ({ name })),
45
45
  },
46
46
  onDelete: fkDef.onDelete,
47
47
  onUpdate: fkDef.onUpdate,
@@ -1,4 +1,4 @@
1
- export type { ColumnSchema, DialectName, ForeignKeySchema, IndexSchema, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlDialectName, SqlQuerier, SqlQueryDialect, TableSchema, } from '../type/index.js';
1
+ export type { ColumnSchema, DialectName, ForeignKeySchema, IndexSchema, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, SchemaDiff, SchemaGenerator, SchemaIntrospector, SqlDialectName, SqlQuerier, SqlQueryDialect, SyncOptions, TableSchema, } from '../type/index.js';
2
2
  export { type Config, isSqlQuerier } from '../type/index.js';
3
3
  export { acquireQuerierForMigrations } from './acquireQuerierForMigrations.js';
4
4
  export { assertCliConfig } from './assertCliConfig.js';
@@ -1,9 +1,6 @@
1
+ import type { ForeignKeyAction } from '../../schema/types.js';
1
2
  import type { ColumnSchema, ForeignKeySchema, IndexSchema, QuerierPool, RawRow, SchemaIntrospector, SqlQuerier, TableSchema } from '../../type/index.js';
2
3
  import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
3
- /**
4
- * Referential action type for foreign key constraints.
5
- */
6
- export type ReferentialAction = 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION';
7
4
  /**
8
5
  * Reads the rows of one statement while introspecting a table.
9
6
  *
@@ -72,7 +69,7 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
72
69
  /**
73
70
  * Normalize referential action string to standard type.
74
71
  */
75
- protected normalizeReferentialAction(action: string): ReferentialAction | undefined;
72
+ protected normalizeReferentialAction(action: string): ForeignKeyAction | undefined;
76
73
  /**
77
74
  * Convert bigint/null values to number safely.
78
75
  */
@@ -131,6 +131,8 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
131
131
  return 'RESTRICT';
132
132
  case 'NO ACTION':
133
133
  return 'NO ACTION';
134
+ case 'SET DEFAULT':
135
+ return 'SET DEFAULT';
134
136
  default:
135
137
  return undefined;
136
138
  }
@@ -1,6 +1,7 @@
1
1
  import { canonicalColumnType } from '../../schema/canonicalType.js';
2
2
  import { createTableNode, SchemaAST } from '../../schema/schemaAST.js';
3
3
  import { escapeSqlId } from '../../util/index.js';
4
+ import { derivedForeignKeyName } from '../../util/sql.util.js';
4
5
  /**
5
6
  * Base class for SQL introspectors with shared AST building logic.
6
7
  */
@@ -83,14 +84,14 @@ export class BaseSqlIntrospector {
83
84
  if (!fromTable)
84
85
  continue;
85
86
  for (const fk of schema.foreignKeys) {
86
- const toTable = tableNodes.get(fk.referencedTable);
87
+ const toTable = tableNodes.get(fk.references.table);
87
88
  if (!toTable)
88
89
  continue;
89
90
  const fromColumns = fk.columns.flatMap((name) => fromTable.columns.get(name) ?? []);
90
- const toColumns = fk.referencedColumns.flatMap((name) => toTable.columns.get(name) ?? []);
91
+ const toColumns = fk.references.columns.flatMap((name) => toTable.columns.get(name) ?? []);
91
92
  if (fromColumns.length > 0 && toColumns.length > 0) {
92
93
  const rel = {
93
- name: fk.name,
94
+ name: fk.name ?? derivedForeignKeyName(schema.name, fk.columns),
94
95
  type: fromColumns[0].isUnique ? 'OneToOne' : 'ManyToOne',
95
96
  from: { table: fromTable, columns: fromColumns },
96
97
  to: { table: toTable, columns: toColumns },
@@ -123,8 +123,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
123
123
  return results.map((row) => ({
124
124
  name: row.constraint_name,
125
125
  columns: (row.columns || '').split(','),
126
- referencedTable: row.referenced_table,
127
- referencedColumns: (row.referenced_columns || '').split(','),
126
+ references: { table: row.referenced_table, columns: (row.referenced_columns || '').split(',') },
128
127
  onDelete: this.normalizeReferentialAction(row.delete_rule),
129
128
  onUpdate: this.normalizeReferentialAction(row.update_rule),
130
129
  }));
@@ -204,8 +204,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
204
204
  return results.map((row) => ({
205
205
  name: row.constraint_name,
206
206
  columns: row.columns,
207
- referencedTable: row.referenced_table,
208
- referencedColumns: row.referenced_columns,
207
+ references: { table: row.referenced_table, columns: row.referenced_columns },
209
208
  onDelete: this.normalizeReferentialAction(row.delete_rule),
210
209
  onUpdate: this.normalizeReferentialAction(row.update_rule),
211
210
  }));
@@ -113,8 +113,7 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
113
113
  // derives it. Seeded from the columns, not the PRAGMA's row id, which nothing else knows.
114
114
  name: derivedForeignKeyName(tableName, columns),
115
115
  columns,
116
- referencedTable: first.table,
117
- referencedColumns: rows.map((r) => r.to),
116
+ references: { table: first.table, columns: rows.map((r) => r.to) },
118
117
  onDelete: this.normalizeReferentialAction(first.on_delete),
119
118
  onUpdate: this.normalizeReferentialAction(first.on_update),
120
119
  };
@@ -1,4 +1,4 @@
1
- import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, Type } from '../type/index.js';
1
+ import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, SyncOptions, Type } from '../type/index.js';
2
2
  import { LoggerWrapper } from '../util/index.js';
3
3
  import type { IMigrationBuilder } from './builder/types.js';
4
4
  /**
@@ -88,31 +88,45 @@ export declare class Migrator {
88
88
  private introspectClaimedSchemas;
89
89
  findEntityForTable(tableName: string): Promise<Type<unknown> | undefined>;
90
90
  /**
91
- * Sync schema directly (for development only - not for production!)
92
- */
93
- sync(options?: {
94
- force?: boolean;
95
- }): Promise<void>;
96
- /**
97
- * Drops and recreates all tables (Development only!)
91
+ * Applies the entity schema to the database: every registered entity, or the one `entity` names.
92
+ *
93
+ * The whole surface is this and {@link planSync}, which answers the same question without running
94
+ * it - `force` and a single entity included, so `--dry-run` means the same thing whatever else was
95
+ * asked for.
98
96
  */
99
- syncForce(): Promise<void>;
97
+ sync(options?: SyncOptions): Promise<void>;
100
98
  /**
101
- * Safely synchronizes the schema by only adding missing tables and columns.
99
+ * Every table dropped and recreated.
100
+ *
101
+ * Both directions span the whole entity set rather than looping an entity at a time. A per-entity
102
+ * AST cannot resolve a cross-entity foreign key, so the old create loop silently produced a schema
103
+ * with no referential integrity; and the old drop loop went in reverse *declaration* order, which
104
+ * says nothing about the relation graph and is rejected as soon as the constraints are really there.
102
105
  */
103
- autoSync(options?: {
104
- safe?: boolean;
105
- drop?: boolean;
106
- logging?: boolean;
107
- }): Promise<void>;
106
+ private forceStatements;
108
107
  /**
109
- * The DDL {@link autoSync} would run, without running it. Separate so `--dry-run` shows the real
108
+ * Sync one entity, for a schema that grows while the process runs: a content type an admin just
109
+ * created is one table to add, where the whole set would read the catalogue to work that out.
110
+ *
111
+ * The new-table case costs one existence check and creates with `IF NOT EXISTS`, so instances racing
112
+ * the same admin save settle instead of colliding. An existing table still pays for introspection,
113
+ * since a column diff needs the columns.
114
+ */
115
+ /** The DDL for one entity: {@link planSync} narrowed to the table it names. */
116
+ private planEntity;
117
+ /** An alter diff as statements, narrowed to what the caller allows. */
118
+ private alterFromDiff;
119
+ /** The same for one entity against the table it already has, and nothing where the two agree. */
120
+ private alterFromEntity;
121
+ /** The configured entities, with `entity` among them however the migrator was built. */
122
+ private entitiesWith;
123
+ /** The introspector for a claimed schema, which is the connection's own where none is claimed. */
124
+ private introspectorFor;
125
+ /**
126
+ * The DDL {@link sync} would run, without running it. Separate so `--dry-run` shows the real
110
127
  * statements rather than a summary of a second, differently-computed diff.
111
128
  */
112
- planSync(options?: {
113
- safe?: boolean;
114
- drop?: boolean;
115
- }): Promise<string[]>;
129
+ planSync(options?: SyncOptions): Promise<string[]>;
116
130
  /**
117
131
  * New tables are emitted together, never one at a time: a single-entity AST has no other table for a
118
132
  * relation to resolve against, so every cross-entity foreign key was dropped and generated schemas