uql-orm 0.25.0 → 0.26.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 (58) hide show
  1. package/README.md +9 -8
  2. package/dist/browser/uql-browser.min.js.map +1 -1
  3. package/dist/cockroachdb/cockroachDialect.js +1 -1
  4. package/dist/dialect/indexSqlDialect.d.ts +3 -2
  5. package/dist/dialect/indexSqlDialect.js +10 -8
  6. package/dist/dialect/mysqlLikeSqlDialect.d.ts +0 -2
  7. package/dist/dialect/mysqlLikeSqlDialect.js +0 -7
  8. package/dist/dialect/pgLikeSqlDialect.js +1 -0
  9. package/dist/maria/mariaDialect.js +1 -1
  10. package/dist/migrate/bin.js +0 -0
  11. package/dist/migrate/builder/migrationBuilder.js +1 -1
  12. package/dist/migrate/builder/tableBuilder.js +2 -2
  13. package/dist/migrate/cli.js +4 -6
  14. package/dist/migrate/codegen/entityCodeGenerator.d.ts +5 -0
  15. package/dist/migrate/codegen/entityCodeGenerator.js +32 -27
  16. package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -1
  17. package/dist/migrate/codegen/fieldOptionsSource.js +6 -1
  18. package/dist/migrate/codegen/indexDecoratorSource.d.ts +14 -0
  19. package/dist/migrate/codegen/indexDecoratorSource.js +105 -0
  20. package/dist/migrate/drift/driftDetector.d.ts +5 -50
  21. package/dist/migrate/drift/driftDetector.js +215 -224
  22. package/dist/migrate/drift/index.d.ts +1 -1
  23. package/dist/migrate/drift/index.js +1 -1
  24. package/dist/migrate/generator/definitionToNode.d.ts +9 -0
  25. package/dist/migrate/generator/definitionToNode.js +79 -0
  26. package/dist/migrate/generator/indexNodeToSchema.js +4 -2
  27. package/dist/migrate/generator/mongoSchemaGenerator.js +3 -3
  28. package/dist/migrate/introspection/baseSqlIntrospector.d.ts +3 -0
  29. package/dist/migrate/introspection/baseSqlIntrospector.js +13 -5
  30. package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -0
  31. package/dist/migrate/introspection/mongoIntrospector.js +5 -10
  32. package/dist/migrate/introspection/mysqlIntrospector.js +1 -1
  33. package/dist/migrate/introspection/postgresIntrospector.d.ts +57 -5
  34. package/dist/migrate/introspection/postgresIntrospector.js +93 -10
  35. package/dist/migrate/introspection/sqliteIntrospector.js +1 -1
  36. package/dist/migrate/migrator.js +3 -2
  37. package/dist/migrate/schemaGenerator.d.ts +26 -3
  38. package/dist/migrate/schemaGenerator.js +53 -92
  39. package/dist/schema/index.d.ts +3 -3
  40. package/dist/schema/index.js +2 -2
  41. package/dist/schema/indexColumns.d.ts +10 -0
  42. package/dist/schema/indexColumns.js +11 -0
  43. package/dist/schema/indexDifferences.d.ts +22 -0
  44. package/dist/schema/indexDifferences.js +49 -0
  45. package/dist/schema/schemaAST.js +2 -8
  46. package/dist/schema/schemaASTBuilder.d.ts +6 -59
  47. package/dist/schema/schemaASTBuilder.js +208 -233
  48. package/dist/schema/schemaASTDiffer.d.ts +9 -56
  49. package/dist/schema/schemaASTDiffer.js +229 -393
  50. package/dist/schema/types.d.ts +5 -12
  51. package/dist/schema/types.js +15 -0
  52. package/dist/type/dialect.d.ts +7 -2
  53. package/dist/type/dialect.js +1 -0
  54. package/dist/type/entity.d.ts +12 -10
  55. package/dist/type/migration.d.ts +14 -1
  56. package/dist/util/string.util.js +6 -1
  57. package/package.json +3 -4
  58. package/LICENSE.md +0 -22
@@ -1,9 +1,10 @@
1
1
  import { AbstractSqlDialect } from '../dialect/index.js';
2
2
  import { getMeta } from '../entity/index.js';
3
3
  import { areTypesEqual, canonicalToSql, fieldOptionsToCanonical, isVectorCategory, sqlToCanonical, } from '../schema/canonicalType.js';
4
- import { SchemaASTBuilder } from '../schema/schemaASTBuilder.js';
4
+ import { buildSchemaAST } from '../schema/schemaASTBuilder.js';
5
5
  import { escapeSqlId, getKeys, isAutoIncrement } from '../util/index.js';
6
6
  import { formatDefaultValue } from './builder/expressions.js';
7
+ import { fullColumnDefinitionToNode, tableDefinitionToNode } from './generator/definitionToNode.js';
7
8
  import { indexNodeToSchema } from './generator/indexNodeToSchema.js';
8
9
  /**
9
10
  * Unified SQL schema generator.
@@ -90,7 +91,7 @@ export class SqlSchemaGenerator {
90
91
  * resolves instead of being silently dropped.
91
92
  */
92
93
  orderedTables(entities, direction, only) {
93
- const ast = new SchemaASTBuilder(this.dialect.namingStrategy).fromEntities(entities);
94
+ const ast = buildEntityAST(this, entities, this.defaultForeignKeyAction);
94
95
  const tables = direction === 'create' ? ast.getCreateOrder() : ast.getDropOrder();
95
96
  if (!only) {
96
97
  return tables;
@@ -331,7 +332,11 @@ export class SqlSchemaGenerator {
331
332
  for (const [name] of currentColumns) {
332
333
  columnsToDrop.push(name);
333
334
  }
334
- if (columnsToAdd.length === 0 && columnsToAlter.length === 0 && columnsToDrop.length === 0) {
335
+ const indexesToAdd = this.missingIndexes(entity, currentTable);
336
+ if (columnsToAdd.length === 0 &&
337
+ columnsToAlter.length === 0 &&
338
+ columnsToDrop.length === 0 &&
339
+ indexesToAdd.length === 0) {
335
340
  return undefined;
336
341
  }
337
342
  return {
@@ -340,8 +345,32 @@ export class SqlSchemaGenerator {
340
345
  columnsToAdd: columnsToAdd.length > 0 ? columnsToAdd : undefined,
341
346
  columnsToAlter: columnsToAlter.length > 0 ? columnsToAlter : undefined,
342
347
  columnsToDrop: columnsToDrop.length > 0 ? columnsToDrop : undefined,
348
+ indexesToAdd: indexesToAdd.length > 0 ? indexesToAdd : undefined,
343
349
  };
344
350
  }
351
+ /**
352
+ * Indexes the entity declares that the table does not have, matched by name and built the same way
353
+ * `CREATE TABLE` builds them, so adding an `@Index` to an entity already in the database is picked
354
+ * up rather than waiting for the table to be created from scratch somewhere else.
355
+ *
356
+ * Only ever additive. An index the entity does not name is left alone: it may well have been
357
+ * created deliberately outside the ORM, and dropping it is a decision for a reviewed migration.
358
+ */
359
+ missingIndexes(entity, currentTable) {
360
+ const desired = buildEntityAST(this, [entity]).getTable(currentTable.name)?.indexes ?? [];
361
+ const present = new Set(currentTable.indexes.map((index) => index.name));
362
+ return desired
363
+ .filter((index) => !present.has(index.name) && !this.isInlineVectorIndex(index))
364
+ .map(indexNodeToSchema);
365
+ }
366
+ /**
367
+ * A vector index this dialect declares inside `CREATE TABLE` rather than as a statement of its own,
368
+ * which MariaDB is alone in doing. It has no `CREATE INDEX` form, so it can only ever be created
369
+ * with its table, never added to one.
370
+ */
371
+ isInlineVectorIndex(index) {
372
+ return this.features.inlineVectorIndex && index.type === 'vector';
373
+ }
345
374
  columnNodeToSchema(col) {
346
375
  return {
347
376
  name: col.name,
@@ -430,12 +459,11 @@ export class SqlSchemaGenerator {
430
459
  generateCreateTableFromNode(table, options = {}) {
431
460
  const columns = [];
432
461
  const constraints = [];
433
- const isInlineVectorIdx = this.features.inlineVectorIndex;
434
- const vectorIndexes = isInlineVectorIdx ? table.indexes.filter((idx) => idx.type === 'vector') : [];
435
- const regularIndexes = isInlineVectorIdx ? table.indexes.filter((idx) => idx.type !== 'vector') : table.indexes;
462
+ const vectorIndexes = table.indexes.filter((index) => this.isInlineVectorIndex(index));
463
+ const regularIndexes = table.indexes.filter((index) => !this.isInlineVectorIndex(index));
436
464
  // MariaDB rejects a `VECTOR INDEX` whose column is nullable ("All parts of a VECTOR index must
437
465
  // be NOT NULL"), so being indexed decides it rather than the entity's own nullability.
438
- const indexedVectorColumns = new Set(vectorIndexes.flatMap((idx) => idx.columns.map((col) => col.name)));
466
+ const indexedVectorColumns = new Set(vectorIndexes.flatMap((idx) => idx.entries.map((entry) => entry.column)));
439
467
  for (const col of table.columns.values()) {
440
468
  const colDef = this.generateColumnFromNode(indexedVectorColumns.has(col.name) ? { ...col, nullable: false } : col);
441
469
  columns.push(colDef);
@@ -503,7 +531,7 @@ export class SqlSchemaGenerator {
503
531
  return this.generateCreateIndex(index.table.name, indexNodeToSchema(index), options);
504
532
  }
505
533
  generateCreateTableFromDefinition(table, options = {}) {
506
- const tableNode = this.tableDefinitionToNode(table);
534
+ const tableNode = tableDefinitionToNode(table);
507
535
  return this.generateCreateTableFromNode(tableNode, options);
508
536
  }
509
537
  generateRenameTableSql(oldName, newName) {
@@ -513,11 +541,11 @@ export class SqlSchemaGenerator {
513
541
  return `ALTER TABLE ${this.escapeId(oldName)} RENAME TO ${this.escapeId(newName)};`;
514
542
  }
515
543
  generateAddColumnSql(tableName, column) {
516
- const colSql = this.generateColumnFromNode(this.fullColumnDefinitionToNode(column, tableName));
544
+ const colSql = this.generateColumnFromNode(fullColumnDefinitionToNode(column, tableName));
517
545
  return `ALTER TABLE ${this.escapeId(tableName)} ADD COLUMN ${colSql};`;
518
546
  }
519
547
  generateAlterColumnSql(tableName, columnName, column) {
520
- const node = this.fullColumnDefinitionToNode(column, tableName);
548
+ const node = fullColumnDefinitionToNode(column, tableName);
521
549
  return this.generateAlterColumnStatements(tableName, { ...this.columnNodeToSchema(node), name: columnName }, this.generateColumnFromNode(node)).join('\n');
522
550
  }
523
551
  generateDropColumnSql(tableName, columnName) {
@@ -542,88 +570,21 @@ export class SqlSchemaGenerator {
542
570
  generateDropForeignKeySql(tableName, constraintName) {
543
571
  return `ALTER TABLE ${this.escapeId(tableName)} ${this.dialect.dropForeignKeySyntax} ${this.escapeId(constraintName)};`;
544
572
  }
545
- tableDefinitionToNode(def) {
546
- const columns = new Map();
547
- const pkNodes = [];
548
- const table = {
549
- name: def.name,
550
- columns,
551
- primaryKey: [], // placeholder
552
- indexes: [],
553
- schema: { tables: new Map(), relationships: [], indexes: [] },
554
- incomingRelations: [],
555
- outgoingRelations: [],
556
- comment: def.comment,
557
- };
558
- for (const colDef of def.columns) {
559
- const node = this.fullColumnDefinitionToNode(colDef, def.name);
560
- node.table = table;
561
- columns.set(node.name, node);
562
- if (node.isPrimaryKey) {
563
- pkNodes.push(node);
564
- }
565
- }
566
- const finalPrimaryKey = def.primaryKey
567
- ? def.primaryKey.map((name) => columns.get(name)).filter((c) => c !== undefined)
568
- : pkNodes;
569
- table.primaryKey = finalPrimaryKey;
570
- for (const idxDef of def.indexes) {
571
- const indexNode = {
572
- ...idxDef,
573
- table,
574
- columns: idxDef.columns
575
- .map((entry) => (entry.expression ? undefined : columns.get(entry.column)))
576
- .filter((c) => c !== undefined),
577
- entries: idxDef.columns,
578
- };
579
- table.indexes.push(indexNode);
580
- }
581
- for (const fkDef of def.foreignKeys) {
582
- const relNode = {
583
- name: fkDef.name ?? `fk_${def.name}_${fkDef.columns.join('_')}`,
584
- type: 'ManyToOne', // Builder default
585
- from: {
586
- table,
587
- columns: fkDef.columns.map((name) => columns.get(name)).filter((c) => c !== undefined),
588
- },
589
- to: {
590
- table: { name: fkDef.referencesTable },
591
- columns: fkDef.referencesColumns.map((name) => ({ name })),
592
- },
593
- onDelete: fkDef.onDelete,
594
- onUpdate: fkDef.onUpdate,
595
- };
596
- table.outgoingRelations.push(relNode);
597
- }
598
- return table;
599
- }
600
- fullColumnDefinitionToNode(col, tableName) {
601
- return {
602
- name: col.name,
603
- type: col.type,
604
- nullable: col.nullable,
605
- defaultValue: col.defaultValue,
606
- isPrimaryKey: col.primaryKey,
607
- isAutoIncrement: col.autoIncrement,
608
- isUnique: col.unique,
609
- comment: col.comment,
610
- table: { name: tableName },
611
- referencedBy: [],
612
- references: col.foreignKey
613
- ? {
614
- name: `fk_${tableName}_${col.name}`,
615
- type: 'ManyToOne',
616
- from: { table: { name: tableName }, columns: [] },
617
- to: {
618
- table: { name: col.foreignKey.table },
619
- columns: col.foreignKey.columns.map((name) => ({ name })),
620
- },
621
- onDelete: col.foreignKey.onDelete,
622
- onUpdate: col.foreignKey.onUpdate,
623
- }
624
- : undefined,
625
- };
626
- }
573
+ }
574
+ /**
575
+ * The entities as an AST, named the way `generator` names things.
576
+ *
577
+ * Its resolvers rather than a naming strategy, because the two disagree: a strategy renames whatever
578
+ * it is handed, while a generator leaves an explicit `@Entity({ name })` alone. Build the AST the
579
+ * other way and the table is created under one name and compared under another, which reports every
580
+ * table of a project using a naming strategy as both missing and unexpected.
581
+ */
582
+ export function buildEntityAST(generator, entities, defaultForeignKeyAction) {
583
+ return buildSchemaAST(entities, {
584
+ resolveTableName: (entity, meta) => generator.resolveTableName(entity, meta),
585
+ resolveColumnName: (key, field) => generator.resolveColumnName(key, field),
586
+ defaultForeignKeyAction,
587
+ });
627
588
  }
628
589
  /**
629
590
  * Synchronous factory for SQL schema generators only.
@@ -18,8 +18,8 @@ export { areTypesEqual, canonicalToColumnType, canonicalToSql, canonicalToTypeSc
18
18
  */
19
19
  export declare function introspectSchema(introspector: SchemaIntrospector): Promise<SchemaAST>;
20
20
  export { SchemaAST } from './schemaAST.js';
21
- export type { BuildFromEntitiesOptions } from './schemaASTBuilder.js';
22
- export { SchemaASTBuilder } from './schemaASTBuilder.js';
21
+ export type { BuildSchemaASTOptions } from './schemaASTBuilder.js';
22
+ export { buildSchemaAST } from './schemaASTBuilder.js';
23
23
  export type { DiffOptions } from './schemaASTDiffer.js';
24
- export { diffSchemas, SchemaASTDiffer } from './schemaASTDiffer.js';
24
+ export { diffSchemas } from './schemaASTDiffer.js';
25
25
  export type { CanonicalType, ColumnDiff, ColumnNode, Drift, DriftReport, DriftSeverity, DriftStatus, DriftType, ForeignKeyAction, IndexDiff, IndexNode, IndexSource, IndexSyncStatus, IndexType, RelationshipDiff, RelationshipNode, RelationshipSource, RelationshipType, SchemaAST as ISchemaAST, SchemaDiffResult, SizeVariant, TableDiff, TableNode, TypeCategory, ValidationError, ValidationErrorType, } from './types.js';
@@ -21,6 +21,6 @@ export async function introspectSchema(introspector) {
21
21
  // SchemaAST class
22
22
  export { SchemaAST } from './schemaAST.js';
23
23
  // Builder
24
- export { SchemaASTBuilder } from './schemaASTBuilder.js';
24
+ export { buildSchemaAST } from './schemaASTBuilder.js';
25
25
  // Differ
26
- export { diffSchemas, SchemaASTDiffer } from './schemaASTDiffer.js';
26
+ export { diffSchemas } from './schemaASTDiffer.js';
@@ -0,0 +1,10 @@
1
+ import type { ColumnNode, IndexNode } from './types.js';
2
+ /**
3
+ * The table columns an index resolves to, in order.
4
+ *
5
+ * Derived rather than stored: an index is defined by its entries, and a second field repeating them
6
+ * as columns is a second thing to keep in step. It went out of step - one introspector rebuilt the
7
+ * entries from the columns it had just built from the entries, and every fixture had to write both.
8
+ * An expression entry resolves to no column at all, which is why the two were never the same list.
9
+ */
10
+ export declare function indexColumns(index: IndexNode): ColumnNode[];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The table columns an index resolves to, in order.
3
+ *
4
+ * Derived rather than stored: an index is defined by its entries, and a second field repeating them
5
+ * as columns is a second thing to keep in step. It went out of step - one introspector rebuilt the
6
+ * entries from the columns it had just built from the entries, and every fixture had to write both.
7
+ * An expression entry resolves to no column at all, which is why the two were never the same list.
8
+ */
9
+ export function indexColumns(index) {
10
+ return index.entries.flatMap((entry) => (entry.expression ? [] : (index.table.columns.get(entry.column) ?? [])));
11
+ }
@@ -0,0 +1,22 @@
1
+ import type { IndexNode } from './types.js';
2
+ /**
3
+ * What an introspector can report about an index, and so all that diffing may compare. Anything it
4
+ * cannot report is skipped: the entity declares it, the database never mentions it, and no migration
5
+ * could ever make the two agree.
6
+ *
7
+ * Deliberately separate from the dialect's `IndexFeature`, which says what an engine can *emit* and
8
+ * rejects outright. The two look alike and are not: Postgres emits an expression index and reads one
9
+ * back, MySQL emits one it cannot describe afterwards.
10
+ */
11
+ export type IndexFacet = 'order' | 'nulls' | 'opsClass' | 'accessMethod' | 'include';
12
+ /**
13
+ * Everything an index differs by, named, or nothing when the two match.
14
+ *
15
+ * Only what both sides can state *structurally* is compared. SQL text is not: a database reprints an
16
+ * expression and a predicate from its parse tree, so `status IN ('a','b')` reads back as
17
+ * `status = ANY (ARRAY['a'::text, 'b'::text])`, `LIKE` as `~~`, and a date literal with its time zone
18
+ * spelled out. Folding that back needs a SQL parser, and every near-miss reports drift that no
19
+ * migration can settle. So a partial index's predicate is never compared, and an index over an
20
+ * expression has its entries left alone while the rest of it still compares.
21
+ */
22
+ export declare function describeIndexDifferences(source: IndexNode, target: IndexNode, facets: ReadonlySet<IndexFacet>): string[];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Everything an index differs by, named, or nothing when the two match.
3
+ *
4
+ * Only what both sides can state *structurally* is compared. SQL text is not: a database reprints an
5
+ * expression and a predicate from its parse tree, so `status IN ('a','b')` reads back as
6
+ * `status = ANY (ARRAY['a'::text, 'b'::text])`, `LIKE` as `~~`, and a date literal with its time zone
7
+ * spelled out. Folding that back needs a SQL parser, and every near-miss reports drift that no
8
+ * migration can settle. So a partial index's predicate is never compared, and an index over an
9
+ * expression has its entries left alone while the rest of it still compares.
10
+ */
11
+ export function describeIndexDifferences(source, target, facets) {
12
+ const differences = [];
13
+ const comparableEntries = ![...source.entries, ...target.entries].some((entry) => entry.expression);
14
+ if (comparableEntries) {
15
+ const [sourceColumns, targetColumns] = [source.entries, target.entries].map((entries) => entries.map((entry) => entrySignature(entry, facets)).join(', '));
16
+ if (sourceColumns !== targetColumns) {
17
+ differences.push(`columns: (${targetColumns}) → (${sourceColumns})`);
18
+ }
19
+ }
20
+ if ((source.unique ?? false) !== (target.unique ?? false)) {
21
+ differences.push(`unique: ${target.unique ?? false} → ${source.unique ?? false}`);
22
+ }
23
+ if (facets.has('accessMethod') && (source.type ?? 'btree') !== (target.type ?? 'btree')) {
24
+ differences.push(`type: ${target.type ?? 'btree'} → ${source.type ?? 'btree'}`);
25
+ }
26
+ if (facets.has('include')) {
27
+ // Order carries no meaning in an `INCLUDE` list, so it is compared as a set.
28
+ const [sourceInclude, targetInclude] = [source.include ?? [], target.include ?? []].map((columns) => [...columns].sort().join(', '));
29
+ if (sourceInclude !== targetInclude) {
30
+ differences.push(`include: (${targetInclude}) → (${sourceInclude})`);
31
+ }
32
+ }
33
+ return differences;
34
+ }
35
+ function entrySignature(entry, facets) {
36
+ const parts = [entry.column];
37
+ if (facets.has('order')) {
38
+ parts.push(entry.order ?? 'asc');
39
+ }
40
+ if (facets.has('nulls')) {
41
+ // Postgres states this on every entry, so an entity that omits it has asked for Postgres's own
42
+ // default: nulls sort opposite to the direction.
43
+ parts.push(`nulls ${entry.nulls ?? ((entry.order ?? 'asc') === 'desc' ? 'first' : 'last')}`);
44
+ }
45
+ if (facets.has('opsClass') && entry.opsClass) {
46
+ parts.push(entry.opsClass);
47
+ }
48
+ return parts.join(' ');
49
+ }
@@ -400,13 +400,7 @@ export class SchemaAST {
400
400
  const table = clone.tables.get(idx.table.name);
401
401
  if (!table)
402
402
  continue;
403
- const columns = idx.columns.map((c) => table.columns.get(c.name)).filter((c) => c !== undefined);
404
- const clonedIdx = {
405
- ...idx,
406
- table,
407
- columns,
408
- };
409
- clone.addIndex(clonedIdx);
403
+ clone.addIndex({ ...idx, table });
410
404
  }
411
405
  return clone;
412
406
  }
@@ -442,7 +436,7 @@ export class SchemaAST {
442
436
  })),
443
437
  indexes: t.indexes.map((i) => ({
444
438
  name: i.name,
445
- columns: i.columns.map((c) => c.name),
439
+ columns: i.entries.map((entry) => entry.column),
446
440
  unique: i.unique,
447
441
  })),
448
442
  })),
@@ -12,7 +12,7 @@ import type { ForeignKeyAction } from './types.js';
12
12
  /**
13
13
  * Options for building SchemaAST from entities.
14
14
  */
15
- export interface BuildFromEntitiesOptions {
15
+ export interface BuildSchemaASTOptions {
16
16
  /** Custom table name resolver */
17
17
  resolveTableName?: (entity: Type<unknown>, meta: EntityMeta<unknown>) => string;
18
18
  /** Custom column name resolver */
@@ -23,62 +23,9 @@ export interface BuildFromEntitiesOptions {
23
23
  defaultForeignKeyAction?: ForeignKeyAction;
24
24
  }
25
25
  /**
26
- * Builds SchemaAST instances from various sources.
26
+ * Build a SchemaAST from entity classes (decorated with `@Entity`, `@Field`, etc.).
27
+ *
28
+ * Three passes, because each needs the one before it to have finished for *every* entity: a relation
29
+ * resolves against a table another entity declares, and an index against the columns of its own.
27
30
  */
28
- export declare class SchemaASTBuilder {
29
- private readonly namingStrategy?;
30
- private readonly defaultForeignKeyAction;
31
- private ast;
32
- constructor(namingStrategy?: NamingStrategy | undefined, defaultForeignKeyAction?: ForeignKeyAction);
33
- /**
34
- * Reset the builder for a new schema.
35
- */
36
- reset(): this;
37
- /**
38
- * Get the built AST.
39
- */
40
- getAST(): SchemaAST;
41
- /**
42
- * Build AST from entity classes (decorated with @Entity, @Field, etc.)
43
- */
44
- fromEntities(entities: readonly Type<unknown>[], options?: BuildFromEntitiesOptions): SchemaAST;
45
- /**
46
- * Resolve the canonical type for a field, inheriting from the referenced
47
- * entity's primary key when the field is a foreign-key reference
48
- * (`@Field({ references: () => SomeEntity })`) with no explicit type of its
49
- * own.
50
- *
51
- * Without this, a field like `creatorId?: UUID` (a bare TypeScript alias for
52
- * `string`, erased at runtime) falls back to the generic string inference in
53
- * {@link fieldOptionsToCanonical} and gets typed as TEXT/VARCHAR - producing a
54
- * foreign key column whose type doesn't match the UUID primary key it
55
- * references, which Postgres (and most databases) reject outright.
56
- *
57
- * `field.typeFromReference` (set by `defineField`, see entity/metadata/definition.ts)
58
- * is what distinguishes "no type was given" from "the decorator explicitly set
59
- * a type" - including explicit constructor overrides like `type: BigInt`, which
60
- * a value-based check (e.g. `typeof field.type === 'string'`) would miss since
61
- * reflection also produces constructor values like `String`/`Number`.
62
- * `columnType` remains the unambiguous, always-respected explicit override.
63
- */
64
- private resolveColumnCanonicalType;
65
- /**
66
- * Add a table from entity metadata.
67
- */
68
- private addTableFromEntity;
69
- /**
70
- * Add relationships from entity relation decorators.
71
- */
72
- private addRelationshipsFromEntity;
73
- /**
74
- * Add indexes from field options (`@Field({ index })`) and from `@Index([...])`, which have nothing
75
- * in common beyond their target table.
76
- */
77
- private addIndexesFromEntity;
78
- /**
79
- * One `@Index([...])`. Its entries keep the authored form (expression, prefix length, order) with
80
- * names resolved, so the generator renders exactly what was declared; `columns` is the resolvable
81
- * subset, which is what diffing and introspection compare.
82
- */
83
- private addCompositeIndex;
84
- }
31
+ export declare function buildSchemaAST(entities: readonly Type<unknown>[], options?: BuildSchemaASTOptions): SchemaAST;