@happyvertical/smrt-core 0.47.2 → 0.49.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/generators.md +12 -0
- package/agents/object-runtime.md +7 -0
- package/agents/schema-paths.md +62 -0
- package/agents/system-diagnostics.md +6 -1
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +9 -6
- package/dist/cascade.js.map +1 -1
- package/dist/collection.d.ts +0 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +35 -45
- package/dist/collection.js.map +1 -1
- package/dist/decorators/compatibility.d.ts +1 -1
- package/dist/decorators/compatibility.d.ts.map +1 -1
- package/dist/decorators/compatibility.js +2 -2
- package/dist/decorators/compatibility.js.map +1 -1
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +33 -24
- package/dist/decorators/index.js.map +1 -1
- package/dist/interceptors.d.ts +3 -1
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +3 -2
- package/dist/interceptors.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/migrations/differ.d.ts +10 -4
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +173 -5
- package/dist/migrations/differ.js.map +1 -1
- package/dist/object.d.ts +1 -5
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +28 -37
- package/dist/object.js.map +1 -1
- package/dist/registry/class-registration.d.ts +2 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +47 -16
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/name-resolver.d.ts.map +1 -1
- package/dist/registry/name-resolver.js +3 -3
- package/dist/registry/name-resolver.js.map +1 -1
- package/dist/registry/relationship-graph.d.ts +2 -0
- package/dist/registry/relationship-graph.d.ts.map +1 -1
- package/dist/registry/relationship-graph.js +60 -5
- package/dist/registry/relationship-graph.js.map +1 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +4 -1
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/shared-state.d.ts +18 -0
- package/dist/registry/shared-state.d.ts.map +1 -1
- package/dist/registry/shared-state.js +25 -1
- package/dist/registry/shared-state.js.map +1 -1
- package/dist/registry/types.d.ts +8 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +32 -5
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +120 -12
- package/dist/registry.js.map +1 -1
- package/dist/relationship-loader.d.ts +6 -0
- package/dist/relationship-loader.d.ts.map +1 -0
- package/dist/relationship-loader.js +35 -0
- package/dist/relationship-loader.js.map +1 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +6 -3
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/schema/foreign-key-ddl.d.ts +8 -0
- package/dist/schema/foreign-key-ddl.d.ts.map +1 -1
- package/dist/schema/foreign-key-ddl.js +12 -1
- package/dist/schema/foreign-key-ddl.js.map +1 -1
- package/dist/schema/foreign-key-orphan-report.d.ts.map +1 -1
- package/dist/schema/foreign-key-orphan-report.js +18 -1
- package/dist/schema/foreign-key-orphan-report.js.map +1 -1
- package/dist/schema/generator.d.ts +2 -0
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +16 -9
- package/dist/schema/generator.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 +74 -0
- package/dist/schema/live-parity.js.map +1 -1
- package/dist/schema/text-cast-probe.d.ts +66 -0
- package/dist/schema/text-cast-probe.d.ts.map +1 -0
- package/dist/schema/text-cast-probe.js +142 -0
- package/dist/schema/text-cast-probe.js.map +1 -0
- package/dist/schema/types.d.ts +28 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +2 -1
- package/dist/schema/utils.js.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/smrt-knowledge.json +11 -11
- package/dist/system/diagnostics.d.ts +22 -0
- package/dist/system/diagnostics.d.ts.map +1 -1
- package/dist/system/diagnostics.js +88 -31
- package/dist/system/diagnostics.js.map +1 -1
- package/dist/test-utils.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -1
- package/dist/utils.js.map +1 -1
- package/dist/vite-plugin/dev-plane-route.d.ts +5 -0
- package/dist/vite-plugin/dev-plane-route.d.ts.map +1 -0
- package/dist/vite-plugin/dev-plane-route.js +91 -0
- package/dist/vite-plugin/dev-plane-route.js.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts +8 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +2 -0
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { formatDefaultValue, quoteIdentifier } from "./sql-identifiers.js";
|
|
2
|
+
//#region src/schema/text-cast-probe.ts
|
|
3
|
+
/**
|
|
4
|
+
* Mask a probed sample value for display in a diagnostic message: keep just
|
|
5
|
+
* enough of the value to recognize it without echoing a full record verbatim
|
|
6
|
+
* into logs/CI output (`created_at`/`_meta_data` values are user data).
|
|
7
|
+
*/
|
|
8
|
+
function maskSampleValue(value) {
|
|
9
|
+
if (value.length === 0) return "(empty)";
|
|
10
|
+
if (value.length <= 6) return `${"•".repeat(value.length)} (length ${value.length})`;
|
|
11
|
+
return `${value.slice(0, 3)}…${value.slice(-3)} (length ${value.length})`;
|
|
12
|
+
}
|
|
13
|
+
/** Session-scoped helper function name; `pg_temp` keeps it off the real schema. */
|
|
14
|
+
var PROBE_FUNCTION = "pg_temp.smrt_probe_cast_ok";
|
|
15
|
+
/**
|
|
16
|
+
* PostgreSQL's "special" date/time input values (see the "Special Values"
|
|
17
|
+
* table in the PostgreSQL docs). Every one of these parses successfully to
|
|
18
|
+
* `timestamptz` — `probeCastSafety`'s real-cast test alone would call a text
|
|
19
|
+
* column literally holding the string `now` or `infinity` "clean" — but
|
|
20
|
+
* casting them evaluates to the *migration's* execution time or an infinite
|
|
21
|
+
* sentinel, silently destroying the original value with no error and no
|
|
22
|
+
* advisory. They must never be treated as safe, regardless of what a bare
|
|
23
|
+
* cast attempt reports.
|
|
24
|
+
*/
|
|
25
|
+
var TIMESTAMPTZ_SPECIAL_VALUES = /* @__PURE__ */ new Set([
|
|
26
|
+
"epoch",
|
|
27
|
+
"infinity",
|
|
28
|
+
"-infinity",
|
|
29
|
+
"now",
|
|
30
|
+
"today",
|
|
31
|
+
"tomorrow",
|
|
32
|
+
"yesterday",
|
|
33
|
+
"allballs"
|
|
34
|
+
]);
|
|
35
|
+
/**
|
|
36
|
+
* A `timestamptz` cast is only safe to apply unattended when the source text
|
|
37
|
+
* already carries an explicit UTC offset (`Z` or `+HH[:MM]`/`-HH[:MM]`) —
|
|
38
|
+
* without one, PostgreSQL interprets the value under the *migrating
|
|
39
|
+
* session's* `TimeZone` setting, so the exact same text can cast to a
|
|
40
|
+
* different instant on a different run/server. That ambiguity is exactly
|
|
41
|
+
* what `--legacy-timezone=UTC` exists to resolve explicitly; the probe must
|
|
42
|
+
* fail closed (not silently guess UTC) so naive values route to that opt-in
|
|
43
|
+
* path instead of being auto-converged.
|
|
44
|
+
*/
|
|
45
|
+
var TIMESTAMPTZ_EXPLICIT_OFFSET_SQL_PATTERN = "^[0-9]{4}-[0-9]{2}-[0-9]{2}([ T][0-9]{2}:[0-9]{2}(:[0-9]{2}(\\.[0-9]+)?)?)?[[:space:]]*(Z|[+-][0-9]{2}(:?[0-9]{2})?)$";
|
|
46
|
+
/**
|
|
47
|
+
* `target` is one of this module's own literal type names — never data —
|
|
48
|
+
* so building the DDL by interpolation is safe; there is no column, table,
|
|
49
|
+
* or row value in this string.
|
|
50
|
+
*/
|
|
51
|
+
function renderCreateProbeFunctionSql() {
|
|
52
|
+
return `CREATE OR REPLACE FUNCTION ${PROBE_FUNCTION}(value text, target_type text) RETURNS boolean LANGUAGE plpgsql AS \$\$ BEGIN IF value IS NULL THEN RETURN true; END IF; IF target_type = 'timestamptz' THEN IF lower(trim(value)) = ANY (ARRAY[${[...TIMESTAMPTZ_SPECIAL_VALUES].map((value) => `'${value}'`).join(",")}]) THEN RETURN false; END IF; IF trim(value) !~* '${TIMESTAMPTZ_EXPLICIT_OFFSET_SQL_PATTERN}' THEN RETURN false; END IF; END IF; EXECUTE format('SELECT %L::%s', value, target_type); RETURN true; EXCEPTION WHEN OTHERS THEN RETURN false; END; \$\$`;
|
|
53
|
+
}
|
|
54
|
+
function renderProbeQuerySql(tableName, columnName, targetType) {
|
|
55
|
+
const column = quoteIdentifier(columnName);
|
|
56
|
+
const table = quoteIdentifier(tableName);
|
|
57
|
+
const isValid = `${PROBE_FUNCTION}(${column}::text, '${targetType}')`;
|
|
58
|
+
return `SELECT count(*) AS invalid_count, min(CASE WHEN NOT ${isValid} THEN ${column}::text END) AS sample_value FROM ${table} WHERE ${column} IS NOT NULL AND NOT ${isValid}`;
|
|
59
|
+
}
|
|
60
|
+
function classifyProbeRows(rows) {
|
|
61
|
+
const count = Number(rows[0]?.invalid_count);
|
|
62
|
+
if (!Number.isFinite(count)) return {
|
|
63
|
+
status: "unavailable",
|
|
64
|
+
reason: "probe returned a non-numeric count"
|
|
65
|
+
};
|
|
66
|
+
if (count === 0) return { status: "clean" };
|
|
67
|
+
const sample = rows[0]?.sample_value;
|
|
68
|
+
return {
|
|
69
|
+
status: "dirty",
|
|
70
|
+
count,
|
|
71
|
+
...typeof sample === "string" ? { sample } : {}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Probe every non-null value of a candidate `text` column for whether it can
|
|
76
|
+
* cast losslessly to `timestamptz` or `jsonb`, using a real, exception-safe
|
|
77
|
+
* cast attempt (not a shape heuristic) so an invalid-calendar timestamp or a
|
|
78
|
+
* structurally-malformed-but-bracket-balanced JSON document is caught the
|
|
79
|
+
* same as an obviously wrong value. Any failure to run the probe (adapter
|
|
80
|
+
* without transaction support, a missing table mid-run, a test double
|
|
81
|
+
* without a realistic response) resolves to `unavailable` — callers must not
|
|
82
|
+
* treat that as "clean"; SMRT never coerces or discards data on an unproven
|
|
83
|
+
* assumption.
|
|
84
|
+
*/
|
|
85
|
+
async function probeCastSafety(db, tableName, columnName, targetType) {
|
|
86
|
+
if (!db.transaction) return {
|
|
87
|
+
status: "unavailable",
|
|
88
|
+
reason: "adapter does not support transactions"
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
return await db.transaction(async (tx) => {
|
|
92
|
+
await tx.query(renderCreateProbeFunctionSql());
|
|
93
|
+
const result = await tx.query(renderProbeQuerySql(tableName, columnName, targetType));
|
|
94
|
+
return classifyProbeRows(Array.isArray(result) ? result : result?.rows ?? []);
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
status: "unavailable",
|
|
99
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Render the one-time PostgreSQL conversion of a legacy `text` column to
|
|
105
|
+
* native `timestamptz`, once {@link probeCastSafety} has confirmed every
|
|
106
|
+
* non-null value casts safely.
|
|
107
|
+
*/
|
|
108
|
+
function renderTimestamptzColumnConversion(tableName, columnName, options = {}) {
|
|
109
|
+
const table = quoteIdentifier(tableName);
|
|
110
|
+
const column = quoteIdentifier(columnName);
|
|
111
|
+
const hasManifestDefault = options.manifestDefaultValue !== void 0;
|
|
112
|
+
const statements = [];
|
|
113
|
+
if (options.hasLiveDefault || hasManifestDefault) statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} DROP DEFAULT`);
|
|
114
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} TYPE timestamptz USING ${column}::timestamptz`);
|
|
115
|
+
if (hasManifestDefault) {
|
|
116
|
+
const formattedDefault = formatDefaultValue(options.manifestDefaultValue, "TIMESTAMP");
|
|
117
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} SET DEFAULT ${formattedDefault}`);
|
|
118
|
+
}
|
|
119
|
+
return statements;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Render the one-time PostgreSQL conversion of a legacy `text` column to
|
|
123
|
+
* native `jsonb`, once {@link probeCastSafety} has confirmed every non-null
|
|
124
|
+
* value casts safely.
|
|
125
|
+
*/
|
|
126
|
+
function renderJsonbColumnConversion(tableName, columnName, options = {}) {
|
|
127
|
+
const table = quoteIdentifier(tableName);
|
|
128
|
+
const column = quoteIdentifier(columnName);
|
|
129
|
+
const hasManifestDefault = options.manifestDefaultValue !== void 0;
|
|
130
|
+
const statements = [];
|
|
131
|
+
if (options.hasLiveDefault || hasManifestDefault) statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} DROP DEFAULT`);
|
|
132
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} TYPE jsonb USING ${column}::jsonb`);
|
|
133
|
+
if (hasManifestDefault) {
|
|
134
|
+
const formattedDefault = formatDefaultValue(options.manifestDefaultValue, "JSON");
|
|
135
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} SET DEFAULT ${formattedDefault}::jsonb`);
|
|
136
|
+
}
|
|
137
|
+
return statements;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
export { maskSampleValue, probeCastSafety, renderJsonbColumnConversion, renderTimestamptzColumnConversion };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=text-cast-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-cast-probe.js","names":[],"sources":["../../src/schema/text-cast-probe.ts"],"sourcesContent":["/**\n * Shared \"probe, then cast\" helpers for text-column type convergence (#2771,\n * #2772).\n *\n * Two drift shapes on a table SMRT itself creates share one problem: a live\n * `text` column holds values that would parse losslessly into the manifest's\n * declared PostgreSQL type (`timestamptz` or `jsonb`), but the differ cannot\n * know that without looking at the data. `probeCastSafety()` answers that with\n * a genuine, exception-safe cast attempt — not a shape heuristic — over every\n * non-null value, and returns one sample so a fail-closed diagnostic can name\n * the offending value without dumping the whole column into JS.\n *\n * PostgreSQL has no `TRY_CAST`/safe-cast builtin before 17 (`pg_input_is_valid`),\n * and this fleet targets 16 (see `.github/workflows/postgres-tests.yml`), so a\n * real per-row safe cast needs PL/pgSQL's exception handling. That handler is\n * created as a session-scoped (`pg_temp`) function, which is why the create\n * statement and the probe query run inside one `db.transaction()` — the two\n * must land on the same physical connection, and a pooled adapter only\n * guarantees that within one transaction. `target_type` is never derived from\n * column data (always the literal `'jsonb'`/`'timestamptz'` this module\n * passes), so the dynamic `EXECUTE format(...)` it builds cannot be steered by\n * a row value; `%L` quotes the probed value as a literal.\n */\n\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { formatDefaultValue, quoteIdentifier } from './sql-identifiers.js';\n\n/** Outcome of a server-side cast-safety probe over one column's non-null values. */\nexport type ShapeProbeResult =\n | { status: 'clean' }\n | { status: 'dirty'; count: number; sample?: string }\n | { status: 'unavailable'; reason: string };\n\n/**\n * Mask a probed sample value for display in a diagnostic message: keep just\n * enough of the value to recognize it without echoing a full record verbatim\n * into logs/CI output (`created_at`/`_meta_data` values are user data).\n */\nexport function maskSampleValue(value: string): string {\n if (value.length === 0) return '(empty)';\n if (value.length <= 6)\n return `${'•'.repeat(value.length)} (length ${value.length})`;\n return `${value.slice(0, 3)}…${value.slice(-3)} (length ${value.length})`;\n}\n\n/** Session-scoped helper function name; `pg_temp` keeps it off the real schema. */\nconst PROBE_FUNCTION = 'pg_temp.smrt_probe_cast_ok';\n\n/**\n * PostgreSQL's \"special\" date/time input values (see the \"Special Values\"\n * table in the PostgreSQL docs). Every one of these parses successfully to\n * `timestamptz` — `probeCastSafety`'s real-cast test alone would call a text\n * column literally holding the string `now` or `infinity` \"clean\" — but\n * casting them evaluates to the *migration's* execution time or an infinite\n * sentinel, silently destroying the original value with no error and no\n * advisory. They must never be treated as safe, regardless of what a bare\n * cast attempt reports.\n */\nconst TIMESTAMPTZ_SPECIAL_VALUES = new Set([\n 'epoch',\n 'infinity',\n '-infinity',\n 'now',\n 'today',\n 'tomorrow',\n 'yesterday',\n 'allballs',\n]);\n\n/**\n * A `timestamptz` cast is only safe to apply unattended when the source text\n * already carries an explicit UTC offset (`Z` or `+HH[:MM]`/`-HH[:MM]`) —\n * without one, PostgreSQL interprets the value under the *migrating\n * session's* `TimeZone` setting, so the exact same text can cast to a\n * different instant on a different run/server. That ambiguity is exactly\n * what `--legacy-timezone=UTC` exists to resolve explicitly; the probe must\n * fail closed (not silently guess UTC) so naive values route to that opt-in\n * path instead of being auto-converged.\n */\n// PostgreSQL's ARE (\"advanced regular expression\") dialect, matched with the\n// case-insensitive `~*` operator server-side — not a JS RegExp, and not\n// derived from one, since the two dialects diverge on escaping/anchoring.\nconst TIMESTAMPTZ_EXPLICIT_OFFSET_SQL_PATTERN =\n '^[0-9]{4}-[0-9]{2}-[0-9]{2}([ T][0-9]{2}:[0-9]{2}(:[0-9]{2}(\\\\.[0-9]+)?)?)?[[:space:]]*(Z|[+-][0-9]{2}(:?[0-9]{2})?)$';\n\n/**\n * `target` is one of this module's own literal type names — never data —\n * so building the DDL by interpolation is safe; there is no column, table,\n * or row value in this string.\n */\nfunction renderCreateProbeFunctionSql(): string {\n const specialValuesArray = [...TIMESTAMPTZ_SPECIAL_VALUES]\n .map((value) => `'${value}'`)\n .join(',');\n return (\n `CREATE OR REPLACE FUNCTION ${PROBE_FUNCTION}(value text, target_type text) ` +\n 'RETURNS boolean LANGUAGE plpgsql AS $$ ' +\n 'BEGIN ' +\n 'IF value IS NULL THEN RETURN true; END IF; ' +\n \"IF target_type = 'timestamptz' THEN \" +\n `IF lower(trim(value)) = ANY (ARRAY[${specialValuesArray}]) THEN RETURN false; END IF; ` +\n `IF trim(value) !~* '${TIMESTAMPTZ_EXPLICIT_OFFSET_SQL_PATTERN}' THEN RETURN false; END IF; ` +\n 'END IF; ' +\n \"EXECUTE format('SELECT %L::%s', value, target_type); \" +\n 'RETURN true; ' +\n 'EXCEPTION WHEN OTHERS THEN RETURN false; ' +\n 'END; $$'\n );\n}\n\nfunction renderProbeQuerySql(\n tableName: string,\n columnName: string,\n targetType: 'timestamptz' | 'jsonb',\n): string {\n const column = quoteIdentifier(columnName);\n const table = quoteIdentifier(tableName);\n const isValid = `${PROBE_FUNCTION}(${column}::text, '${targetType}')`;\n return (\n `SELECT count(*) AS invalid_count, ` +\n `min(CASE WHEN NOT ${isValid} THEN ${column}::text END) AS sample_value ` +\n `FROM ${table} WHERE ${column} IS NOT NULL AND NOT ${isValid}`\n );\n}\n\nfunction classifyProbeRows(\n rows: { invalid_count?: unknown; sample_value?: unknown }[],\n): ShapeProbeResult {\n const count = Number(rows[0]?.invalid_count);\n if (!Number.isFinite(count)) {\n return {\n status: 'unavailable',\n reason: 'probe returned a non-numeric count',\n };\n }\n if (count === 0) return { status: 'clean' };\n const sample = rows[0]?.sample_value;\n return {\n status: 'dirty',\n count,\n ...(typeof sample === 'string' ? { sample } : {}),\n };\n}\n\n/**\n * Probe every non-null value of a candidate `text` column for whether it can\n * cast losslessly to `timestamptz` or `jsonb`, using a real, exception-safe\n * cast attempt (not a shape heuristic) so an invalid-calendar timestamp or a\n * structurally-malformed-but-bracket-balanced JSON document is caught the\n * same as an obviously wrong value. Any failure to run the probe (adapter\n * without transaction support, a missing table mid-run, a test double\n * without a realistic response) resolves to `unavailable` — callers must not\n * treat that as \"clean\"; SMRT never coerces or discards data on an unproven\n * assumption.\n */\nexport async function probeCastSafety(\n db: DatabaseInterface,\n tableName: string,\n columnName: string,\n targetType: 'timestamptz' | 'jsonb',\n): Promise<ShapeProbeResult> {\n if (!db.transaction) {\n return {\n status: 'unavailable',\n reason: 'adapter does not support transactions',\n };\n }\n try {\n return await db.transaction(async (tx) => {\n await tx.query(renderCreateProbeFunctionSql());\n const result = await tx.query(\n renderProbeQuerySql(tableName, columnName, targetType),\n );\n const rows = (Array.isArray(result) ? result : (result?.rows ?? [])) as {\n invalid_count?: unknown;\n sample_value?: unknown;\n }[];\n return classifyProbeRows(rows);\n });\n } catch (error) {\n return {\n status: 'unavailable',\n reason: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Column-conversion options shared by {@link renderTimestamptzColumnConversion}\n * and {@link renderJsonbColumnConversion}.\n *\n * `hasLiveDefault` and `manifestDefaultValue` are independent: PostgreSQL\n * rejects `ALTER COLUMN ... TYPE` outright whenever the column has *any*\n * existing default that cannot be auto-cast to the target type — this is\n * true whether or not the manifest itself declares a default (a legacy\n * `text` column with `DEFAULT ''` blocks the ALTER exactly the same as one\n * the manifest also wants a default on), so `DROP DEFAULT` must be gated on\n * the *live* default. `SET DEFAULT` afterward must be gated on the\n * *manifest* default instead: re-establishing a live-only default the\n * manifest no longer declares would silently resurrect drift\n * `compareColumnConstraints` is supposed to report and let an operator\n * choose to drop (see #2771/#2772 review findings: DROP/TYPE/SET DEFAULT\n * mirrors the pre-existing `generateTypeUpgradeSQL` path, and the DROP gate\n * must key off live state PostgreSQL itself enforces, not manifest intent).\n */\ninterface ColumnConversionOptions {\n hasLiveDefault?: boolean;\n manifestDefaultValue?: unknown;\n}\n\n/**\n * Render the one-time PostgreSQL conversion of a legacy `text` column to\n * native `timestamptz`, once {@link probeCastSafety} has confirmed every\n * non-null value casts safely.\n */\nexport function renderTimestamptzColumnConversion(\n tableName: string,\n columnName: string,\n options: ColumnConversionOptions = {},\n): string[] {\n const table = quoteIdentifier(tableName);\n const column = quoteIdentifier(columnName);\n const hasManifestDefault = options.manifestDefaultValue !== undefined;\n const statements: string[] = [];\n if (options.hasLiveDefault || hasManifestDefault) {\n statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} DROP DEFAULT`);\n }\n statements.push(\n `ALTER TABLE ${table} ALTER COLUMN ${column} TYPE timestamptz USING ${column}::timestamptz`,\n );\n if (hasManifestDefault) {\n const formattedDefault = formatDefaultValue(\n options.manifestDefaultValue,\n 'TIMESTAMP',\n );\n statements.push(\n `ALTER TABLE ${table} ALTER COLUMN ${column} SET DEFAULT ${formattedDefault}`,\n );\n }\n return statements;\n}\n\n/**\n * Render the one-time PostgreSQL conversion of a legacy `text` column to\n * native `jsonb`, once {@link probeCastSafety} has confirmed every non-null\n * value casts safely.\n */\nexport function renderJsonbColumnConversion(\n tableName: string,\n columnName: string,\n options: ColumnConversionOptions = {},\n): string[] {\n const table = quoteIdentifier(tableName);\n const column = quoteIdentifier(columnName);\n const hasManifestDefault = options.manifestDefaultValue !== undefined;\n const statements: string[] = [];\n if (options.hasLiveDefault || hasManifestDefault) {\n statements.push(`ALTER TABLE ${table} ALTER COLUMN ${column} DROP DEFAULT`);\n }\n statements.push(\n `ALTER TABLE ${table} ALTER COLUMN ${column} TYPE jsonb USING ${column}::jsonb`,\n );\n if (hasManifestDefault) {\n const formattedDefault = formatDefaultValue(\n options.manifestDefaultValue,\n 'JSON',\n );\n statements.push(\n `ALTER TABLE ${table} ALTER COLUMN ${column} SET DEFAULT ${formattedDefault}::jsonb`,\n );\n }\n return statements;\n}\n"],"mappings":"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"}
|
package/dist/schema/types.d.ts
CHANGED
|
@@ -339,6 +339,34 @@ export interface SchemaChange {
|
|
|
339
339
|
* carrying an advisory and no `sql`/`sqlStatements` is never executed.
|
|
340
340
|
*/
|
|
341
341
|
advisory?: SchemaChangeAdvisory;
|
|
342
|
+
/**
|
|
343
|
+
* True on an advisory-only `add_foreign_key` change specifically blocked
|
|
344
|
+
* by live orphan child rows (#2748) — as opposed to a type mismatch, a
|
|
345
|
+
* conflicting existing constraint, or an engine that cannot add a foreign
|
|
346
|
+
* key to an existing table. Lets a caller (`db:migrate --null-orphans`)
|
|
347
|
+
* distinguish "repair the data and retry" from every other manual-repair
|
|
348
|
+
* reason without parsing `advisory.message`.
|
|
349
|
+
*/
|
|
350
|
+
orphanBlocked?: boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Present alongside `orphanBlocked: true`: whether the child column
|
|
353
|
+
* allows NULL, matching `renderForeignKeyOrphanRepair()`'s own
|
|
354
|
+
* nullable/not-nullable branch. `false` means the differ's suggested
|
|
355
|
+
* repair SQL is the "Manual repair required" comment, never an
|
|
356
|
+
* executable `UPDATE`.
|
|
357
|
+
*/
|
|
358
|
+
orphanNullable?: boolean;
|
|
359
|
+
/**
|
|
360
|
+
* True on an advisory-only `add_foreign_key` change specifically blocked
|
|
361
|
+
* because this engine cannot express the constraint at all (SQLite
|
|
362
|
+
* requires a table rebuild; DuckDB has no `ALTER TABLE ADD CONSTRAINT`)
|
|
363
|
+
* (#2748). Unlike `orphanBlocked` or a type-conflict advisory, this
|
|
364
|
+
* reason says nothing about the column's own state — a caller (`db:migrate
|
|
365
|
+
* --apply-unblocked`) must not treat the child column as a dependency
|
|
366
|
+
* blocker for it, since no rerun on this engine ever resolves it and doing
|
|
367
|
+
* so would withhold unrelated DDL on that column permanently.
|
|
368
|
+
*/
|
|
369
|
+
engineUnsupported?: boolean;
|
|
342
370
|
/** Generated SQL statement */
|
|
343
371
|
sql?: string;
|
|
344
372
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schema/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/schema/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/schema/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,gBAAgB,CAAC;AAInE,OAAO,EACL,+BAA+B,EAC/B,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,KAAK,sBAAsB,EAC3B,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,4BAA4B,EAC5B,4BAA4B,EAC5B,0BAA0B,EAC1B,yBAAyB,EACzB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAMlC,SAAS,EAAE;IAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACxE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EAC7C,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,cAAc,CAAA;CAAO,mBAsJ1C;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA6Ff"}
|
package/dist/schema/utils.js
CHANGED
|
@@ -22,7 +22,8 @@ import { collectManifestTables, manifestColumnsToDefinitions, manifestIndexesToD
|
|
|
22
22
|
* @returns SQL schema creation statement with CREATE TABLE and CREATE INDEX statements
|
|
23
23
|
*/
|
|
24
24
|
async function generateSchema(ClassType, providedFields, options = {}) {
|
|
25
|
-
const
|
|
25
|
+
const owner = ObjectRegistry.getClassByConstructor(ClassType);
|
|
26
|
+
const className = owner?.qualifiedName ?? owner?.name ?? ClassType.name;
|
|
26
27
|
const tableName = tableNameFromClass(ClassType);
|
|
27
28
|
if (!providedFields || providedFields.size === 0) await ObjectRegistry.ensureManifestLoaded(className);
|
|
28
29
|
const cachedFields = providedFields && providedFields.size > 0 ? providedFields : await ObjectRegistry.getAllFields(className);
|
package/dist/schema/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../src/schema/utils.ts"],"sourcesContent":["/**\n * Schema generation utilities - Node.js only\n *\n * These functions use SchemaGenerator which depends on node:crypto.\n * Separated from main utils.ts to prevent bundling in browser builds.\n *\n * SMRT handles ALL database maintenance directly.\n * SDK SQL remains a pure query/CRUD layer.\n */\n\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type { SmrtObject } from '../object.js';\nimport { ObjectRegistry } from '../registry.js';\nimport type { FieldDefinition } from '../scanner/types.js';\nimport { tableNameFromClass } from '../utils.js';\nimport { type DatabaseEngine, detectEngine } from './ddl/index.js';\nimport { schemaDependenciesForEngine } from './foreign-key-ddl.js';\nimport { SchemaManager } from './schema-manager.js';\n\nexport {\n materializeManifestDDLForEngine,\n tokenizeSQLDDLBody,\n} from './ddl/materialize-manifest.js';\n// Index-rendering helpers live in a separate file to avoid pulling the\n// heavyweight registry/collection module graph into the DDL strategies.\nexport {\n assertIdentifierFits,\n identifierByteLength,\n isJsonPathIndex,\n isStiSubtypeUniqueIndex,\n MAX_IDENTIFIER_BYTES,\n renderIndexTarget,\n shortenIdentifier,\n} from './index-utils.js';\n// Structured manifest → executable DDL helpers (#2358). Re-exported here for\n// `@happyvertical/smrt-vitest`, which already imports this subpath.\nexport {\n type CollectedManifestTable,\n collectManifestTables,\n type ManifestColumnLike,\n type ManifestIndexLike,\n type ManifestSchemaLike,\n manifestColumnsToDefinitions,\n manifestIndexesToDefinitions,\n manifestSchemaToDefinition,\n mergeSchemaDefinitionInto,\n renderCollectedManifestTable,\n} from './manifest-schema.js';\n\n/**\n * Generates a complete database schema SQL statement for a class\n *\n * This is a thin wrapper around SchemaGenerator that provides the\n * single source of truth for schema generation. Uses ObjectRegistry\n * cached fields from AST manifest for consistent schema generation.\n *\n * **Note**: Uses dynamic import for SchemaGenerator to avoid bundling\n * Node.js-only code (node:crypto) in browser builds.\n *\n * @param ClassType - Class constructor to generate schema for\n * @param providedFields - Optional fields map (used during registration)\n * @returns SQL schema creation statement with CREATE TABLE and CREATE INDEX statements\n */\nexport async function generateSchema(\n // Accepts any SmrtObject constructor: the registry-stored `typeof SmrtObject`\n // as well as collection item classes whose construct signature is narrower\n // than the full static surface. Only `.name` and the construct identity are\n // used here. `never[]` keeps the construct signature contravariantly\n // compatible with constructors that declare their own argument shapes.\n ClassType: { new (...args: never[]): SmrtObject; readonly name: string },\n providedFields?: Map<string, FieldDefinition>,\n options: { engine?: DatabaseEngine } = {},\n) {\n const className = ClassType.name;\n const tableName = tableNameFromClass(ClassType);\n\n // For external packages, ensure manifest is loaded before proceeding\n if (!providedFields || providedFields.size === 0) {\n await ObjectRegistry.ensureManifestLoaded(className);\n }\n\n // Use provided fields if available AND non-empty (during registration), otherwise get from registry\n // NEW: Use getAllFields() to include inherited fields from parent classes\n const cachedFields =\n providedFields && providedFields.size > 0\n ? providedFields\n : await ObjectRegistry.getAllFields(className);\n\n // Throw error if no fields found\n if (cachedFields.size === 0) {\n // Detect if running in test environment\n const testGlobals = globalThis as {\n describe?: unknown;\n it?: unknown;\n };\n const isTestEnv =\n process.env.NODE_ENV === 'test' ||\n process.env.VITEST === 'true' ||\n typeof testGlobals.describe !== 'undefined' ||\n typeof testGlobals.it !== 'undefined';\n\n const testHint = isTestEnv\n ? `\\n\\n⚠️ Are you using 'smrt test'? ` +\n `Tests require manifest generation.\\n` +\n ` ✅ Use: smrt test\\n` +\n ` ❌ NOT: npx vitest\\n`\n : '';\n\n // Check if class is actually registered (decorator ran but no fields loaded)\n const isRegistered = ObjectRegistry.hasClass(className);\n\n if (isRegistered) {\n // Class registered but no fields - manifest problem\n throw new Error(\n `No field metadata found for class '${className}'. ` +\n `The class is registered (decorator ran) but has no field definitions. ` +\n `This usually means the manifest file is missing or stale.` +\n testHint,\n );\n } else {\n // Class not registered - decorator never ran\n throw new Error(\n `Cannot generate schema for unregistered class '${className}'. ` +\n `Ensure the class is decorated with @smrt() for schema generation to work. ` +\n `Runtime introspection has been removed per issue #131.` +\n testHint,\n );\n }\n }\n\n // Check if class uses STI strategy\n const tableStrategy = ObjectRegistry.getTableStrategy(className);\n\n // Dynamic import SchemaGenerator (Node.js-only, uses node:crypto)\n // This prevents bundling it into browser builds\n const { SchemaGenerator } = await import('./generator.js');\n const generator = new SchemaGenerator();\n const registeredClass = ObjectRegistry.getClass(className);\n // Every key the generator reads from `@smrt()` config must be listed here:\n // this bag is rebuilt by hand rather than passed through, so an unlisted\n // option is silently unreachable at runtime while still appearing in the\n // manifest. `indexes` (#2357) was exactly that. `conflictColumns` is the\n // RESOLVED conflict target, not the raw decorator option: for a\n // tenant-scoped class with no explicit `conflictColumns` the registry\n // derives `[tenant_id, ...natural key]` (#2360), and `save()` upserts on\n // exactly that, so the unique index generated here must match it.\n const runtimeSchemaConfig = registeredClass?.config\n ? {\n conflictColumns: ObjectRegistry.getConflictColumns(className),\n idType: registeredClass.config.idType,\n indexes: registeredClass.config.indexes,\n registry: ObjectRegistry,\n }\n : undefined;\n\n let schemaDefinition: Awaited<\n ReturnType<\n InstanceType<typeof SchemaGenerator>['generateSchemaFromRegistry']\n >\n >;\n\n if (tableStrategy === 'sti') {\n // STI: Generate shared table for base class\n const stiBase = ObjectRegistry.getSTIBase(className);\n\n if (!stiBase) {\n throw new Error(\n `STI strategy detected for '${className}' but no STI base class found. ` +\n `This should not happen - please report this bug.`,\n );\n }\n\n // Only generate schema for the base class (not for children).\n // R5-canon: `getSTIBase` returns the qualified name; compare against\n // the qualified form of this class so an STI base isn't\n // mis-classified as a subclass and skipped.\n const qualifiedClassName =\n registeredClass?.qualifiedName ?? registeredClass?.name ?? className;\n if (qualifiedClassName === stiBase || className === stiBase) {\n // This is the base class - generate STI schema\n schemaDefinition = await generator.generateSTISchemaFromRegistry(\n className,\n tableName,\n cachedFields,\n runtimeSchemaConfig,\n );\n } else {\n // This is a child class - return null or empty schema\n // The base class schema already includes all fields\n // Child classes don't need their own tables\n return ''; // Empty SQL - table already created by base class\n }\n } else {\n // CTI: Generate separate table for each class\n schemaDefinition = generator.generateSchemaFromRegistry(\n className,\n tableName,\n cachedFields,\n runtimeSchemaConfig,\n );\n }\n\n // Store the full schema definition in the registry\n // This is critical for:\n // - STI tables where descendants have additional columns (issue #427)\n // - Per-engine DDL generation via SchemaManager\n if (registeredClass) {\n // Store the full SchemaDefinition for SchemaManager to use\n registeredClass.schema = schemaDefinition;\n // Also store generated DDL for backward compatibility\n registeredClass.schema.ddl = generator.generateSQL(\n schemaDefinition,\n 'sqlite',\n );\n }\n\n return generator.generateSQL(schemaDefinition, options.engine);\n}\n\n/**\n * Ensure schema exists for a registered class.\n *\n * This compatibility helper is kept for tooling flows such as CLI commands that\n * explicitly prepare schema ahead of runtime. Core runtime no longer calls this\n * automatically.\n *\n * @deprecated Prefer explicit migration/bootstrap tooling. Runtime verifies\n * schema and fails fast when tables are missing.\n */\nexport async function ensureSchema(\n db: DatabaseInterface,\n className: string,\n): Promise<void> {\n const registered = ObjectRegistry.getClass(className);\n if (!registered) {\n throw new Error(\n `Cannot ensure schema for unregistered class '${className}'. ` +\n `Ensure the class is decorated with @smrt() and registered in the ObjectRegistry.`,\n );\n }\n\n const tableStrategy = ObjectRegistry.getTableStrategy(className);\n if (tableStrategy === 'sti') {\n const stiBase = ObjectRegistry.getSTIBase(className);\n // R5-canon: `getSTIBase` returns the qualified name. Compare\n // against the qualified form of `className` so an STI base isn't\n // mis-classified as a child and recursed on. Falls back to a\n // simple-name compare for classes without a package context.\n const qualifiedClassName =\n registered.qualifiedName ?? registered.name ?? className;\n if (stiBase && stiBase !== qualifiedClassName && stiBase !== className) {\n await ensureSchema(db, stiBase);\n return;\n }\n }\n\n let schemaDefinition = ObjectRegistry.getSchema(className);\n if (tableStrategy === 'sti') {\n const tableName = ObjectRegistry.getTableName(className);\n if (tableName) {\n const mergedSchema =\n ObjectRegistry.getAllSchemasAsDefinitions()[tableName];\n if (mergedSchema) {\n schemaDefinition = mergedSchema;\n }\n }\n }\n\n if (!schemaDefinition?.tableName) {\n const providedFields =\n registered.fields.size > 0 ? registered.fields : undefined;\n await generateSchema(registered.constructor, providedFields);\n schemaDefinition = ObjectRegistry.getSchema(className);\n\n if (tableStrategy === 'sti') {\n const tableName = ObjectRegistry.getTableName(className);\n if (tableName) {\n const mergedSchema =\n ObjectRegistry.getAllSchemasAsDefinitions()[tableName];\n if (mergedSchema) {\n schemaDefinition = mergedSchema;\n }\n }\n }\n }\n\n if (!schemaDefinition?.tableName) {\n throw new Error(\n `No schema definition found for class '${className}'. ` +\n `Run the manifest generation/build step before preparing schema.`,\n );\n }\n\n // Use the merged table definition for shared tables (especially STI).\n // Per-class schema metadata can reflect only the current class, while\n // the database table must include columns from all classes sharing it.\n const mergedSchemaDefinition =\n ObjectRegistry.getAllSchemasAsDefinitions()[schemaDefinition.tableName];\n const effectiveSchemaDefinition = mergedSchemaDefinition ?? schemaDefinition;\n\n // A single requested class can be part of a mutual FK cycle. Build the\n // complete registered dependency closure and let SchemaManager plan it as\n // one unit; creating the requested table alone would leave the first\n // PostgreSQL CREATE referencing a table that does not exist (#2413).\n const allSchemas = ObjectRegistry.getAllSchemasAsDefinitions();\n const engine =\n typeof (db as { exportTable?: unknown }).exportTable === 'function'\n ? 'json'\n : detectEngine(db.url);\n const required = new Map<string, typeof effectiveSchemaDefinition>();\n const collect = (schema: typeof effectiveSchemaDefinition): void => {\n if (required.has(schema.tableName)) return;\n required.set(schema.tableName, schema);\n for (const dependency of schemaDependenciesForEngine(schema, engine)) {\n const dependencySchema = allSchemas[dependency];\n if (dependencySchema) collect(dependencySchema);\n }\n };\n collect(effectiveSchemaDefinition);\n\n const schemaManager = new SchemaManager(db, {\n skipTriggers:\n typeof (db as { exportTable?: unknown }).exportTable === 'function',\n });\n await schemaManager.ensureTables([...required.values()]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../src/schema/utils.ts"],"sourcesContent":["/**\n * Schema generation utilities - Node.js only\n *\n * These functions use SchemaGenerator which depends on node:crypto.\n * Separated from main utils.ts to prevent bundling in browser builds.\n *\n * SMRT handles ALL database maintenance directly.\n * SDK SQL remains a pure query/CRUD layer.\n */\n\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type { SmrtObject } from '../object.js';\nimport type { SmrtObjectConstructor } from '../registry/types.js';\nimport { ObjectRegistry } from '../registry.js';\nimport type { FieldDefinition } from '../scanner/types.js';\nimport { tableNameFromClass } from '../utils.js';\nimport { type DatabaseEngine, detectEngine } from './ddl/index.js';\nimport { schemaDependenciesForEngine } from './foreign-key-ddl.js';\nimport { SchemaManager } from './schema-manager.js';\n\nexport {\n materializeManifestDDLForEngine,\n tokenizeSQLDDLBody,\n} from './ddl/materialize-manifest.js';\n// Index-rendering helpers live in a separate file to avoid pulling the\n// heavyweight registry/collection module graph into the DDL strategies.\nexport {\n assertIdentifierFits,\n identifierByteLength,\n isJsonPathIndex,\n isStiSubtypeUniqueIndex,\n MAX_IDENTIFIER_BYTES,\n renderIndexTarget,\n shortenIdentifier,\n} from './index-utils.js';\n// Structured manifest → executable DDL helpers (#2358). Re-exported here for\n// `@happyvertical/smrt-vitest`, which already imports this subpath.\nexport {\n type CollectedManifestTable,\n collectManifestTables,\n type ManifestColumnLike,\n type ManifestIndexLike,\n type ManifestSchemaLike,\n manifestColumnsToDefinitions,\n manifestIndexesToDefinitions,\n manifestSchemaToDefinition,\n mergeSchemaDefinitionInto,\n renderCollectedManifestTable,\n} from './manifest-schema.js';\n\n/**\n * Generates a complete database schema SQL statement for a class\n *\n * This is a thin wrapper around SchemaGenerator that provides the\n * single source of truth for schema generation. Uses ObjectRegistry\n * cached fields from AST manifest for consistent schema generation.\n *\n * **Note**: Uses dynamic import for SchemaGenerator to avoid bundling\n * Node.js-only code (node:crypto) in browser builds.\n *\n * @param ClassType - Class constructor to generate schema for\n * @param providedFields - Optional fields map (used during registration)\n * @returns SQL schema creation statement with CREATE TABLE and CREATE INDEX statements\n */\nexport async function generateSchema(\n // Accepts any SmrtObject constructor: the registry-stored `typeof SmrtObject`\n // as well as collection item classes whose construct signature is narrower\n // than the full static surface. Only `.name` and the construct identity are\n // used here. `never[]` keeps the construct signature contravariantly\n // compatible with constructors that declare their own argument shapes.\n ClassType: { new (...args: never[]): SmrtObject; readonly name: string },\n providedFields?: Map<string, FieldDefinition>,\n options: { engine?: DatabaseEngine } = {},\n) {\n // Identity-only lookup; no constructor arguments are invoked by the registry.\n const owner = ObjectRegistry.getClassByConstructor(\n ClassType as unknown as SmrtObjectConstructor,\n );\n const className = owner?.qualifiedName ?? owner?.name ?? ClassType.name;\n const tableName = tableNameFromClass(ClassType);\n\n // For external packages, ensure manifest is loaded before proceeding\n if (!providedFields || providedFields.size === 0) {\n await ObjectRegistry.ensureManifestLoaded(className);\n }\n\n // Use provided fields if available AND non-empty (during registration), otherwise get from registry\n // NEW: Use getAllFields() to include inherited fields from parent classes\n const cachedFields =\n providedFields && providedFields.size > 0\n ? providedFields\n : await ObjectRegistry.getAllFields(className);\n\n // Throw error if no fields found\n if (cachedFields.size === 0) {\n // Detect if running in test environment\n const testGlobals = globalThis as {\n describe?: unknown;\n it?: unknown;\n };\n const isTestEnv =\n process.env.NODE_ENV === 'test' ||\n process.env.VITEST === 'true' ||\n typeof testGlobals.describe !== 'undefined' ||\n typeof testGlobals.it !== 'undefined';\n\n const testHint = isTestEnv\n ? `\\n\\n⚠️ Are you using 'smrt test'? ` +\n `Tests require manifest generation.\\n` +\n ` ✅ Use: smrt test\\n` +\n ` ❌ NOT: npx vitest\\n`\n : '';\n\n // Check if class is actually registered (decorator ran but no fields loaded)\n const isRegistered = ObjectRegistry.hasClass(className);\n\n if (isRegistered) {\n // Class registered but no fields - manifest problem\n throw new Error(\n `No field metadata found for class '${className}'. ` +\n `The class is registered (decorator ran) but has no field definitions. ` +\n `This usually means the manifest file is missing or stale.` +\n testHint,\n );\n } else {\n // Class not registered - decorator never ran\n throw new Error(\n `Cannot generate schema for unregistered class '${className}'. ` +\n `Ensure the class is decorated with @smrt() for schema generation to work. ` +\n `Runtime introspection has been removed per issue #131.` +\n testHint,\n );\n }\n }\n\n // Check if class uses STI strategy\n const tableStrategy = ObjectRegistry.getTableStrategy(className);\n\n // Dynamic import SchemaGenerator (Node.js-only, uses node:crypto)\n // This prevents bundling it into browser builds\n const { SchemaGenerator } = await import('./generator.js');\n const generator = new SchemaGenerator();\n const registeredClass = ObjectRegistry.getClass(className);\n // Every key the generator reads from `@smrt()` config must be listed here:\n // this bag is rebuilt by hand rather than passed through, so an unlisted\n // option is silently unreachable at runtime while still appearing in the\n // manifest. `indexes` (#2357) was exactly that. `conflictColumns` is the\n // RESOLVED conflict target, not the raw decorator option: for a\n // tenant-scoped class with no explicit `conflictColumns` the registry\n // derives `[tenant_id, ...natural key]` (#2360), and `save()` upserts on\n // exactly that, so the unique index generated here must match it.\n const runtimeSchemaConfig = registeredClass?.config\n ? {\n conflictColumns: ObjectRegistry.getConflictColumns(className),\n idType: registeredClass.config.idType,\n indexes: registeredClass.config.indexes,\n registry: ObjectRegistry,\n }\n : undefined;\n\n let schemaDefinition: Awaited<\n ReturnType<\n InstanceType<typeof SchemaGenerator>['generateSchemaFromRegistry']\n >\n >;\n\n if (tableStrategy === 'sti') {\n // STI: Generate shared table for base class\n const stiBase = ObjectRegistry.getSTIBase(className);\n\n if (!stiBase) {\n throw new Error(\n `STI strategy detected for '${className}' but no STI base class found. ` +\n `This should not happen - please report this bug.`,\n );\n }\n\n // Only generate schema for the base class (not for children).\n // R5-canon: `getSTIBase` returns the qualified name; compare against\n // the qualified form of this class so an STI base isn't\n // mis-classified as a subclass and skipped.\n const qualifiedClassName =\n registeredClass?.qualifiedName ?? registeredClass?.name ?? className;\n if (qualifiedClassName === stiBase || className === stiBase) {\n // This is the base class - generate STI schema\n schemaDefinition = await generator.generateSTISchemaFromRegistry(\n className,\n tableName,\n cachedFields,\n runtimeSchemaConfig,\n );\n } else {\n // This is a child class - return null or empty schema\n // The base class schema already includes all fields\n // Child classes don't need their own tables\n return ''; // Empty SQL - table already created by base class\n }\n } else {\n // CTI: Generate separate table for each class\n schemaDefinition = generator.generateSchemaFromRegistry(\n className,\n tableName,\n cachedFields,\n runtimeSchemaConfig,\n );\n }\n\n // Store the full schema definition in the registry\n // This is critical for:\n // - STI tables where descendants have additional columns (issue #427)\n // - Per-engine DDL generation via SchemaManager\n if (registeredClass) {\n // Store the full SchemaDefinition for SchemaManager to use\n registeredClass.schema = schemaDefinition;\n // Also store generated DDL for backward compatibility\n registeredClass.schema.ddl = generator.generateSQL(\n schemaDefinition,\n 'sqlite',\n );\n }\n\n return generator.generateSQL(schemaDefinition, options.engine);\n}\n\n/**\n * Ensure schema exists for a registered class.\n *\n * This compatibility helper is kept for tooling flows such as CLI commands that\n * explicitly prepare schema ahead of runtime. Core runtime no longer calls this\n * automatically.\n *\n * @deprecated Prefer explicit migration/bootstrap tooling. Runtime verifies\n * schema and fails fast when tables are missing.\n */\nexport async function ensureSchema(\n db: DatabaseInterface,\n className: string,\n): Promise<void> {\n const registered = ObjectRegistry.getClass(className);\n if (!registered) {\n throw new Error(\n `Cannot ensure schema for unregistered class '${className}'. ` +\n `Ensure the class is decorated with @smrt() and registered in the ObjectRegistry.`,\n );\n }\n\n const tableStrategy = ObjectRegistry.getTableStrategy(className);\n if (tableStrategy === 'sti') {\n const stiBase = ObjectRegistry.getSTIBase(className);\n // R5-canon: `getSTIBase` returns the qualified name. Compare\n // against the qualified form of `className` so an STI base isn't\n // mis-classified as a child and recursed on. Falls back to a\n // simple-name compare for classes without a package context.\n const qualifiedClassName =\n registered.qualifiedName ?? registered.name ?? className;\n if (stiBase && stiBase !== qualifiedClassName && stiBase !== className) {\n await ensureSchema(db, stiBase);\n return;\n }\n }\n\n let schemaDefinition = ObjectRegistry.getSchema(className);\n if (tableStrategy === 'sti') {\n const tableName = ObjectRegistry.getTableName(className);\n if (tableName) {\n const mergedSchema =\n ObjectRegistry.getAllSchemasAsDefinitions()[tableName];\n if (mergedSchema) {\n schemaDefinition = mergedSchema;\n }\n }\n }\n\n if (!schemaDefinition?.tableName) {\n const providedFields =\n registered.fields.size > 0 ? registered.fields : undefined;\n await generateSchema(registered.constructor, providedFields);\n schemaDefinition = ObjectRegistry.getSchema(className);\n\n if (tableStrategy === 'sti') {\n const tableName = ObjectRegistry.getTableName(className);\n if (tableName) {\n const mergedSchema =\n ObjectRegistry.getAllSchemasAsDefinitions()[tableName];\n if (mergedSchema) {\n schemaDefinition = mergedSchema;\n }\n }\n }\n }\n\n if (!schemaDefinition?.tableName) {\n throw new Error(\n `No schema definition found for class '${className}'. ` +\n `Run the manifest generation/build step before preparing schema.`,\n );\n }\n\n // Use the merged table definition for shared tables (especially STI).\n // Per-class schema metadata can reflect only the current class, while\n // the database table must include columns from all classes sharing it.\n const mergedSchemaDefinition =\n ObjectRegistry.getAllSchemasAsDefinitions()[schemaDefinition.tableName];\n const effectiveSchemaDefinition = mergedSchemaDefinition ?? schemaDefinition;\n\n // A single requested class can be part of a mutual FK cycle. Build the\n // complete registered dependency closure and let SchemaManager plan it as\n // one unit; creating the requested table alone would leave the first\n // PostgreSQL CREATE referencing a table that does not exist (#2413).\n const allSchemas = ObjectRegistry.getAllSchemasAsDefinitions();\n const engine =\n typeof (db as { exportTable?: unknown }).exportTable === 'function'\n ? 'json'\n : detectEngine(db.url);\n const required = new Map<string, typeof effectiveSchemaDefinition>();\n const collect = (schema: typeof effectiveSchemaDefinition): void => {\n if (required.has(schema.tableName)) return;\n required.set(schema.tableName, schema);\n for (const dependency of schemaDependenciesForEngine(schema, engine)) {\n const dependencySchema = allSchemas[dependency];\n if (dependencySchema) collect(dependencySchema);\n }\n };\n collect(effectiveSchemaDefinition);\n\n const schemaManager = new SchemaManager(db, {\n skipTriggers:\n typeof (db as { exportTable?: unknown }).exportTable === 'function',\n });\n await schemaManager.ensureTables([...required.values()]);\n}\n"],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizeForeignKeyAction, resolveForeignKeyDeleteAction } from "./schema/foreign-key-policy.js";
|
|
2
|
-
import { foreignKeyConstraintName, renderForeignKeyConstraint, renderForeignKeyConstraintComment, renderForeignKeyConstraintDrop, renderForeignKeyConstraintValidate, schemaForeignKeys, schemaForeignKeysForEngine } from "./schema/foreign-key-ddl.js";
|
|
2
|
+
import { foreignKeyConstraintName, renderForeignKeyAddStatements, renderForeignKeyConstraint, renderForeignKeyConstraintComment, renderForeignKeyConstraintDrop, renderForeignKeyConstraintValidate, schemaForeignKeys, schemaForeignKeysForEngine } from "./schema/foreign-key-ddl.js";
|
|
3
3
|
import { BaseDDLStrategy } from "./schema/ddl/base-strategy.js";
|
|
4
4
|
import { DuckDBStrategy } from "./schema/ddl/duckdb-strategy.js";
|
|
5
5
|
import { JsonDuckDBStrategy } from "./schema/ddl/json-duckdb-strategy.js";
|
|
@@ -16,4 +16,4 @@ import { LiveSchemaParityError, checkLiveSchemaParity, normalizeSqlType, parseIn
|
|
|
16
16
|
import { SchemaCodeGenerator } from "./schema/code-generator.js";
|
|
17
17
|
import { SchemaAggregator } from "./schema/schema-aggregator.js";
|
|
18
18
|
import "./schema/index.js";
|
|
19
|
-
export { BaseDDLStrategy, DuckDBStrategy, JsonDuckDBStrategy, LiveSchemaParityError, PostgresStrategy, SQLiteStrategy, SYSTEM_TABLE_NAMES, SchemaAggregator, SchemaCodeGenerator, SchemaGenerator, SchemaManager, checkLiveSchemaParity, collectForeignKeyOrphanCounts, collectManifestTables, createSchemaManager, detectEngine, foreignKeyConstraintName, generateDDLForEngine, generateMultiEngineDDL, getAllStrategies, getDDLStrategy, getSystemTableShapes, manifestColumnsToDefinitions, manifestIndexesToDefinitions, manifestSchemaToDefinition, mergeSchemaDefinitionInto, normalizeForeignKeyAction, normalizeSqlType, parseIndexDefColumns, parseTableShapes, planForeignKeyCreation, renderCollectedManifestTable, renderForeignKeyConstraint, renderForeignKeyConstraintComment, renderForeignKeyConstraintDrop, renderForeignKeyConstraintValidate, resolveForeignKeyDeleteAction, schemaForeignKeys, schemaForeignKeysForEngine };
|
|
19
|
+
export { BaseDDLStrategy, DuckDBStrategy, JsonDuckDBStrategy, LiveSchemaParityError, PostgresStrategy, SQLiteStrategy, SYSTEM_TABLE_NAMES, SchemaAggregator, SchemaCodeGenerator, SchemaGenerator, SchemaManager, checkLiveSchemaParity, collectForeignKeyOrphanCounts, collectManifestTables, createSchemaManager, detectEngine, foreignKeyConstraintName, generateDDLForEngine, generateMultiEngineDDL, getAllStrategies, getDDLStrategy, getSystemTableShapes, manifestColumnsToDefinitions, manifestIndexesToDefinitions, manifestSchemaToDefinition, mergeSchemaDefinitionInto, normalizeForeignKeyAction, normalizeSqlType, parseIndexDefColumns, parseTableShapes, planForeignKeyCreation, renderCollectedManifestTable, renderForeignKeyAddStatements, renderForeignKeyConstraint, renderForeignKeyConstraintComment, renderForeignKeyConstraintDrop, renderForeignKeyConstraintValidate, resolveForeignKeyDeleteAction, schemaForeignKeys, schemaForeignKeysForEngine };
|