@dbsp/cli 1.1.11 → 2.0.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/dist/{chunk-AQC34IO5.js → chunk-T4ODDHZQ.js} +2 -2
- package/dist/chunk-T4ODDHZQ.js.map +1 -0
- package/dist/{chunk-YGOS4A3O.js → chunk-V3GXFQNB.js} +1 -1
- package/dist/chunk-V3GXFQNB.js.map +1 -0
- package/dist/{chunk-H2K46TNM.js → chunk-VUNVYYOZ.js} +161 -34
- package/dist/chunk-VUNVYYOZ.js.map +1 -0
- package/dist/generators/schema-codegen.d.ts +11 -6
- package/dist/generators/schema-codegen.js +3 -3
- package/dist/index.js +37 -22
- package/dist/index.js.map +1 -1
- package/dist/repl/batch.d.ts +1 -1
- package/dist/repl/batch.js +1 -1
- package/dist/{repl-G56OK3F7.js → repl-PIFVQAHC.js} +2 -2
- package/dist/utils/schema-loader.d.ts +1 -1
- package/dist/utils/schema-loader.js +1 -1
- package/package.json +5 -5
- package/dist/chunk-AQC34IO5.js.map +0 -1
- package/dist/chunk-H2K46TNM.js.map +0 -1
- package/dist/chunk-YGOS4A3O.js.map +0 -1
- /package/dist/{repl-G56OK3F7.js.map → repl-PIFVQAHC.js.map} +0 -0
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// src/generators/schema-codegen.ts
|
|
2
|
+
import {
|
|
3
|
+
canGenerateCreateIndex,
|
|
4
|
+
generateCreateIndex,
|
|
5
|
+
identityNaming
|
|
6
|
+
} from "@dbsp/adapter-pgsql";
|
|
7
|
+
import {
|
|
8
|
+
validateSchemaIndexOptions
|
|
9
|
+
} from "@dbsp/core";
|
|
10
|
+
|
|
1
11
|
// src/utils/db-utils.ts
|
|
2
12
|
async function createDbConnection(connectionUrl) {
|
|
3
13
|
let pg;
|
|
@@ -33,6 +43,9 @@ function snakeToCamelCase(name) {
|
|
|
33
43
|
function singleQuoteEscape(s) {
|
|
34
44
|
return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}'`;
|
|
35
45
|
}
|
|
46
|
+
function blockCommentText(s) {
|
|
47
|
+
return s.replace(/\*\//g, "* /").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
48
|
+
}
|
|
36
49
|
function quoteKey(name) {
|
|
37
50
|
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {
|
|
38
51
|
return name;
|
|
@@ -56,14 +69,14 @@ function generateColumnCode(column, isPrimaryKey, fkInfo, options) {
|
|
|
56
69
|
}
|
|
57
70
|
const canUseShortForm = !isPrimaryKey && !column.nullable && column.default === void 0 && !column.unique;
|
|
58
71
|
if (canUseShortForm) {
|
|
59
|
-
let code2 =
|
|
72
|
+
let code2 = singleQuoteEscape(column.type);
|
|
60
73
|
if (options.includeDbTypeComments && column.originalDbType) {
|
|
61
|
-
code2 += ` /* from: ${column.originalDbType} */`;
|
|
74
|
+
code2 += ` /* from: ${blockCommentText(column.originalDbType)} */`;
|
|
62
75
|
}
|
|
63
76
|
return code2;
|
|
64
77
|
}
|
|
65
78
|
const props = [];
|
|
66
|
-
props.push(`type:
|
|
79
|
+
props.push(`type: ${singleQuoteEscape(column.type)}`);
|
|
67
80
|
if (isPrimaryKey) {
|
|
68
81
|
props.push("primaryKey: true");
|
|
69
82
|
}
|
|
@@ -78,7 +91,7 @@ function generateColumnCode(column, isPrimaryKey, fkInfo, options) {
|
|
|
78
91
|
}
|
|
79
92
|
let code = `{ ${props.join(", ")} }`;
|
|
80
93
|
if (options.includeDbTypeComments && column.originalDbType) {
|
|
81
|
-
code += ` /* from: ${column.originalDbType} */`;
|
|
94
|
+
code += ` /* from: ${blockCommentText(column.originalDbType)} */`;
|
|
82
95
|
}
|
|
83
96
|
return code;
|
|
84
97
|
}
|
|
@@ -118,7 +131,7 @@ function generateRefCode(column, fkInfo, isPrimaryKey, options) {
|
|
|
118
131
|
code = `ref(${singleQuoteEscape(refTable)}, { ${refOptions.join(", ")} })`;
|
|
119
132
|
}
|
|
120
133
|
if (options.includeDbTypeComments && column.originalDbType) {
|
|
121
|
-
code += ` /* from: ${column.originalDbType} */`;
|
|
134
|
+
code += ` /* from: ${blockCommentText(column.originalDbType)} */`;
|
|
122
135
|
}
|
|
123
136
|
return code;
|
|
124
137
|
}
|
|
@@ -178,35 +191,148 @@ function generatedName(name, options) {
|
|
|
178
191
|
function stringArrayLiteral(values) {
|
|
179
192
|
return `[${values.map(singleQuoteEscape).join(", ")}]`;
|
|
180
193
|
}
|
|
194
|
+
function booleanLiteral(value) {
|
|
195
|
+
return value === true ? "true" : "false";
|
|
196
|
+
}
|
|
197
|
+
function stringRecordLiteral(values, keyName = (name) => name) {
|
|
198
|
+
const entries = Object.entries(values).map(
|
|
199
|
+
([key, value]) => `[${singleQuoteEscape(keyName(key))}]: ${singleQuoteEscape(value)}`
|
|
200
|
+
);
|
|
201
|
+
return `{ ${entries.join(", ")} }`;
|
|
202
|
+
}
|
|
181
203
|
function tableLevelForeignKeys(table) {
|
|
182
204
|
return table.foreignKeys.filter(
|
|
183
205
|
(fk) => fk.references.schema !== void 0 || fk.columns.length > 1 || fk.references.columns.length > 1
|
|
184
206
|
);
|
|
185
207
|
}
|
|
208
|
+
function hasExpressions(idx) {
|
|
209
|
+
return (idx.expressions?.length ?? 0) > 0;
|
|
210
|
+
}
|
|
211
|
+
function tableLevelIndexes(table) {
|
|
212
|
+
return table.indexes.filter(
|
|
213
|
+
(idx) => indexRepresentationWarning(table.name, idx) === void 0
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
function warningName(name) {
|
|
217
|
+
return name === void 0 ? "<unnamed>" : JSON.stringify(name);
|
|
218
|
+
}
|
|
219
|
+
function errorMessage(error) {
|
|
220
|
+
return error instanceof Error ? error.message : String(error);
|
|
221
|
+
}
|
|
222
|
+
function indexRepresentationWarning(tableName, idx) {
|
|
223
|
+
if (hasExpressions(idx)) {
|
|
224
|
+
return `Expression index ${warningName(idx.name)} on table ${warningName(tableName)} cannot be represented in the schema and is not managed by dbsp. dbsp will neither drop nor recreate it; maintain it by hand.`;
|
|
225
|
+
}
|
|
226
|
+
const ddlWarning = ddlIndexRepresentationWarning(tableName, idx);
|
|
227
|
+
const schemaWarning = schemaIndexRepresentationWarning(tableName, idx);
|
|
228
|
+
return ddlWarning ?? schemaWarning;
|
|
229
|
+
}
|
|
230
|
+
function ddlIndexRepresentationWarning(tableName, idx) {
|
|
231
|
+
if (canGenerateCreateIndex(tableName, idx, void 0, identityNaming)) {
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
generateCreateIndex(tableName, idx, void 0, identityNaming);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return `Index ${warningName(idx.name)} on table ${warningName(tableName)} cannot be represented in the schema and is not managed by dbsp because the DDL emitter rejected it: ${errorMessage(error)}. dbsp will neither drop nor recreate it; maintain it by hand.`;
|
|
238
|
+
}
|
|
239
|
+
return void 0;
|
|
240
|
+
}
|
|
241
|
+
function schemaIndexRepresentationWarning(tableName, idx) {
|
|
242
|
+
try {
|
|
243
|
+
validateSchemaIndexOptions(tableName, idx);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return `Index ${warningName(idx.name)} on table ${warningName(tableName)} cannot be represented in the schema and is not managed by dbsp because schema() rejected it: ${errorMessage(error)}. dbsp will neither drop nor recreate it; maintain it by hand.`;
|
|
246
|
+
}
|
|
247
|
+
return void 0;
|
|
248
|
+
}
|
|
249
|
+
function schemaCodegenWarnings(model) {
|
|
250
|
+
const warnings = [];
|
|
251
|
+
for (const table of model.tables.values()) {
|
|
252
|
+
for (const idx of table.indexes) {
|
|
253
|
+
const warning = indexRepresentationWarning(table.name, idx);
|
|
254
|
+
if (warning !== void 0) {
|
|
255
|
+
warnings.push(warning);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return warnings;
|
|
260
|
+
}
|
|
261
|
+
function modelWarnings(model) {
|
|
262
|
+
const warnings = model.warnings;
|
|
263
|
+
return Array.isArray(warnings) && warnings.every((w) => typeof w === "string") ? warnings : [];
|
|
264
|
+
}
|
|
265
|
+
function generateIndexCode(idx, options) {
|
|
266
|
+
const indexOptions = [
|
|
267
|
+
`columns: ${stringArrayLiteral(idx.columns.map((col) => generatedName(col, options)))}`
|
|
268
|
+
];
|
|
269
|
+
if (idx.unique !== void 0) {
|
|
270
|
+
indexOptions.push(`unique: ${booleanLiteral(idx.unique)}`);
|
|
271
|
+
}
|
|
272
|
+
if (idx.name !== void 0) {
|
|
273
|
+
indexOptions.push(`name: ${singleQuoteEscape(idx.name)}`);
|
|
274
|
+
}
|
|
275
|
+
if (idx.method !== void 0) {
|
|
276
|
+
indexOptions.push(`method: ${singleQuoteEscape(idx.method)}`);
|
|
277
|
+
}
|
|
278
|
+
if (idx.where !== void 0) {
|
|
279
|
+
indexOptions.push(`where: ${singleQuoteEscape(idx.where)}`);
|
|
280
|
+
}
|
|
281
|
+
if (idx.opclass !== void 0) {
|
|
282
|
+
indexOptions.push(
|
|
283
|
+
`opclass: ${stringRecordLiteral(idx.opclass, (key) => generatedName(key, options))}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (idx.with !== void 0) {
|
|
287
|
+
indexOptions.push(`with: ${stringRecordLiteral(idx.with)}`);
|
|
288
|
+
}
|
|
289
|
+
if (idx.include !== void 0) {
|
|
290
|
+
indexOptions.push(
|
|
291
|
+
`include: ${stringArrayLiteral(idx.include.map((col) => generatedName(col, options)))}`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
if (idx.nullsNotDistinct !== void 0) {
|
|
295
|
+
indexOptions.push(
|
|
296
|
+
`nullsNotDistinct: ${booleanLiteral(idx.nullsNotDistinct)}`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
return ` { ${indexOptions.join(", ")} }`;
|
|
300
|
+
}
|
|
186
301
|
function generateTableConstraintCode(model, options) {
|
|
187
302
|
const tableBlocks = [];
|
|
188
303
|
for (const table of model.tables.values()) {
|
|
189
304
|
const fks = tableLevelForeignKeys(table);
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
refOptions
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
305
|
+
const indexes = tableLevelIndexes(table);
|
|
306
|
+
if (fks.length === 0 && indexes.length === 0) continue;
|
|
307
|
+
const tableOptions = [];
|
|
308
|
+
if (indexes.length > 0) {
|
|
309
|
+
const indexLines = indexes.map((idx) => generateIndexCode(idx, options));
|
|
310
|
+
tableOptions.push(` indexes: [
|
|
311
|
+
${indexLines.join(",\n")}
|
|
312
|
+
]`);
|
|
313
|
+
}
|
|
314
|
+
if (fks.length > 0) {
|
|
315
|
+
const fkLines = fks.map((fk) => {
|
|
316
|
+
const refOptions = [
|
|
317
|
+
...fk.references.schema !== void 0 ? [`schema: ${singleQuoteEscape(fk.references.schema)}`] : [],
|
|
318
|
+
`columns: ${stringArrayLiteral(fk.columns.map((col) => generatedName(col, options)))}`,
|
|
319
|
+
`references: ${stringArrayLiteral(fk.references.columns.map((col) => generatedName(col, options)))}`
|
|
320
|
+
];
|
|
321
|
+
if (fk.onDelete && fk.onDelete !== "NO ACTION") {
|
|
322
|
+
refOptions.push(`onDelete: ${singleQuoteEscape(fk.onDelete)}`);
|
|
323
|
+
}
|
|
324
|
+
if (fk.onUpdate && fk.onUpdate !== "NO ACTION") {
|
|
325
|
+
refOptions.push(`onUpdate: ${singleQuoteEscape(fk.onUpdate)}`);
|
|
326
|
+
}
|
|
327
|
+
return ` ref(${singleQuoteEscape(generatedName(fk.references.table, options))}, { ${refOptions.join(", ")} })`;
|
|
328
|
+
});
|
|
329
|
+
tableOptions.push(` foreignKeys: [
|
|
330
|
+
${fkLines.join(",\n")}
|
|
331
|
+
]`);
|
|
332
|
+
}
|
|
205
333
|
tableBlocks.push(
|
|
206
334
|
` ${quoteKey(generatedName(table.name, options))}: {
|
|
207
|
-
|
|
208
|
-
${fkLines.join(",\n")}
|
|
209
|
-
]
|
|
335
|
+
${tableOptions.join(",\n")}
|
|
210
336
|
}`
|
|
211
337
|
);
|
|
212
338
|
}
|
|
@@ -215,24 +341,22 @@ ${fkLines.join(",\n")}
|
|
|
215
341
|
${tableBlocks.join(",\n\n")}
|
|
216
342
|
}`;
|
|
217
343
|
}
|
|
218
|
-
function
|
|
344
|
+
function generateSchemaFileWithDiagnostics(model, options = {}) {
|
|
345
|
+
const warnings = [...modelWarnings(model), ...schemaCodegenWarnings(model)];
|
|
346
|
+
for (const warning of warnings) {
|
|
347
|
+
options.onWarning?.(warning);
|
|
348
|
+
}
|
|
219
349
|
const lines = [];
|
|
220
350
|
lines.push("/**");
|
|
221
351
|
lines.push(" * Auto-generated by: dbsp introspect");
|
|
222
352
|
if (options.sourceUrl) {
|
|
223
353
|
const redactedUrl = redactDbUrl(options.sourceUrl);
|
|
224
|
-
lines.push(` * Source: ${redactedUrl}`);
|
|
354
|
+
lines.push(` * Source: ${blockCommentText(redactedUrl)}`);
|
|
225
355
|
}
|
|
226
356
|
if (options.introspectedAt) {
|
|
227
357
|
lines.push(` * Generated: ${options.introspectedAt.toISOString()}`);
|
|
228
358
|
}
|
|
229
359
|
lines.push(" *");
|
|
230
|
-
if (options.warnings && options.warnings.length > 0) {
|
|
231
|
-
lines.push(" * \u26A0\uFE0F Warnings:");
|
|
232
|
-
for (const warning of options.warnings) {
|
|
233
|
-
lines.push(` * - ${warning}`);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
360
|
lines.push(" * Review before using in production.");
|
|
237
361
|
lines.push(" */");
|
|
238
362
|
lines.push("");
|
|
@@ -283,12 +407,15 @@ function generateSchemaFile(model, options = {}) {
|
|
|
283
407
|
lines.push(`export const dbCasing = '${options.dbCasing}' as const;`);
|
|
284
408
|
lines.push("");
|
|
285
409
|
}
|
|
286
|
-
return
|
|
410
|
+
return {
|
|
411
|
+
code: lines.join("\n"),
|
|
412
|
+
warnings
|
|
413
|
+
};
|
|
287
414
|
}
|
|
288
415
|
|
|
289
416
|
export {
|
|
290
417
|
createDbConnection,
|
|
291
418
|
redactDbUrl,
|
|
292
|
-
|
|
419
|
+
generateSchemaFileWithDiagnostics
|
|
293
420
|
};
|
|
294
|
-
//# sourceMappingURL=chunk-
|
|
421
|
+
//# sourceMappingURL=chunk-VUNVYYOZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generators/schema-codegen.ts","../src/utils/db-utils.ts"],"sourcesContent":["/**\n * CLI-DDL: Schema Codegen Module\n *\n * Generates TypeScript schema files from IntrospectedModelIR.\n * Used by `dbsp introspect` to create dbsp.schema.ts from a database.\n */\n\nimport {\n\tcanGenerateCreateIndex,\n\tgenerateCreateIndex,\n\tidentityNaming,\n} from '@dbsp/adapter-pgsql';\nimport {\n\ttype ModelIR,\n\ttype TableIR,\n\tvalidateSchemaIndexOptions,\n} from '@dbsp/core';\nimport type { IndexIR } from '@dbsp/types';\nimport { redactDbUrl } from '../utils/db-utils.js';\n\n/**\n * Convert a snake_case string to camelCase.\n * @example 'author_id' → 'authorId'\n */\nfunction snakeToCamelCase(name: string): string {\n\treturn name.replace(/_([a-z])/g, (_, letter: string) => letter.toUpperCase());\n}\n\n/**\n * Options for schema code generation.\n */\nexport interface SchemaCodegenOptions {\n\t/** Source database URL (for comment header) */\n\treadonly sourceUrl?: string;\n\t/** Include original DB types as comments */\n\treadonly includeDbTypeComments?: boolean;\n\t/** Receive diagnostics as they are collected, including model warnings. */\n\treadonly onWarning?: (message: string) => void;\n\t/** Timestamp of introspection */\n\treadonly introspectedAt?: Date;\n\t/**\n\t * Database column casing convention.\n\t * When set to 'snake_case', column names are converted to camelCase\n\t * and a `dbCasing: 'snake_case'` comment is added to the generated code.\n\t * @default 'preserve'\n\t */\n\treadonly dbCasing?: 'snake_case' | 'camelCase' | 'preserve';\n}\n\nexport type SchemaCodegenDiagnosticsOptions = SchemaCodegenOptions;\n\nexport interface SchemaCodegenResult {\n\treadonly code: string;\n\treadonly warnings: readonly string[];\n}\n\n/**\n * Serialize a column default value to valid TypeScript source code.\n *\n * CODEX-11/CODEX-14: String(value) produced \"[object Object]\" for SQL-expression\n * defaults like { sql: 'now()' }, and single-quote wrapping without escape broke\n * strings containing quotes, backslashes, or newlines.\n *\n * Rules:\n * - null/undefined → 'null'\n * - number | boolean → unquoted literal (String(value))\n * - string → singleQuoteEscape (escapes \\, ', \\n, \\r, \\t — produces single-quoted TS literal)\n * - { sql: '...' } shape → `{ sql: ${JSON.stringify(sqlExpr)} }`\n * - anything else → throws (unrecognized shape; prevents silent [object Object])\n */\n\n/**\n * Wrap a string value in single quotes for TypeScript source output,\n * escaping any single quotes and backslashes contained in the value.\n *\n * Examples:\n * 'hello' → \"'hello'\"\n * \"O'Brien\" → \"'O\\\\'Brien'\"\n * 'C:\\\\Users' → \"'C:\\\\\\\\Users'\"\n */\nfunction singleQuoteEscape(s: string): string {\n\treturn `'${s\n\t\t.replace(/\\\\/g, '\\\\\\\\')\n\t\t.replace(/'/g, \"\\\\'\")\n\t\t.replace(/\\n/g, '\\\\n')\n\t\t.replace(/\\r/g, '\\\\r')\n\t\t.replace(/\\t/g, '\\\\t')}'`;\n}\n\n/**\n * Neutralize text that must remain inside generated block comments.\n *\n * Catalog-derived values are otherwise capable of closing `/* ... *\\/` comments\n * and turning the rest of the generated file into executable TypeScript.\n */\nfunction blockCommentText(s: string): string {\n\treturn s\n\t\t.replace(/\\*\\//g, '* /')\n\t\t.replace(/\\n/g, '\\\\n')\n\t\t.replace(/\\r/g, '\\\\r')\n\t\t.replace(/\\t/g, '\\\\t');\n}\n\n/**\n * Return the key suitable for use as a TypeScript object key.\n * Valid JS identifiers can be used bare; anything else is single-quote-wrapped.\n * A valid JS identifier starts with a letter, underscore, or $, followed by\n * letters, digits, underscores, or $.\n */\nfunction quoteKey(name: string): string {\n\tif (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {\n\t\treturn name;\n\t}\n\t// F3: Delegate to singleQuoteEscape() which also handles \\n, \\r, \\t.\n\t// The previous inline chain only escaped \\\\ and ' — identifiers containing\n\t// newlines, CR, or tabs would have produced invalid TypeScript output.\n\treturn singleQuoteEscape(name);\n}\n\nfunction emitDefault(d: unknown): string {\n\tif (d === null || d === undefined) return 'null';\n\tif (typeof d === 'number' || typeof d === 'boolean') return String(d);\n\tif (typeof d === 'string') return singleQuoteEscape(d);\n\tif (\n\t\ttypeof d === 'object' &&\n\t\t'sql' in d &&\n\t\ttypeof (d as { sql: unknown }).sql === 'string'\n\t) {\n\t\treturn `{ sql: ${singleQuoteEscape((d as { sql: string }).sql)} }`;\n\t}\n\tthrow new Error(\n\t\t`[schema-codegen] Unrecognized default shape: ${JSON.stringify(d)}`,\n\t);\n}\n\n/**\n * Generate column definition object code.\n */\nfunction generateColumnCode(\n\tcolumn: TableIR['columns'][number],\n\tisPrimaryKey: boolean,\n\tfkInfo:\n\t\t| {\n\t\t\t\ttable: string;\n\t\t\t\tcolumn?: string;\n\t\t\t\tnullable?: boolean;\n\t\t\t\tunique?: boolean;\n\t\t\t\tonDelete?: string;\n\t\t\t\tonUpdate?: string;\n\t\t\t\tisSelfRef?: boolean;\n\t\t }\n\t\t| undefined,\n\toptions: SchemaCodegenOptions,\n): string {\n\t// CODEX-13: FK + PK overlap — a column can be both FK and PK (e.g. shared-PK 1:1).\n\t// We must preserve isPrimaryKey even when fkInfo is present.\n\tif (fkInfo) {\n\t\treturn generateRefCode(column, fkInfo, isPrimaryKey, options);\n\t}\n\n\t// Check if we can use short form (just 'type' string)\n\tconst canUseShortForm =\n\t\t!isPrimaryKey &&\n\t\t!column.nullable &&\n\t\tcolumn.default === undefined &&\n\t\t!column.unique;\n\n\tif (canUseShortForm) {\n\t\t// Short form: 'type'\n\t\tlet code = singleQuoteEscape(column.type);\n\t\tif (options.includeDbTypeComments && column.originalDbType) {\n\t\t\tcode += ` /* from: ${blockCommentText(column.originalDbType)} */`;\n\t\t}\n\t\treturn code;\n\t}\n\n\t// Long form: { type, primaryKey?, nullable?, default?, unique? }\n\tconst props: string[] = [];\n\tprops.push(`type: ${singleQuoteEscape(column.type)}`);\n\n\tif (isPrimaryKey) {\n\t\tprops.push('primaryKey: true');\n\t}\n\n\tif (column.nullable) {\n\t\tprops.push('nullable: true');\n\t}\n\n\tif (column.default !== undefined) {\n\t\tprops.push(`default: ${emitDefault(column.default)}`);\n\t}\n\n\tif (column.unique) {\n\t\tprops.push('unique: true');\n\t}\n\n\tlet code = `{ ${props.join(', ')} }`;\n\n\tif (options.includeDbTypeComments && column.originalDbType) {\n\t\tcode += ` /* from: ${blockCommentText(column.originalDbType)} */`;\n\t}\n\n\treturn code;\n}\n\n/**\n * Generate ref() call for a foreign key column.\n * ARCH-005: Use ref() instead of references: { table, column }\n */\nfunction generateRefCode(\n\tcolumn: TableIR['columns'][number],\n\tfkInfo: {\n\t\ttable: string;\n\t\tcolumn?: string;\n\t\tnullable?: boolean;\n\t\tunique?: boolean;\n\t\tonDelete?: string;\n\t\tonUpdate?: string;\n\t\tisSelfRef?: boolean;\n\t},\n\tisPrimaryKey: boolean,\n\toptions: SchemaCodegenOptions,\n): string {\n\tconst refOptions: string[] = [];\n\n\t// C2: emit references option for non-PK FK target columns.\n\t// Now that buildRefColumn plumbs options.references into ModelIR,\n\t// emitting ref('table', { references: ['col'] }) round-trips correctly.\n\t// L1-followup-4: apply snake→camel transform to the target column name when\n\t// dbCasing='snake_case' so the generated references array round-trips correctly.\n\tif (fkInfo.column) {\n\t\tconst refColName =\n\t\t\toptions.dbCasing === 'snake_case'\n\t\t\t\t? snakeToCamelCase(fkInfo.column)\n\t\t\t\t: fkInfo.column;\n\t\trefOptions.push(`references: [${singleQuoteEscape(refColName)}]`);\n\t}\n\n\t// CODEX-13: FK column that is also PK — emit isPrimaryKey inside ref() options\n\tif (isPrimaryKey) {\n\t\trefOptions.push('isPrimaryKey: true');\n\t}\n\n\t// Nullable FK\n\tif (fkInfo.nullable || column.nullable) {\n\t\trefOptions.push('nullable: true');\n\t}\n\n\t// Unique FK → hasOne (1:1 relation)\n\tif (fkInfo.unique || column.unique) {\n\t\trefOptions.push('unique: true');\n\t}\n\n\t// onDelete action\n\tif (fkInfo.onDelete && fkInfo.onDelete !== 'NO ACTION') {\n\t\trefOptions.push(`onDelete: ${singleQuoteEscape(fkInfo.onDelete)}`);\n\t}\n\n\t// CODEX-12: onUpdate action\n\tif (fkInfo.onUpdate && fkInfo.onUpdate !== 'NO ACTION') {\n\t\trefOptions.push(`onUpdate: ${singleQuoteEscape(fkInfo.onUpdate)}`);\n\t}\n\n\t// Self-referential FK needs roles\n\tif (fkInfo.isSelfRef) {\n\t\t// Infer role names from column name\n\t\t// e.g., 'parentId' → parent: 'parent', children: 'children'\n\t\tconst baseName = column.name.replace(/_?[iI]d$/, '');\n\t\tconst childrenName = baseName === 'parent' ? 'children' : `${baseName}s`;\n\t\trefOptions.push(\n\t\t\t`roles: { parent: ${singleQuoteEscape(baseName)}, children: ${singleQuoteEscape(childrenName)} }`,\n\t\t);\n\t}\n\n\t// Build the ref() call — table name converted if dbCasing applies.\n\t// Route through singleQuoteEscape so a table name containing a single quote,\n\t// backslash, or newline produces valid TypeScript instead of a syntax error.\n\tconst refTable =\n\t\toptions.dbCasing === 'snake_case'\n\t\t\t? snakeToCamelCase(fkInfo.table)\n\t\t\t: fkInfo.table;\n\tlet code: string;\n\tif (refOptions.length === 0) {\n\t\tcode = `ref(${singleQuoteEscape(refTable)})`;\n\t} else {\n\t\tcode = `ref(${singleQuoteEscape(refTable)}, { ${refOptions.join(', ')} })`;\n\t}\n\n\t// Add comment for original DB type if requested\n\tif (options.includeDbTypeComments && column.originalDbType) {\n\t\tcode += ` /* from: ${blockCommentText(column.originalDbType)} */`;\n\t}\n\n\treturn code;\n}\n\n/**\n * Generate table definition code.\n */\nfunction generateTableCode(\n\ttable: TableIR,\n\toptions: SchemaCodegenOptions,\n): string {\n\t// ARCH-005: Build FK info map with extended properties\n\tconst fkMap = new Map<\n\t\tstring,\n\t\t{\n\t\t\ttable: string;\n\t\t\tcolumn?: string;\n\t\t\tnullable?: boolean;\n\t\t\tunique?: boolean;\n\t\t\tonDelete?: string;\n\t\t\tonUpdate?: string;\n\t\t\tisSelfRef?: boolean;\n\t\t}\n\t>();\n\n\tfor (const fk of table.foreignKeys) {\n\t\tconst localCol = fk.columns[0];\n\t\tconst refCol = fk.references.columns[0];\n\t\tif (\n\t\t\tfk.columns.length === 1 &&\n\t\t\tfk.references.columns.length === 1 &&\n\t\t\tfk.references.schema === undefined &&\n\t\t\tlocalCol &&\n\t\t\trefCol\n\t\t) {\n\t\t\t// Find the column to check nullable/unique\n\t\t\t// FK column names may be snake_case (from raw SQL) while column.name\n\t\t\t// is camelCase (when CamelCasePlugin is active)\n\t\t\tconst colDef = table.columns.find(\n\t\t\t\t(c) => c.name === localCol || c.name === snakeToCamelCase(localCol),\n\t\t\t);\n\n\t\t\tconst entry: {\n\t\t\t\ttable: string;\n\t\t\t\tcolumn?: string;\n\t\t\t\tnullable?: boolean;\n\t\t\t\tunique?: boolean;\n\t\t\t\tonDelete?: string;\n\t\t\t\tonUpdate?: string;\n\t\t\t\tisSelfRef?: boolean;\n\t\t\t} = {\n\t\t\t\ttable: fk.references.table,\n\t\t\t\tisSelfRef:\n\t\t\t\t\tfk.references.schema === undefined &&\n\t\t\t\t\tfk.references.table === table.name,\n\t\t\t};\n\n\t\t\t// Only include column if not 'id' (the default)\n\t\t\tif (refCol !== 'id') {\n\t\t\t\tentry.column = refCol;\n\t\t\t}\n\n\t\t\t// Include nullable if true\n\t\t\tif (colDef?.nullable) {\n\t\t\t\tentry.nullable = true;\n\t\t\t}\n\n\t\t\t// Include unique if true\n\t\t\tif (colDef?.unique) {\n\t\t\t\tentry.unique = true;\n\t\t\t}\n\n\t\t\t// Include onDelete if not the default\n\t\t\tif (fk.onDelete && fk.onDelete !== 'NO ACTION') {\n\t\t\t\tentry.onDelete = fk.onDelete;\n\t\t\t}\n\n\t\t\t// CODEX-12: Include onUpdate if not the default\n\t\t\tif (fk.onUpdate && fk.onUpdate !== 'NO ACTION') {\n\t\t\t\tentry.onUpdate = fk.onUpdate;\n\t\t\t}\n\n\t\t\t// Store under both snake_case and camelCase keys so fkMap.get(col.name) works\n\t\t\t// regardless of naming convention\n\t\t\tfkMap.set(localCol, entry);\n\t\t\tconst camelCol = snakeToCamelCase(localCol);\n\t\t\tif (camelCol !== localCol) {\n\t\t\t\tfkMap.set(camelCol, entry);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst shouldCamelCase = options.dbCasing === 'snake_case';\n\n\tconst columnLines = table.columns.map((col) => {\n\t\tconst isPrimaryKey = table.primaryKey\n\t\t\t? typeof table.primaryKey === 'string'\n\t\t\t\t? col.name === table.primaryKey\n\t\t\t\t: table.primaryKey.includes(col.name)\n\t\t\t: false;\n\t\tconst fkInfo = fkMap.get(col.name);\n\t\tconst code = generateColumnCode(col, isPrimaryKey, fkInfo, options);\n\t\tconst rawColName = shouldCamelCase ? snakeToCamelCase(col.name) : col.name;\n\t\t// C7: quote the key if it's not a valid JS identifier (e.g. contains hyphens)\n\t\tconst colKey = quoteKey(rawColName);\n\t\treturn `\\t\\t${colKey}: ${code}`;\n\t});\n\n\tconst rawTableName = shouldCamelCase\n\t\t? snakeToCamelCase(table.name)\n\t\t: table.name;\n\t// C7: quote the table key if it's not a valid JS identifier\n\tconst tableKey = quoteKey(rawTableName);\n\treturn `\\t${tableKey}: {\\n${columnLines.join(',\\n')},\\n\\t}`;\n}\n\nfunction generatedName(name: string, options: SchemaCodegenOptions): string {\n\treturn options.dbCasing === 'snake_case' ? snakeToCamelCase(name) : name;\n}\n\nfunction stringArrayLiteral(values: readonly string[]): string {\n\treturn `[${values.map(singleQuoteEscape).join(', ')}]`;\n}\n\n/**\n * Emit a strict boolean literal. Interpolating the value itself would run a\n * foreign object's toString into the generated source, which is later imported.\n */\nfunction booleanLiteral(value: unknown): 'true' | 'false' {\n\treturn value === true ? 'true' : 'false';\n}\n\nfunction stringRecordLiteral(\n\tvalues: Readonly<Record<string, string>>,\n\tkeyName: (name: string) => string = (name) => name,\n): string {\n\tconst entries = Object.entries(values).map(\n\t\t([key, value]) =>\n\t\t\t`[${singleQuoteEscape(keyName(key))}]: ${singleQuoteEscape(value)}`,\n\t);\n\treturn `{ ${entries.join(', ')} }`;\n}\n\nfunction tableLevelForeignKeys(table: TableIR): TableIR['foreignKeys'] {\n\treturn table.foreignKeys.filter(\n\t\t(fk) =>\n\t\t\tfk.references.schema !== undefined ||\n\t\t\tfk.columns.length > 1 ||\n\t\t\tfk.references.columns.length > 1,\n\t);\n}\n\nfunction hasExpressions(idx: IndexIR): boolean {\n\treturn (idx.expressions?.length ?? 0) > 0;\n}\n\nfunction tableLevelIndexes(table: TableIR): readonly IndexIR[] {\n\treturn table.indexes.filter(\n\t\t(idx) => indexRepresentationWarning(table.name, idx) === undefined,\n\t);\n}\n\nfunction warningName(name: string | undefined): string {\n\treturn name === undefined ? '<unnamed>' : JSON.stringify(name);\n}\n\nfunction errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nfunction indexRepresentationWarning(\n\ttableName: string,\n\tidx: IndexIR,\n): string | undefined {\n\tif (hasExpressions(idx)) {\n\t\treturn `Expression index ${warningName(idx.name)} on table ${warningName(tableName)} cannot be represented in the schema and is not managed by dbsp. dbsp will neither drop nor recreate it; maintain it by hand.`;\n\t}\n\tconst ddlWarning = ddlIndexRepresentationWarning(tableName, idx);\n\tconst schemaWarning = schemaIndexRepresentationWarning(tableName, idx);\n\treturn ddlWarning ?? schemaWarning;\n}\n\nfunction ddlIndexRepresentationWarning(\n\ttableName: string,\n\tidx: IndexIR,\n): string | undefined {\n\tif (canGenerateCreateIndex(tableName, idx, undefined, identityNaming)) {\n\t\treturn undefined;\n\t}\n\ttry {\n\t\tgenerateCreateIndex(tableName, idx, undefined, identityNaming);\n\t} catch (error) {\n\t\treturn `Index ${warningName(idx.name)} on table ${warningName(tableName)} cannot be represented in the schema and is not managed by dbsp because the DDL emitter rejected it: ${errorMessage(error)}. dbsp will neither drop nor recreate it; maintain it by hand.`;\n\t}\n\treturn undefined;\n}\n\nfunction schemaIndexRepresentationWarning(\n\ttableName: string,\n\tidx: IndexIR,\n): string | undefined {\n\ttry {\n\t\tvalidateSchemaIndexOptions(tableName, idx);\n\t} catch (error) {\n\t\treturn `Index ${warningName(idx.name)} on table ${warningName(tableName)} cannot be represented in the schema and is not managed by dbsp because schema() rejected it: ${errorMessage(error)}. dbsp will neither drop nor recreate it; maintain it by hand.`;\n\t}\n\treturn undefined;\n}\n\nfunction schemaCodegenWarnings(model: ModelIR): string[] {\n\tconst warnings: string[] = [];\n\tfor (const table of model.tables.values()) {\n\t\tfor (const idx of table.indexes) {\n\t\t\tconst warning = indexRepresentationWarning(table.name, idx);\n\t\t\tif (warning !== undefined) {\n\t\t\t\twarnings.push(warning);\n\t\t\t}\n\t\t}\n\t}\n\treturn warnings;\n}\n\nfunction modelWarnings(model: ModelIR): readonly string[] {\n\tconst warnings = (model as ModelIR & { readonly warnings?: unknown })\n\t\t.warnings;\n\treturn Array.isArray(warnings) && warnings.every((w) => typeof w === 'string')\n\t\t? warnings\n\t\t: [];\n}\n\nfunction generateIndexCode(\n\tidx: IndexIR,\n\toptions: SchemaCodegenOptions,\n): string {\n\tconst indexOptions = [\n\t\t`columns: ${stringArrayLiteral(idx.columns.map((col) => generatedName(col, options)))}`,\n\t];\n\tif (idx.unique !== undefined) {\n\t\tindexOptions.push(`unique: ${booleanLiteral(idx.unique)}`);\n\t}\n\tif (idx.name !== undefined) {\n\t\tindexOptions.push(`name: ${singleQuoteEscape(idx.name)}`);\n\t}\n\tif (idx.method !== undefined) {\n\t\tindexOptions.push(`method: ${singleQuoteEscape(idx.method)}`);\n\t}\n\tif (idx.where !== undefined) {\n\t\tindexOptions.push(`where: ${singleQuoteEscape(idx.where)}`);\n\t}\n\tif (idx.opclass !== undefined) {\n\t\tindexOptions.push(\n\t\t\t`opclass: ${stringRecordLiteral(idx.opclass, (key) => generatedName(key, options))}`,\n\t\t);\n\t}\n\tif (idx.with !== undefined) {\n\t\tindexOptions.push(`with: ${stringRecordLiteral(idx.with)}`);\n\t}\n\tif (idx.include !== undefined) {\n\t\tindexOptions.push(\n\t\t\t`include: ${stringArrayLiteral(idx.include.map((col) => generatedName(col, options)))}`,\n\t\t);\n\t}\n\tif (idx.nullsNotDistinct !== undefined) {\n\t\tindexOptions.push(\n\t\t\t`nullsNotDistinct: ${booleanLiteral(idx.nullsNotDistinct)}`,\n\t\t);\n\t}\n\treturn `\\t\\t\\t{ ${indexOptions.join(', ')} }`;\n}\n\nfunction generateTableConstraintCode(\n\tmodel: ModelIR,\n\toptions: SchemaCodegenOptions,\n): string | undefined {\n\tconst tableBlocks: string[] = [];\n\tfor (const table of model.tables.values()) {\n\t\tconst fks = tableLevelForeignKeys(table);\n\t\tconst indexes = tableLevelIndexes(table);\n\t\tif (fks.length === 0 && indexes.length === 0) continue;\n\t\tconst tableOptions: string[] = [];\n\t\tif (indexes.length > 0) {\n\t\t\tconst indexLines = indexes.map((idx) => generateIndexCode(idx, options));\n\t\t\ttableOptions.push(`\\t\\tindexes: [\\n${indexLines.join(',\\n')}\\n\\t\\t]`);\n\t\t}\n\t\tif (fks.length > 0) {\n\t\t\tconst fkLines = fks.map((fk) => {\n\t\t\t\tconst refOptions = [\n\t\t\t\t\t...(fk.references.schema !== undefined\n\t\t\t\t\t\t? [`schema: ${singleQuoteEscape(fk.references.schema)}`]\n\t\t\t\t\t\t: []),\n\t\t\t\t\t`columns: ${stringArrayLiteral(fk.columns.map((col) => generatedName(col, options)))}`,\n\t\t\t\t\t`references: ${stringArrayLiteral(fk.references.columns.map((col) => generatedName(col, options)))}`,\n\t\t\t\t];\n\t\t\t\tif (fk.onDelete && fk.onDelete !== 'NO ACTION') {\n\t\t\t\t\trefOptions.push(`onDelete: ${singleQuoteEscape(fk.onDelete)}`);\n\t\t\t\t}\n\t\t\t\tif (fk.onUpdate && fk.onUpdate !== 'NO ACTION') {\n\t\t\t\t\trefOptions.push(`onUpdate: ${singleQuoteEscape(fk.onUpdate)}`);\n\t\t\t\t}\n\t\t\t\treturn `\\t\\t\\tref(${singleQuoteEscape(generatedName(fk.references.table, options))}, { ${refOptions.join(', ')} })`;\n\t\t\t});\n\t\t\ttableOptions.push(`\\t\\tforeignKeys: [\\n${fkLines.join(',\\n')}\\n\\t\\t]`);\n\t\t}\n\t\ttableBlocks.push(\n\t\t\t`\\t${quoteKey(generatedName(table.name, options))}: {\\n${tableOptions.join(',\\n')}\\n\\t}`,\n\t\t);\n\t}\n\tif (tableBlocks.length === 0) return undefined;\n\treturn `{\\n${tableBlocks.join(',\\n\\n')}\\n}`;\n}\n\n/**\n * Generate a TypeScript schema file from ModelIR and return diagnostics.\n *\n * @param model - The ModelIR (or IntrospectedModelIR) to generate from\n * @param options - Code generation options\n * @returns Generated source code and warnings\n */\nexport function generateSchemaFileWithDiagnostics(\n\tmodel: ModelIR,\n\toptions: SchemaCodegenDiagnosticsOptions = {},\n): SchemaCodegenResult {\n\tconst warnings = [...modelWarnings(model), ...schemaCodegenWarnings(model)];\n\tfor (const warning of warnings) {\n\t\toptions.onWarning?.(warning);\n\t}\n\n\tconst lines: string[] = [];\n\n\t// Header comment\n\tlines.push('/**');\n\tlines.push(' * Auto-generated by: dbsp introspect');\n\tif (options.sourceUrl) {\n\t\tconst redactedUrl = redactDbUrl(options.sourceUrl);\n\t\tlines.push(` * Source: ${blockCommentText(redactedUrl)}`);\n\t}\n\tif (options.introspectedAt) {\n\t\tlines.push(` * Generated: ${options.introspectedAt.toISOString()}`);\n\t}\n\tlines.push(' *');\n\tlines.push(' * Review before using in production.');\n\tlines.push(' */');\n\tlines.push('');\n\n\t// ARCH-005: Check if any table has FKs to determine imports\n\tconst hasForeignKeys = Array.from(model.tables.values()).some(\n\t\t(table) => table.foreignKeys.length > 0,\n\t);\n\n\t// Imports for the live schema() + ref() authoring API.\n\tconst coreImports = ['schema'];\n\tif (hasForeignKeys) {\n\t\tcoreImports.push('ref');\n\t}\n\tlines.push(`import { ${coreImports.join(', ')} } from '@dbsp/core';`);\n\tif (options.dbCasing && options.dbCasing !== 'preserve') {\n\t\tlines.push(\"import { createPgsqlAdapter } from '@dbsp/adapter-pgsql';\");\n\t}\n\tlines.push('');\n\n\t// Schema definition.\n\tlines.push('export const dbSchema = schema({');\n\n\t// Generate each table\n\tconst tables = Array.from(model.tables.values());\n\tconst tableLines = tables.map((table) => generateTableCode(table, options));\n\tlines.push(tableLines.join(',\\n\\n'));\n\n\tconst tableConstraints = generateTableConstraintCode(model, options);\n\tif (tableConstraints) {\n\t\tlines.push(`}, ${tableConstraints});`);\n\t} else {\n\t\tlines.push('});');\n\t}\n\tlines.push('');\n\n\t// Add a default export so schema-loader can load generated files directly.\n\t// schema-loader accepts module.schema || module.default; the named dbSchema\n\t// export is the canonical API, but the default export allows `loadSchema()`\n\t// to round-trip without the caller knowing the internal export name.\n\tlines.push('export default dbSchema;');\n\tlines.push('');\n\n\t// Usage hint with dbCasing\n\tif (options.dbCasing && options.dbCasing !== 'preserve') {\n\t\tlines.push('/**');\n\t\tlines.push(\n\t\t\t` * Usage: columns above are camelCase; the database uses ${options.dbCasing}.`,\n\t\t);\n\t\tlines.push(\n\t\t\t' * Pass dbCasing to the adapter so it maps camelCase ↔ snake_case automatically.',\n\t\t);\n\t\tlines.push(' *');\n\t\tlines.push(' * @example');\n\t\tlines.push(' * ```typescript');\n\t\tlines.push(' * const orm = createOrm({');\n\t\tlines.push(' * model: dbSchema.model,');\n\t\tlines.push(\n\t\t\t` * adapter: createPgsqlAdapter(pool, { dbCasing: '${options.dbCasing}' }),`,\n\t\t);\n\t\tlines.push(' * });');\n\t\tlines.push(' * ```');\n\t\tlines.push(' */');\n\t\tlines.push(`export const dbCasing = '${options.dbCasing}' as const;`);\n\t\tlines.push('');\n\t}\n\n\treturn {\n\t\tcode: lines.join('\\n'),\n\t\twarnings,\n\t};\n}\n","/**\n * Shared database utilities for CLI commands.\n *\n * E09: Extracted from verify.ts and introspect.ts to DRY up code.\n * E09b: Shared URL redaction for secure logging.\n */\n\n/**\n * Create a database connection pool.\n * pg is an optional peer dependency.\n *\n * @param connectionUrl - PostgreSQL connection URL\n * @returns Pool instance\n * @throws Error if pg is not installed\n */\nexport async function createDbConnection(connectionUrl: string) {\n\tlet pg: typeof import('pg').default;\n\ttry {\n\t\tconst mod = await import('pg');\n\t\tpg = mod.default;\n\t} catch {\n\t\tthrow new Error(\n\t\t\t'pg is required for this command. Install it with: pnpm add pg',\n\t\t);\n\t}\n\n\tconst pool = new pg.Pool({\n\t\tconnectionString: connectionUrl,\n\t});\n\treturn { pool };\n}\n\n/**\n * Redact password from a database connection URL for safe logging.\n *\n * @param url - Database connection URL (e.g., postgres://user:pass@host/db)\n * @returns URL with password replaced by ***\n *\n * @example\n * redactDbUrl('postgres://user:secret@localhost/mydb')\n * // => 'postgres://user:***@localhost/mydb'\n */\nexport function redactDbUrl(url: string): string {\n\ttry {\n\t\tconst u = new URL(url);\n\t\tif (u.password) {\n\t\t\tu.password = '***';\n\t\t}\n\t\treturn u.toString();\n\t} catch {\n\t\t// Fall back to regex for non-standard or malformed URLs\n\t\treturn url.replace(/:[^:@]+@/, ':***@');\n\t}\n}\n"],"mappings":"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","names":["code"]}
|
|
@@ -15,8 +15,8 @@ interface SchemaCodegenOptions {
|
|
|
15
15
|
readonly sourceUrl?: string;
|
|
16
16
|
/** Include original DB types as comments */
|
|
17
17
|
readonly includeDbTypeComments?: boolean;
|
|
18
|
-
/**
|
|
19
|
-
readonly
|
|
18
|
+
/** Receive diagnostics as they are collected, including model warnings. */
|
|
19
|
+
readonly onWarning?: (message: string) => void;
|
|
20
20
|
/** Timestamp of introspection */
|
|
21
21
|
readonly introspectedAt?: Date;
|
|
22
22
|
/**
|
|
@@ -27,13 +27,18 @@ interface SchemaCodegenOptions {
|
|
|
27
27
|
*/
|
|
28
28
|
readonly dbCasing?: 'snake_case' | 'camelCase' | 'preserve';
|
|
29
29
|
}
|
|
30
|
+
type SchemaCodegenDiagnosticsOptions = SchemaCodegenOptions;
|
|
31
|
+
interface SchemaCodegenResult {
|
|
32
|
+
readonly code: string;
|
|
33
|
+
readonly warnings: readonly string[];
|
|
34
|
+
}
|
|
30
35
|
/**
|
|
31
|
-
* Generate a TypeScript schema file from ModelIR.
|
|
36
|
+
* Generate a TypeScript schema file from ModelIR and return diagnostics.
|
|
32
37
|
*
|
|
33
38
|
* @param model - The ModelIR (or IntrospectedModelIR) to generate from
|
|
34
39
|
* @param options - Code generation options
|
|
35
|
-
* @returns
|
|
40
|
+
* @returns Generated source code and warnings
|
|
36
41
|
*/
|
|
37
|
-
declare function
|
|
42
|
+
declare function generateSchemaFileWithDiagnostics(model: ModelIR, options?: SchemaCodegenDiagnosticsOptions): SchemaCodegenResult;
|
|
38
43
|
|
|
39
|
-
export { type SchemaCodegenOptions,
|
|
44
|
+
export { type SchemaCodegenDiagnosticsOptions, type SchemaCodegenOptions, type SchemaCodegenResult, generateSchemaFileWithDiagnostics };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createDbConnection,
|
|
4
|
-
|
|
4
|
+
generateSchemaFileWithDiagnostics,
|
|
5
5
|
redactDbUrl
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VUNVYYOZ.js";
|
|
7
7
|
import {
|
|
8
8
|
loadSchema,
|
|
9
9
|
loadSchemaFromCwd
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-T4ODDHZQ.js";
|
|
11
11
|
import {
|
|
12
12
|
config,
|
|
13
13
|
validateIdentifier
|
|
@@ -29,6 +29,14 @@ var generateCommand = new Command("generate").description("Generate code from sc
|
|
|
29
29
|
).action(
|
|
30
30
|
async (target, options) => {
|
|
31
31
|
try {
|
|
32
|
+
if (target === "manifest" || target === "kysely") {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Target '${target}' has been removed. Use 'ddl' for SQL generation, or the 'introspect' command to generate a schema file.`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (target !== "ddl") {
|
|
38
|
+
throw new Error(`Unknown target: ${target}. Available targets: ddl`);
|
|
39
|
+
}
|
|
32
40
|
let schema;
|
|
33
41
|
let schemaPath;
|
|
34
42
|
if (options.schema) {
|
|
@@ -86,15 +94,6 @@ var generateCommand = new Command("generate").description("Generate code from sc
|
|
|
86
94
|
}
|
|
87
95
|
break;
|
|
88
96
|
}
|
|
89
|
-
case "manifest":
|
|
90
|
-
case "kysely":
|
|
91
|
-
throw new Error(
|
|
92
|
-
`Target '${target}' has been removed in ARCH-005. These generators required the legacy defineSchema() format. Use 'ddl' target for SQL generation.`
|
|
93
|
-
);
|
|
94
|
-
default:
|
|
95
|
-
throw new Error(
|
|
96
|
-
`Unknown target: ${target}. Available targets: ddl`
|
|
97
|
-
);
|
|
98
97
|
}
|
|
99
98
|
} catch (error) {
|
|
100
99
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -108,6 +107,21 @@ var generateCommand = new Command("generate").description("Generate code from sc
|
|
|
108
107
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
109
108
|
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
110
109
|
import { Command as Command2 } from "commander";
|
|
110
|
+
function uniqueWarnings(warnings) {
|
|
111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
112
|
+
const unique = [];
|
|
113
|
+
for (const warning of warnings) {
|
|
114
|
+
if (seen.has(warning)) continue;
|
|
115
|
+
seen.add(warning);
|
|
116
|
+
unique.push(warning);
|
|
117
|
+
}
|
|
118
|
+
return unique;
|
|
119
|
+
}
|
|
120
|
+
function reportWarnings(warnings) {
|
|
121
|
+
for (const warning of warnings) {
|
|
122
|
+
console.log(` \u26A0\uFE0F ${warning}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
111
125
|
var introspectCommand = new Command2("introspect").description("Generate schema.ts from database introspection").requiredOption("-d, --db <url>", "Database connection URL (required)").option("-o, --out <file>", "Output schema file", "./dbsp.schema.ts").option("--schema-name <name>", "Database schema name", "public").option(
|
|
112
126
|
"--exclude <patterns>",
|
|
113
127
|
"Tables to exclude (comma-separated glob patterns)",
|
|
@@ -142,23 +156,24 @@ var introspectCommand = new Command2("introspect").description("Generate schema.
|
|
|
142
156
|
console.log(
|
|
143
157
|
`\u{1F4CA} Found ${tableCount} tables, ${relationCount} relations, ${hierarchyCount} hierarchies`
|
|
144
158
|
);
|
|
145
|
-
if (model.warnings?.length) {
|
|
146
|
-
for (const w of model.warnings) {
|
|
147
|
-
console.log(` \u26A0\uFE0F ${w}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
console.log("");
|
|
151
159
|
const codegenOptions = {
|
|
152
160
|
sourceUrl: options.db,
|
|
153
161
|
includeDbTypeComments: options.dbTypeComments,
|
|
154
|
-
warnings: model.warnings,
|
|
155
162
|
introspectedAt: model.introspectedAt,
|
|
156
163
|
dbCasing: options.dbCasing
|
|
157
164
|
};
|
|
158
|
-
const
|
|
165
|
+
const generated = generateSchemaFileWithDiagnostics(
|
|
166
|
+
model,
|
|
167
|
+
codegenOptions
|
|
168
|
+
);
|
|
169
|
+
const warnings = uniqueWarnings(generated.warnings);
|
|
170
|
+
if (warnings.length > 0) {
|
|
171
|
+
reportWarnings(warnings);
|
|
172
|
+
}
|
|
173
|
+
console.log("");
|
|
159
174
|
const outPath = resolve2(process.cwd(), options.out);
|
|
160
175
|
mkdirSync2(dirname2(outPath), { recursive: true });
|
|
161
|
-
writeFileSync2(outPath,
|
|
176
|
+
writeFileSync2(outPath, generated.code, "utf-8");
|
|
162
177
|
console.log(`\u2705 Generated schema: ${outPath}`);
|
|
163
178
|
console.log(` Tables: ${tableCount}`);
|
|
164
179
|
console.log(` Relations: ${relationCount}`);
|
|
@@ -926,7 +941,7 @@ var replCommand = new Command5("repl").description("Launch interactive REPL for
|
|
|
926
941
|
});
|
|
927
942
|
return;
|
|
928
943
|
}
|
|
929
|
-
const { startRepl } = await import("./repl-
|
|
944
|
+
const { startRepl } = await import("./repl-PIFVQAHC.js");
|
|
930
945
|
await startRepl({
|
|
931
946
|
schema,
|
|
932
947
|
schemaPath,
|