@happyvertical/smrt-core 0.40.70 → 0.41.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/AGENTS.md +2 -2
- package/agents/change-feed.md +1 -1
- package/agents/schema-paths.md +487 -29
- package/dist/browser.js +5 -4
- package/dist/cascade.d.ts +120 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +430 -0
- package/dist/cascade.js.map +1 -0
- package/dist/change-feed.d.ts +34 -2
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +52 -11
- package/dist/change-feed.js.map +1 -1
- package/dist/class.d.ts +36 -3
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +59 -5
- package/dist/class.js.map +1 -1
- package/dist/collection-cache.js +0 -0
- package/dist/collection-cache.js.map +1 -1
- package/dist/collection.d.ts +29 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +66 -14
- package/dist/collection.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/decorators/index.d.ts +63 -5
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +37 -5
- package/dist/decorators/index.js.map +1 -1
- package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
- package/dist/dispatch/collections/Dispatches.js +19 -4
- package/dist/dispatch/collections/Dispatches.js.map +1 -1
- package/dist/dispatch/types.d.ts +5 -0
- package/dist/dispatch/types.d.ts.map +1 -1
- package/dist/embedded-write-queue.d.ts +46 -0
- package/dist/embedded-write-queue.d.ts.map +1 -0
- package/dist/embedded-write-queue.js +66 -0
- package/dist/embedded-write-queue.js.map +1 -0
- package/dist/embeddings/storage.d.ts +7 -0
- package/dist/embeddings/storage.d.ts.map +1 -1
- package/dist/embeddings/storage.js +29 -12
- package/dist/embeddings/storage.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/hierarchical.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/interceptors.d.ts +21 -0
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +27 -1
- package/dist/interceptors.js.map +1 -1
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +4 -7
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/static-manifest.js +10 -10
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +19 -19
- package/dist/migrations/differ.d.ts +9 -1
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +10 -5
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -1
- package/dist/migrations/minor-units.d.ts +162 -0
- package/dist/migrations/minor-units.d.ts.map +1 -0
- package/dist/migrations/minor-units.js +381 -0
- package/dist/migrations/minor-units.js.map +1 -0
- package/dist/migrations.js +2 -1
- package/dist/object.d.ts +98 -5
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +138 -30
- package/dist/object.js.map +1 -1
- package/dist/query-bounds.d.ts +5 -1
- package/dist/query-bounds.d.ts.map +1 -1
- package/dist/query-bounds.js +5 -1
- package/dist/query-bounds.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +3 -1
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/manifest-field-merge.d.ts +12 -0
- package/dist/registry/manifest-field-merge.d.ts.map +1 -1
- package/dist/registry/manifest-field-merge.js +14 -2
- package/dist/registry/manifest-field-merge.js.map +1 -1
- package/dist/registry/schema-builder.d.ts +22 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +205 -165
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/types.d.ts +3 -2
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +41 -46
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +61 -83
- package/dist/registry.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +45 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +92 -28
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/schema/conflict-target.d.ts +104 -0
- package/dist/schema/conflict-target.d.ts.map +1 -0
- package/dist/schema/conflict-target.js +129 -0
- package/dist/schema/conflict-target.js.map +1 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.js +2 -1
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/postgres-strategy.js +12 -1
- package/dist/schema/ddl/postgres-strategy.js.map +1 -1
- package/dist/schema/generator.d.ts +252 -16
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +411 -106
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/index-utils.d.ts +120 -0
- package/dist/schema/index-utils.d.ts.map +1 -1
- package/dist/schema/index-utils.js +242 -1
- package/dist/schema/index-utils.js.map +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -2
- package/dist/schema/live-parity.d.ts.map +1 -1
- package/dist/schema/live-parity.js +4 -2
- package/dist/schema/live-parity.js.map +1 -1
- package/dist/schema/system-table-shapes.d.ts +13 -0
- package/dist/schema/system-table-shapes.d.ts.map +1 -1
- package/dist/schema/system-table-shapes.js +14 -1
- package/dist/schema/system-table-shapes.js.map +1 -1
- package/dist/schema/utils.d.ts +1 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +3 -3
- package/dist/schema/utils.js.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/smrt-knowledge.json +9 -9
- package/dist/system/compatibility.d.ts +42 -0
- package/dist/system/compatibility.d.ts.map +1 -1
- package/dist/system/compatibility.js +182 -9
- package/dist/system/compatibility.js.map +1 -1
- package/dist/system/index.d.ts +1 -0
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +3 -2
- package/dist/system/retention.d.ts +237 -0
- package/dist/system/retention.d.ts.map +1 -0
- package/dist/system/retention.js +497 -0
- package/dist/system/retention.js.map +1 -0
- package/dist/system/schema.d.ts +100 -15
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +81 -45
- package/dist/system/schema.js.map +1 -1
- package/dist/system/types.d.ts +0 -2
- package/dist/system/types.d.ts.map +1 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +4 -9
- package/dist/vite-plugin/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -11,6 +11,18 @@ import { RegisteredField } from './types';
|
|
|
11
11
|
* `Map<string, RegisteredField>` they came from.
|
|
12
12
|
*/
|
|
13
13
|
type ManifestFieldInput = FieldDefinition;
|
|
14
|
+
/**
|
|
15
|
+
* Read a schema-shaping field attribute from either placement.
|
|
16
|
+
*
|
|
17
|
+
* `required`, `default`, and `description` are written at the top level by
|
|
18
|
+
* manifest field definitions but are normalized into `_meta` by
|
|
19
|
+
* {@link createFieldFromManifest} / {@link mergeManifestField}. Any consumer
|
|
20
|
+
* that shapes a column from a field must therefore check both placements, or
|
|
21
|
+
* it silently drops NOT NULL and DEFAULT for every registry-sourced field
|
|
22
|
+
* (issue #2372). Top level wins so an explicit manifest value overrides the
|
|
23
|
+
* normalized copy.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readFieldAttribute(fieldDef: ManifestFieldInput, key: 'required' | 'default' | 'description'): unknown;
|
|
14
26
|
export declare function createFieldFromManifest(fieldDef: ManifestFieldInput): RegisteredField;
|
|
15
27
|
export declare function mergeManifestField(existingField: RegisteredField, fieldDef: ManifestFieldInput): RegisteredField;
|
|
16
28
|
export declare function manifestFieldDiffers(existingField: RegisteredField, fieldDef: ManifestFieldInput): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-field-merge.d.ts","sourceRoot":"","sources":["../../src/registry/manifest-field-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;;GASG;AACH,KAAK,kBAAkB,GAAG,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest-field-merge.d.ts","sourceRoot":"","sources":["../../src/registry/manifest-field-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;;GASG;AACH,KAAK,kBAAkB,GAAG,eAAe,CAAC;AAyB1C;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,UAAU,GAAG,SAAS,GAAG,aAAa,WAW5C;AAmBD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,kBAAkB,GAC3B,eAAe,CA0BjB;AAED,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,eAAe,EAC9B,QAAQ,EAAE,kBAAkB,GAC3B,eAAe,CAiCjB;AAED,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,eAAe,EAC9B,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAQT"}
|
|
@@ -6,10 +6,22 @@ function readExistingFieldMeta(existingField, key) {
|
|
|
6
6
|
if (hasOwnDefinedValue(existingField, key)) return existingField[key];
|
|
7
7
|
if (hasOwnDefinedValue(existingField?._meta, key)) return existingField._meta?.[key];
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Read a schema-shaping field attribute from either placement.
|
|
11
|
+
*
|
|
12
|
+
* `required`, `default`, and `description` are written at the top level by
|
|
13
|
+
* manifest field definitions but are normalized into `_meta` by
|
|
14
|
+
* {@link createFieldFromManifest} / {@link mergeManifestField}. Any consumer
|
|
15
|
+
* that shapes a column from a field must therefore check both placements, or
|
|
16
|
+
* it silently drops NOT NULL and DEFAULT for every registry-sourced field
|
|
17
|
+
* (issue #2372). Top level wins so an explicit manifest value overrides the
|
|
18
|
+
* normalized copy.
|
|
19
|
+
*/
|
|
20
|
+
function readFieldAttribute(fieldDef, key) {
|
|
10
21
|
if (hasOwnDefinedValue(fieldDef, key)) return fieldDef[key];
|
|
11
22
|
if (hasOwnDefinedValue(fieldDef?._meta, key)) return fieldDef._meta?.[key];
|
|
12
23
|
}
|
|
24
|
+
var readManifestFieldMeta = readFieldAttribute;
|
|
13
25
|
function assignDefinedMeta(target, source) {
|
|
14
26
|
if (!source) return;
|
|
15
27
|
for (const [key, value] of Object.entries(source)) if (value !== void 0) target[key] = value;
|
|
@@ -62,6 +74,6 @@ function manifestFieldDiffers(existingField, fieldDef) {
|
|
|
62
74
|
return mergedField.type !== existingField.type || JSON.stringify(mergedField._meta || {}) !== JSON.stringify(existingField._meta || {});
|
|
63
75
|
}
|
|
64
76
|
//#endregion
|
|
65
|
-
export { createFieldFromManifest, manifestFieldDiffers, mergeManifestField };
|
|
77
|
+
export { createFieldFromManifest, manifestFieldDiffers, mergeManifestField, readFieldAttribute };
|
|
66
78
|
|
|
67
79
|
//# sourceMappingURL=manifest-field-merge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-field-merge.js","names":[],"sources":["../../src/registry/manifest-field-merge.ts"],"sourcesContent":["import type { FieldDefinition, FieldMeta } from '../scanner/types.js';\nimport type { RegisteredField } from './types';\n\n/**\n * Manifest-sourced field input handled by the merge helpers.\n *\n * Manifest fields are plain {@link FieldDefinition}s. Already-registered\n * runtime fields are the canonical {@link RegisteredField} superset, which\n * extends `FieldDefinition`, so a `FieldDefinition` parameter accepts both\n * shapes as the incoming `fieldDef`. The helpers return the canonical\n * {@link RegisteredField} so merged fields stay assignable back into the\n * `Map<string, RegisteredField>` they came from.\n */\ntype ManifestFieldInput = FieldDefinition;\n\nfunction hasOwnDefinedValue(source: object | undefined, key: string): boolean {\n return (\n !!source &&\n Object.hasOwn(source, key) &&\n (source as Record<string, unknown>)[key] !== undefined\n );\n}\n\nfunction readExistingFieldMeta(\n existingField: RegisteredField,\n key: 'required' | 'default' | 'description',\n) {\n if (hasOwnDefinedValue(existingField, key)) {\n return existingField[key];\n }\n\n if (hasOwnDefinedValue(existingField?._meta, key)) {\n return existingField._meta?.[key];\n }\n\n return undefined;\n}\n\
|
|
1
|
+
{"version":3,"file":"manifest-field-merge.js","names":[],"sources":["../../src/registry/manifest-field-merge.ts"],"sourcesContent":["import type { FieldDefinition, FieldMeta } from '../scanner/types.js';\nimport type { RegisteredField } from './types';\n\n/**\n * Manifest-sourced field input handled by the merge helpers.\n *\n * Manifest fields are plain {@link FieldDefinition}s. Already-registered\n * runtime fields are the canonical {@link RegisteredField} superset, which\n * extends `FieldDefinition`, so a `FieldDefinition` parameter accepts both\n * shapes as the incoming `fieldDef`. The helpers return the canonical\n * {@link RegisteredField} so merged fields stay assignable back into the\n * `Map<string, RegisteredField>` they came from.\n */\ntype ManifestFieldInput = FieldDefinition;\n\nfunction hasOwnDefinedValue(source: object | undefined, key: string): boolean {\n return (\n !!source &&\n Object.hasOwn(source, key) &&\n (source as Record<string, unknown>)[key] !== undefined\n );\n}\n\nfunction readExistingFieldMeta(\n existingField: RegisteredField,\n key: 'required' | 'default' | 'description',\n) {\n if (hasOwnDefinedValue(existingField, key)) {\n return existingField[key];\n }\n\n if (hasOwnDefinedValue(existingField?._meta, key)) {\n return existingField._meta?.[key];\n }\n\n return undefined;\n}\n\n/**\n * Read a schema-shaping field attribute from either placement.\n *\n * `required`, `default`, and `description` are written at the top level by\n * manifest field definitions but are normalized into `_meta` by\n * {@link createFieldFromManifest} / {@link mergeManifestField}. Any consumer\n * that shapes a column from a field must therefore check both placements, or\n * it silently drops NOT NULL and DEFAULT for every registry-sourced field\n * (issue #2372). Top level wins so an explicit manifest value overrides the\n * normalized copy.\n */\nexport function readFieldAttribute(\n fieldDef: ManifestFieldInput,\n key: 'required' | 'default' | 'description',\n) {\n if (hasOwnDefinedValue(fieldDef, key)) {\n return fieldDef[key];\n }\n\n if (hasOwnDefinedValue(fieldDef?._meta, key)) {\n return fieldDef._meta?.[key];\n }\n\n return undefined;\n}\n\nconst readManifestFieldMeta = readFieldAttribute;\n\nfunction assignDefinedMeta(\n target: Record<string, unknown>,\n source: Record<string, unknown> | undefined,\n) {\n if (!source) {\n return;\n }\n\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined) {\n target[key] = value;\n }\n }\n}\n\nexport function createFieldFromManifest(\n fieldDef: ManifestFieldInput,\n): RegisteredField {\n const meta: FieldMeta = {};\n\n assignDefinedMeta(meta, fieldDef?._meta);\n\n // Write through the open `FieldMeta` index signature: assigning the same\n // loop value to a union of named keys (`default` is `unknown`, the others\n // narrower) collapses the write target to `undefined`, so route by string.\n const metaRecord = meta as Record<string, unknown>;\n for (const key of ['required', 'default', 'description'] as const) {\n const value = readManifestFieldMeta(fieldDef, key);\n if (value !== undefined) {\n metaRecord[key] = value;\n }\n }\n\n // Preserve top-level `related` for relationship-graph lookups\n // (foreignKey / crossPackageRef / oneToMany / manyToMany all rely on it).\n const out: RegisteredField = {\n type: fieldDef.type,\n _meta: meta,\n };\n if (fieldDef.related !== undefined) {\n out.related = fieldDef.related;\n }\n return out;\n}\n\nexport function mergeManifestField(\n existingField: RegisteredField,\n fieldDef: ManifestFieldInput,\n): RegisteredField {\n const hasTenancyMarker =\n existingField.__tenancy?.isTenantIdField ||\n existingField._meta?.__tenancy?.isTenantIdField;\n\n const nextMeta: FieldMeta = {\n ...(existingField._meta || {}),\n };\n\n assignDefinedMeta(nextMeta, fieldDef?._meta);\n\n // See createFieldFromManifest: route writes through the index signature so a\n // shared loop value isn't narrowed to the `undefined`-only union write target.\n const nextMetaRecord = nextMeta as Record<string, unknown>;\n for (const key of ['required', 'default', 'description'] as const) {\n const manifestValue = readManifestFieldMeta(fieldDef, key);\n if (manifestValue !== undefined) {\n nextMetaRecord[key] = manifestValue;\n continue;\n }\n\n const existingValue = readExistingFieldMeta(existingField, key);\n if (existingValue !== undefined && nextMetaRecord[key] === undefined) {\n nextMetaRecord[key] = existingValue;\n }\n }\n\n return {\n ...existingField,\n type:\n !hasTenancyMarker && fieldDef.type ? fieldDef.type : existingField.type,\n _meta: nextMeta,\n };\n}\n\nexport function manifestFieldDiffers(\n existingField: RegisteredField,\n fieldDef: ManifestFieldInput,\n): boolean {\n const mergedField = mergeManifestField(existingField, fieldDef);\n\n return (\n mergedField.type !== existingField.type ||\n JSON.stringify(mergedField._meta || {}) !==\n JSON.stringify(existingField._meta || {})\n );\n}\n"],"mappings":";AAeA,SAAS,mBAAmB,QAA4B,KAAsB;CAC5E,OACE,CAAC,CAAC,UACF,OAAO,OAAO,QAAQ,GAAG,KACxB,OAAmC,SAAS,KAAA;AAEjD;AAEA,SAAS,sBACP,eACA,KACA;CACA,IAAI,mBAAmB,eAAe,GAAG,GACvC,OAAO,cAAc;CAGvB,IAAI,mBAAmB,eAAe,OAAO,GAAG,GAC9C,OAAO,cAAc,QAAQ;AAIjC;;;;;;;;;;;;AAaA,SAAgB,mBACd,UACA,KACA;CACA,IAAI,mBAAmB,UAAU,GAAG,GAClC,OAAO,SAAS;CAGlB,IAAI,mBAAmB,UAAU,OAAO,GAAG,GACzC,OAAO,SAAS,QAAQ;AAI5B;AAEA,IAAM,wBAAwB;AAE9B,SAAS,kBACP,QACA,QACA;CACA,IAAI,CAAC,QACH;CAGF,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,GAC9C,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO;AAGpB;AAEA,SAAgB,wBACd,UACiB;CACjB,MAAM,OAAkB,CAAC;CAEzB,kBAAkB,MAAM,UAAU,KAAK;CAKvC,MAAM,aAAa;CACnB,KAAK,MAAM,OAAO;EAAC;EAAY;EAAW;CAAa,GAAY;EACjE,MAAM,QAAQ,sBAAsB,UAAU,GAAG;EACjD,IAAI,UAAU,KAAA,GACZ,WAAW,OAAO;CAEtB;CAIA,MAAM,MAAuB;EAC3B,MAAM,SAAS;EACf,OAAO;CACT;CACA,IAAI,SAAS,YAAY,KAAA,GACvB,IAAI,UAAU,SAAS;CAEzB,OAAO;AACT;AAEA,SAAgB,mBACd,eACA,UACiB;CACjB,MAAM,mBACJ,cAAc,WAAW,mBACzB,cAAc,OAAO,WAAW;CAElC,MAAM,WAAsB,EAC1B,GAAI,cAAc,SAAS,CAAC,EAC9B;CAEA,kBAAkB,UAAU,UAAU,KAAK;CAI3C,MAAM,iBAAiB;CACvB,KAAK,MAAM,OAAO;EAAC;EAAY;EAAW;CAAa,GAAY;EACjE,MAAM,gBAAgB,sBAAsB,UAAU,GAAG;EACzD,IAAI,kBAAkB,KAAA,GAAW;GAC/B,eAAe,OAAO;GACtB;EACF;EAEA,MAAM,gBAAgB,sBAAsB,eAAe,GAAG;EAC9D,IAAI,kBAAkB,KAAA,KAAa,eAAe,SAAS,KAAA,GACzD,eAAe,OAAO;CAE1B;CAEA,OAAO;EACL,GAAG;EACH,MACE,CAAC,oBAAoB,SAAS,OAAO,SAAS,OAAO,cAAc;EACrE,OAAO;CACT;AACF;AAEA,SAAgB,qBACd,eACA,UACS;CACT,MAAM,cAAc,mBAAmB,eAAe,QAAQ;CAE9D,OACE,YAAY,SAAS,cAAc,QACnC,KAAK,UAAU,YAAY,SAAS,CAAC,CAAC,MACpC,KAAK,UAAU,cAAc,SAAS,CAAC,CAAC;AAE9C"}
|
|
@@ -101,17 +101,38 @@ export declare function generateDDLFromColumns(tableName: string, columns: Recor
|
|
|
101
101
|
* @returns Formatted SQL default value
|
|
102
102
|
*/
|
|
103
103
|
export declare function formatDefaultValue(value: unknown, type: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* Options for {@link fieldsToColumns}.
|
|
106
|
+
*/
|
|
107
|
+
export interface FieldsToColumnsOptions {
|
|
108
|
+
/**
|
|
109
|
+
* Shape the columns for a single-table-inheritance table.
|
|
110
|
+
*
|
|
111
|
+
* An STI table holds the union of every subtype's fields, so a column that
|
|
112
|
+
* only one subtype declares must stay nullable — rows of sibling subtypes
|
|
113
|
+
* never populate it. This matches `generateSTISchemaFromManifest`, which
|
|
114
|
+
* emits `notNull: false` for every non-system column on an STI table.
|
|
115
|
+
* Declared defaults are still emitted.
|
|
116
|
+
*/
|
|
117
|
+
stiUnionColumns?: boolean;
|
|
118
|
+
}
|
|
104
119
|
/**
|
|
105
120
|
* Convert a Map of field definitions to column definitions
|
|
106
121
|
*
|
|
107
122
|
* Used by getAllSchemas() to generate columns from fields when a class
|
|
108
123
|
* has no pre-generated schema (e.g., STI subclasses registered from manifest).
|
|
109
124
|
*
|
|
125
|
+
* `required`, `default`, and `description` are read from either the top level
|
|
126
|
+
* or `_meta`: registry-sourced fields normalize them into `_meta`, so reading
|
|
127
|
+
* only the top level dropped NOT NULL and DEFAULT for every such field
|
|
128
|
+
* (#2372).
|
|
129
|
+
*
|
|
110
130
|
* @param fields - Map of field name to field definition
|
|
131
|
+
* @param options - Table-shape options (see {@link FieldsToColumnsOptions})
|
|
111
132
|
* @returns Record of column name to column definition
|
|
112
133
|
* @private
|
|
113
134
|
*/
|
|
114
|
-
export declare function fieldsToColumns(fields: Map<string, FieldDefinition
|
|
135
|
+
export declare function fieldsToColumns(fields: Map<string, FieldDefinition>, options?: FieldsToColumnsOptions): Record<string, ColumnDefinition>;
|
|
115
136
|
/**
|
|
116
137
|
* Map field type to SQL data type
|
|
117
138
|
* @private
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../../src/registry/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../../src/registry/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAM7D,OAAO,KAAK,EACV,gBAAgB,EAEhB,gBAAgB,EAChB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAmL5B;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAIpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkB7D;AAwTD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAmFxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAsC7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACzC,KAAK,UAAQ,EACb,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,CAwDR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EACpC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAwFlC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC,GACjC,WAAW,CAqBb"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { classnameToTablename, toSnakeCase } from "../utils/naming.js";
|
|
1
2
|
import { formatDefaultValue as formatDefaultValue$1, quoteIdentifier } from "../schema/sql-identifiers.js";
|
|
3
|
+
import { shortenIdentifier } from "../schema/index-utils.js";
|
|
2
4
|
import { getDDLStrategy } from "../schema/ddl/index.js";
|
|
3
5
|
import { getClasses, getCollectionTableNames } from "./shared-state.js";
|
|
4
6
|
import { findClass } from "./name-resolver.js";
|
|
5
|
-
import {
|
|
7
|
+
import { conflictIndexName } from "../schema/conflict-target.js";
|
|
6
8
|
import "../utils.js";
|
|
9
|
+
import { readFieldAttribute } from "./manifest-field-merge.js";
|
|
7
10
|
import { isCollectionRegistration } from "./collection-resolution.js";
|
|
8
11
|
import { ObjectRegistry } from "../registry.js";
|
|
9
12
|
//#region src/registry/schema-builder.ts
|
|
@@ -34,10 +37,10 @@ function applyDecoratorSqlTypeOverrides(className, columns) {
|
|
|
34
37
|
}
|
|
35
38
|
return columns;
|
|
36
39
|
}
|
|
37
|
-
function mergeRuntimeFieldColumns(className, schemaColumns, fields) {
|
|
40
|
+
function mergeRuntimeFieldColumns(className, schemaColumns, fields, options) {
|
|
38
41
|
const columnsToUse = { ...schemaColumns || {} };
|
|
39
42
|
if (fields.size > 0) {
|
|
40
|
-
const fieldColumns = fieldsToColumns(fields);
|
|
43
|
+
const fieldColumns = fieldsToColumns(fields, options);
|
|
41
44
|
for (const [columnName, columnDef] of Object.entries(fieldColumns)) if (!columnsToUse[columnName]) columnsToUse[columnName] = columnDef;
|
|
42
45
|
for (const [fieldName, fieldDef] of fields) {
|
|
43
46
|
const columnName = toSnakeCase(fieldName);
|
|
@@ -60,24 +63,58 @@ function getReferenceKind(fieldDef) {
|
|
|
60
63
|
if (fieldDef.type === "foreignKey") return "foreignKey";
|
|
61
64
|
if (fieldDef.type === "crossPackageRef") return "crossPackageRef";
|
|
62
65
|
}
|
|
63
|
-
function shouldEmitDefault(fieldDef, sqlType) {
|
|
64
|
-
return !(getReferenceKind(fieldDef) === "tenantId" && sqlType === "UUID" &&
|
|
66
|
+
function shouldEmitDefault(fieldDef, sqlType, defaultValue) {
|
|
67
|
+
return !(getReferenceKind(fieldDef) === "tenantId" && sqlType === "UUID" && defaultValue === "");
|
|
65
68
|
}
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Fallback base columns for a table whose contributing class carries no
|
|
71
|
+
* manifest columns.
|
|
72
|
+
*
|
|
73
|
+
* These mirror what the manifest schema generators emit for the same table
|
|
74
|
+
* (`generateSchemaFromManifest` / `generateSTISchemaFromManifest`), so a table
|
|
75
|
+
* assembled from runtime field metadata alone has the same NOT NULL and
|
|
76
|
+
* DEFAULT shape as one assembled from a manifest. Divergence here is what made
|
|
77
|
+
* the merged table shape depend on which class registered first (#2372).
|
|
78
|
+
*/
|
|
79
|
+
function createBaseColumns(registered, isSTI) {
|
|
80
|
+
const baseColumns = {
|
|
68
81
|
id: {
|
|
69
82
|
type: registered.config?.idType === "text" ? "TEXT" : "UUID",
|
|
70
83
|
primaryKey: true,
|
|
84
|
+
notNull: true,
|
|
71
85
|
referenceKind: "id"
|
|
72
86
|
},
|
|
73
87
|
slug: {
|
|
74
88
|
type: "TEXT",
|
|
75
89
|
notNull: true
|
|
76
90
|
},
|
|
77
|
-
context: {
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
context: {
|
|
92
|
+
type: "TEXT",
|
|
93
|
+
notNull: true,
|
|
94
|
+
defaultValue: ""
|
|
95
|
+
},
|
|
96
|
+
created_at: {
|
|
97
|
+
type: "TIMESTAMP",
|
|
98
|
+
notNull: true,
|
|
99
|
+
defaultValue: "current_timestamp"
|
|
100
|
+
},
|
|
101
|
+
updated_at: {
|
|
102
|
+
type: "TIMESTAMP",
|
|
103
|
+
notNull: true,
|
|
104
|
+
defaultValue: "current_timestamp"
|
|
105
|
+
}
|
|
80
106
|
};
|
|
107
|
+
if (isSTI) {
|
|
108
|
+
baseColumns._meta_type = {
|
|
109
|
+
type: "TEXT",
|
|
110
|
+
notNull: true
|
|
111
|
+
};
|
|
112
|
+
baseColumns._meta_data = {
|
|
113
|
+
type: "JSON",
|
|
114
|
+
notNull: false
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return baseColumns;
|
|
81
118
|
}
|
|
82
119
|
/**
|
|
83
120
|
* Get cached schema definition for a registered class
|
|
@@ -139,14 +176,151 @@ function getTableName(name) {
|
|
|
139
176
|
}
|
|
140
177
|
function withConflictIndex(tableName, columns, indexes, conflictColumns) {
|
|
141
178
|
if (conflictColumns.length === 0 || !conflictColumns.every((column) => columns[column])) return indexes;
|
|
179
|
+
const primaryKeyColumns = Object.entries(columns).filter(([, column]) => column.primaryKey === true).map(([name]) => name);
|
|
180
|
+
if (primaryKeyColumns.length > 0 && primaryKeyColumns.length === conflictColumns.length && primaryKeyColumns.every((column) => conflictColumns.includes(column))) return indexes;
|
|
142
181
|
if (indexes.some((index) => index.unique === true && !index.where && !index.jsonPath && index.columns.length === conflictColumns.length && index.columns.every((column) => conflictColumns.includes(column)))) return indexes;
|
|
143
|
-
const
|
|
144
|
-
const name =
|
|
145
|
-
|
|
182
|
+
const tenantColumn = Object.entries(columns).find(([, column]) => column.referenceKind === "tenantId")?.[0];
|
|
183
|
+
const name = shortenIdentifier(conflictIndexName(tableName, conflictColumns, tenantColumn));
|
|
184
|
+
const conflictIndex = {
|
|
146
185
|
name,
|
|
147
186
|
columns: conflictColumns,
|
|
148
187
|
unique: true
|
|
149
|
-
}
|
|
188
|
+
};
|
|
189
|
+
if (indexes.some((index) => index.name === name)) return indexes.map((index) => index.name === name ? conflictIndex : index);
|
|
190
|
+
return [...indexes, conflictIndex];
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Resolve the physical table a registered class writes to.
|
|
194
|
+
*
|
|
195
|
+
* STI subclasses are folded onto their base's table. Returns `undefined` when
|
|
196
|
+
* the class has no table (for example an unresolvable manifest stub).
|
|
197
|
+
*
|
|
198
|
+
* R5-canon: STI lookups use the qualified key so a colliding simple name in
|
|
199
|
+
* another package cannot yield the wrong table strategy or STI base and move
|
|
200
|
+
* this class's columns under that other package's table.
|
|
201
|
+
*/
|
|
202
|
+
function resolveContributorTable(registered, fallbackName) {
|
|
203
|
+
const simpleName = registered.name || fallbackName;
|
|
204
|
+
const qualifiedName = registered.qualifiedName ?? simpleName;
|
|
205
|
+
if (!registered.schema?.tableName && registered.extends) {
|
|
206
|
+
if (ObjectRegistry.getTableStrategy(qualifiedName) === "sti") {
|
|
207
|
+
const stiBaseName = ObjectRegistry.getSTIBase(qualifiedName);
|
|
208
|
+
if (stiBaseName && stiBaseName !== qualifiedName) {
|
|
209
|
+
const stiBaseClass = findClass(stiBaseName);
|
|
210
|
+
if (stiBaseClass?.schema?.tableName) {
|
|
211
|
+
if (!registered.schema) registered.schema = {
|
|
212
|
+
tableName: "",
|
|
213
|
+
ddl: "",
|
|
214
|
+
columns: {},
|
|
215
|
+
indexes: [],
|
|
216
|
+
triggers: [],
|
|
217
|
+
foreignKeys: [],
|
|
218
|
+
dependencies: [],
|
|
219
|
+
version: ""
|
|
220
|
+
};
|
|
221
|
+
registered.schema.tableName = stiBaseClass.schema.tableName;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (!registered.schema?.tableName) return;
|
|
227
|
+
let tableName = registered.schema.tableName;
|
|
228
|
+
const isSTI = ObjectRegistry.getTableStrategy(qualifiedName) === "sti";
|
|
229
|
+
let isSTIBase = isSTI;
|
|
230
|
+
let conflictKey = qualifiedName;
|
|
231
|
+
if (isSTI) {
|
|
232
|
+
const stiBaseName = ObjectRegistry.getSTIBase(qualifiedName);
|
|
233
|
+
if (stiBaseName) {
|
|
234
|
+
conflictKey = stiBaseName;
|
|
235
|
+
if (stiBaseName !== qualifiedName) {
|
|
236
|
+
isSTIBase = false;
|
|
237
|
+
const stiBaseClass = findClass(stiBaseName);
|
|
238
|
+
if (stiBaseClass?.schema?.tableName) tableName = stiBaseClass.schema.tableName;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
tableName,
|
|
244
|
+
contributor: {
|
|
245
|
+
registered,
|
|
246
|
+
simpleName,
|
|
247
|
+
qualifiedName,
|
|
248
|
+
isSTI,
|
|
249
|
+
isSTIBase,
|
|
250
|
+
depth: ObjectRegistry.getInheritanceChain(qualifiedName).length,
|
|
251
|
+
conflictKey
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Order the classes that share one table deterministically.
|
|
257
|
+
*
|
|
258
|
+
* The first contributor seeds the table: it supplies the base columns, the
|
|
259
|
+
* `idType`, the conflict columns and the cached DDL, and its columns win every
|
|
260
|
+
* merge conflict. Registration order must not decide that, or the same
|
|
261
|
+
* hierarchy yields a different table shape depending on which class a manifest
|
|
262
|
+
* happens to list first — child-first dropped NOT NULL and DEFAULT from the
|
|
263
|
+
* base columns (#2372).
|
|
264
|
+
*
|
|
265
|
+
* Order: the STI base first, then ancestors before descendants, then by
|
|
266
|
+
* qualified name so the result is a total order.
|
|
267
|
+
*/
|
|
268
|
+
function sortTableContributors(contributors) {
|
|
269
|
+
return [...contributors].sort((a, b) => {
|
|
270
|
+
if (a.isSTIBase !== b.isSTIBase) return a.isSTIBase ? -1 : 1;
|
|
271
|
+
if (a.depth !== b.depth) return a.depth - b.depth;
|
|
272
|
+
return a.qualifiedName.localeCompare(b.qualifiedName);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Assemble every physical table from the classes that contribute to it.
|
|
277
|
+
*
|
|
278
|
+
* Shared by {@link getAllSchemas} and {@link getAllSchemasAsDefinitions} so
|
|
279
|
+
* both produce the same merged shape. The result depends only on what is
|
|
280
|
+
* registered, never on the order it was registered in.
|
|
281
|
+
*/
|
|
282
|
+
function buildMergedTableSchemas() {
|
|
283
|
+
const contributorsByTable = /* @__PURE__ */ new Map();
|
|
284
|
+
for (const [className, registered] of getClasses()) {
|
|
285
|
+
if (isCollectionRegistration(className, registered, collectionRegistrationLookup)) continue;
|
|
286
|
+
const resolved = resolveContributorTable(registered, className);
|
|
287
|
+
if (!resolved) continue;
|
|
288
|
+
const existing = contributorsByTable.get(resolved.tableName);
|
|
289
|
+
if (existing) existing.push(resolved.contributor);
|
|
290
|
+
else contributorsByTable.set(resolved.tableName, [resolved.contributor]);
|
|
291
|
+
}
|
|
292
|
+
const tableSchemas = {};
|
|
293
|
+
for (const [tableName, contributors] of contributorsByTable) for (const contributor of sortTableContributors(contributors)) {
|
|
294
|
+
const { registered, simpleName, isSTI } = contributor;
|
|
295
|
+
const columnsToUse = mergeRuntimeFieldColumns(simpleName, registered.schema?.columns, registered.fields, { stiUnionColumns: isSTI });
|
|
296
|
+
let tableSchema = tableSchemas[tableName];
|
|
297
|
+
if (!tableSchema) {
|
|
298
|
+
tableSchema = {
|
|
299
|
+
tableName,
|
|
300
|
+
columns: {
|
|
301
|
+
...createBaseColumns(registered, isSTI),
|
|
302
|
+
...columnsToUse
|
|
303
|
+
},
|
|
304
|
+
indexes: [],
|
|
305
|
+
ddl: registered.schema?.ddl || "",
|
|
306
|
+
isSTI,
|
|
307
|
+
conflictColumns: ObjectRegistry.getConflictColumns(contributor.conflictKey)
|
|
308
|
+
};
|
|
309
|
+
tableSchemas[tableName] = tableSchema;
|
|
310
|
+
} else for (const [colName, colDef] of Object.entries(columnsToUse)) if (!tableSchema.columns[colName]) tableSchema.columns[colName] = colDef;
|
|
311
|
+
const schemaIndexes = registered.schema?.indexes;
|
|
312
|
+
if (schemaIndexes && schemaIndexes.length > 0) {
|
|
313
|
+
const existingNames = new Set(tableSchema.indexes.map((index) => index.name));
|
|
314
|
+
for (const index of schemaIndexes) {
|
|
315
|
+
if (typeof index === "string") continue;
|
|
316
|
+
if (!existingNames.has(index.name)) {
|
|
317
|
+
tableSchema.indexes.push(index);
|
|
318
|
+
existingNames.add(index.name);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return tableSchemas;
|
|
150
324
|
}
|
|
151
325
|
/**
|
|
152
326
|
* Get all pre-generated schemas for explicit adapter bootstrap paths.
|
|
@@ -164,81 +338,7 @@ function withConflictIndex(tableName, columns, indexes, conflictColumns) {
|
|
|
164
338
|
* ```
|
|
165
339
|
*/
|
|
166
340
|
function getAllSchemas(engine) {
|
|
167
|
-
const tableSchemas =
|
|
168
|
-
for (const [_className, registered] of getClasses()) {
|
|
169
|
-
if (isCollectionRegistration(_className, registered, collectionRegistrationLookup)) continue;
|
|
170
|
-
const simpleName = registered.name || _className;
|
|
171
|
-
if (!registered.schema?.tableName && registered.extends) {
|
|
172
|
-
const qualifiedName = registered.qualifiedName ?? simpleName;
|
|
173
|
-
const lookupKey = qualifiedName;
|
|
174
|
-
if (ObjectRegistry.getTableStrategy(lookupKey) === "sti") {
|
|
175
|
-
const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);
|
|
176
|
-
if (stiBaseName && stiBaseName !== qualifiedName) {
|
|
177
|
-
const stiBaseClass = findClass(stiBaseName);
|
|
178
|
-
if (stiBaseClass?.schema?.tableName) {
|
|
179
|
-
if (!registered.schema) registered.schema = {
|
|
180
|
-
tableName: "",
|
|
181
|
-
ddl: "",
|
|
182
|
-
columns: {},
|
|
183
|
-
indexes: [],
|
|
184
|
-
triggers: [],
|
|
185
|
-
foreignKeys: [],
|
|
186
|
-
dependencies: [],
|
|
187
|
-
version: ""
|
|
188
|
-
};
|
|
189
|
-
registered.schema.tableName = stiBaseClass.schema.tableName;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (registered.schema?.tableName) {
|
|
195
|
-
let tableName = registered.schema.tableName;
|
|
196
|
-
const qualifiedName = registered.qualifiedName ?? simpleName;
|
|
197
|
-
const lookupKey = qualifiedName;
|
|
198
|
-
const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);
|
|
199
|
-
if (tableStrategy === "sti") {
|
|
200
|
-
const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);
|
|
201
|
-
if (stiBaseName && stiBaseName !== qualifiedName) {
|
|
202
|
-
const stiBaseClass = findClass(stiBaseName);
|
|
203
|
-
if (stiBaseClass?.schema?.tableName) tableName = stiBaseClass.schema.tableName;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
const columnsToUse = mergeRuntimeFieldColumns(simpleName, registered.schema.columns, registered.fields);
|
|
207
|
-
if (!tableSchemas[tableName]) {
|
|
208
|
-
const baseColumns = createBaseColumns(registered);
|
|
209
|
-
const isSTI = tableStrategy === "sti";
|
|
210
|
-
if (isSTI) {
|
|
211
|
-
baseColumns._meta_type = {
|
|
212
|
-
type: "TEXT",
|
|
213
|
-
notNull: true,
|
|
214
|
-
defaultValue: ""
|
|
215
|
-
};
|
|
216
|
-
baseColumns._meta_data = { type: "JSON" };
|
|
217
|
-
}
|
|
218
|
-
tableSchemas[tableName] = {
|
|
219
|
-
tableName,
|
|
220
|
-
columns: {
|
|
221
|
-
...baseColumns,
|
|
222
|
-
...columnsToUse
|
|
223
|
-
},
|
|
224
|
-
indexes: [],
|
|
225
|
-
ddl: registered.schema.ddl || "",
|
|
226
|
-
isSTI,
|
|
227
|
-
conflictColumns: ObjectRegistry.getConflictColumns(isSTI ? ObjectRegistry.getSTIBase(lookupKey) || lookupKey : lookupKey)
|
|
228
|
-
};
|
|
229
|
-
} else for (const [colName, colDef] of Object.entries(columnsToUse)) if (!tableSchemas[tableName].columns[colName]) tableSchemas[tableName].columns[colName] = colDef;
|
|
230
|
-
if (registered.schema.indexes && registered.schema.indexes.length > 0) {
|
|
231
|
-
const existingNames = new Set(tableSchemas[tableName].indexes.map((idx) => typeof idx === "string" ? idx : idx.name));
|
|
232
|
-
for (const idx of registered.schema.indexes) {
|
|
233
|
-
const indexName = typeof idx === "string" ? idx : idx.name;
|
|
234
|
-
if (!existingNames.has(indexName)) if (typeof idx === "string") {} else {
|
|
235
|
-
tableSchemas[tableName].indexes.push(idx);
|
|
236
|
-
existingNames.add(idx.name);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
341
|
+
const tableSchemas = buildMergedTableSchemas();
|
|
242
342
|
const schemas = {};
|
|
243
343
|
for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {
|
|
244
344
|
const engineIndexes = engine ? withConflictIndex(tableName, tableSchema.columns, tableSchema.indexes, tableSchema.conflictColumns) : tableSchema.indexes;
|
|
@@ -285,77 +385,7 @@ function getAllSchemas(engine) {
|
|
|
285
385
|
* @returns Map of tableName to SchemaDefinition
|
|
286
386
|
*/
|
|
287
387
|
function getAllSchemasAsDefinitions() {
|
|
288
|
-
const tableSchemas =
|
|
289
|
-
for (const [_className, registered] of getClasses()) {
|
|
290
|
-
if (isCollectionRegistration(_className, registered, collectionRegistrationLookup)) continue;
|
|
291
|
-
const simpleName = registered.name || _className;
|
|
292
|
-
if (!registered.schema?.tableName && registered.extends) {
|
|
293
|
-
const qualifiedName = registered.qualifiedName ?? simpleName;
|
|
294
|
-
const lookupKey = qualifiedName;
|
|
295
|
-
if (ObjectRegistry.getTableStrategy(lookupKey) === "sti") {
|
|
296
|
-
const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);
|
|
297
|
-
if (stiBaseName && stiBaseName !== qualifiedName) {
|
|
298
|
-
const stiBaseClass = findClass(stiBaseName);
|
|
299
|
-
if (stiBaseClass?.schema?.tableName) {
|
|
300
|
-
if (!registered.schema) registered.schema = {
|
|
301
|
-
tableName: "",
|
|
302
|
-
ddl: "",
|
|
303
|
-
columns: {},
|
|
304
|
-
indexes: [],
|
|
305
|
-
triggers: [],
|
|
306
|
-
foreignKeys: [],
|
|
307
|
-
dependencies: [],
|
|
308
|
-
version: ""
|
|
309
|
-
};
|
|
310
|
-
registered.schema.tableName = stiBaseClass.schema.tableName;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (registered.schema?.tableName) {
|
|
316
|
-
let tableName = registered.schema.tableName;
|
|
317
|
-
const qualifiedName = registered.qualifiedName ?? simpleName;
|
|
318
|
-
const lookupKey = qualifiedName;
|
|
319
|
-
const tableStrategy = ObjectRegistry.getTableStrategy(lookupKey);
|
|
320
|
-
if (tableStrategy === "sti") {
|
|
321
|
-
const stiBaseName = ObjectRegistry.getSTIBase(lookupKey);
|
|
322
|
-
if (stiBaseName && stiBaseName !== qualifiedName) {
|
|
323
|
-
const stiBaseClass = findClass(stiBaseName);
|
|
324
|
-
if (stiBaseClass?.schema?.tableName) tableName = stiBaseClass.schema.tableName;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
const columnsToUse = mergeRuntimeFieldColumns(simpleName, registered.schema.columns, registered.fields);
|
|
328
|
-
if (!tableSchemas[tableName]) {
|
|
329
|
-
const baseColumns = createBaseColumns(registered);
|
|
330
|
-
const isSTI = tableStrategy === "sti";
|
|
331
|
-
if (isSTI) {
|
|
332
|
-
baseColumns._meta_type = {
|
|
333
|
-
type: "TEXT",
|
|
334
|
-
notNull: true,
|
|
335
|
-
defaultValue: ""
|
|
336
|
-
};
|
|
337
|
-
baseColumns._meta_data = { type: "JSON" };
|
|
338
|
-
}
|
|
339
|
-
tableSchemas[tableName] = {
|
|
340
|
-
tableName,
|
|
341
|
-
columns: {
|
|
342
|
-
...baseColumns,
|
|
343
|
-
...columnsToUse
|
|
344
|
-
},
|
|
345
|
-
indexes: [],
|
|
346
|
-
isSTI,
|
|
347
|
-
conflictColumns: ObjectRegistry.getConflictColumns(isSTI ? ObjectRegistry.getSTIBase(lookupKey) || lookupKey : lookupKey)
|
|
348
|
-
};
|
|
349
|
-
} else for (const [colName, colDef] of Object.entries(columnsToUse)) if (!tableSchemas[tableName].columns[colName]) tableSchemas[tableName].columns[colName] = colDef;
|
|
350
|
-
if (registered.schema.indexes && registered.schema.indexes.length > 0) {
|
|
351
|
-
const existingNames = new Set(tableSchemas[tableName].indexes.map((idx) => idx.name));
|
|
352
|
-
for (const idx of registered.schema.indexes) if (!existingNames.has(idx.name)) {
|
|
353
|
-
tableSchemas[tableName].indexes.push(idx);
|
|
354
|
-
existingNames.add(idx.name);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
388
|
+
const tableSchemas = buildMergedTableSchemas();
|
|
359
389
|
const schemas = {};
|
|
360
390
|
for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {
|
|
361
391
|
if (Object.keys(tableSchema.columns).length === 0) continue;
|
|
@@ -427,11 +457,17 @@ function formatDefaultValue(value, type) {
|
|
|
427
457
|
* Used by getAllSchemas() to generate columns from fields when a class
|
|
428
458
|
* has no pre-generated schema (e.g., STI subclasses registered from manifest).
|
|
429
459
|
*
|
|
460
|
+
* `required`, `default`, and `description` are read from either the top level
|
|
461
|
+
* or `_meta`: registry-sourced fields normalize them into `_meta`, so reading
|
|
462
|
+
* only the top level dropped NOT NULL and DEFAULT for every such field
|
|
463
|
+
* (#2372).
|
|
464
|
+
*
|
|
430
465
|
* @param fields - Map of field name to field definition
|
|
466
|
+
* @param options - Table-shape options (see {@link FieldsToColumnsOptions})
|
|
431
467
|
* @returns Record of column name to column definition
|
|
432
468
|
* @private
|
|
433
469
|
*/
|
|
434
|
-
function fieldsToColumns(fields) {
|
|
470
|
+
function fieldsToColumns(fields, options) {
|
|
435
471
|
const columns = {};
|
|
436
472
|
for (const [fieldName, fieldDef] of fields) {
|
|
437
473
|
if (fieldName === "id" || fieldName === "created_at" || fieldName === "updated_at" || fieldName === "slug" || fieldName === "context") continue;
|
|
@@ -440,14 +476,18 @@ function fieldsToColumns(fields) {
|
|
|
440
476
|
if (fieldDef.type === "meta") continue;
|
|
441
477
|
const sqlType = fieldDef._meta?.sqlType || (fieldDef.type === "crossPackageRef" && (fieldDef._meta?.idType === "text" || fieldDef.idType === "text") ? "TEXT" : mapFieldTypeToSQL(fieldDef.type));
|
|
442
478
|
const normalizedSqlType = String(sqlType).toUpperCase();
|
|
479
|
+
const referenceKind = getReferenceKind(fieldDef);
|
|
480
|
+
const required = readFieldAttribute(fieldDef, "required");
|
|
481
|
+
const defaultValue = readFieldAttribute(fieldDef, "default");
|
|
482
|
+
const description = readFieldAttribute(fieldDef, "description");
|
|
443
483
|
const column = {
|
|
444
484
|
type: normalizedSqlType,
|
|
445
|
-
referenceKind
|
|
446
|
-
notNull: fieldDef._meta?.nullable ? false :
|
|
485
|
+
referenceKind,
|
|
486
|
+
notNull: options?.stiUnionColumns ? false : fieldDef._meta?.nullable ? false : Boolean(required),
|
|
447
487
|
unique: fieldDef._meta?.unique || false,
|
|
448
|
-
description
|
|
488
|
+
description
|
|
449
489
|
};
|
|
450
|
-
if (
|
|
490
|
+
if (defaultValue !== void 0 && shouldEmitDefault(fieldDef, normalizedSqlType, defaultValue)) column.defaultValue = defaultValue;
|
|
451
491
|
if (fieldDef.type === "foreignKey" && fieldDef.related) {
|
|
452
492
|
const [table, columnName = "id"] = fieldDef.related.split(".");
|
|
453
493
|
const fieldMeta = fieldDef._meta;
|