@happyvertical/smrt-core 0.40.15 → 0.40.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/browser.js +2 -2
  2. package/dist/change-feed.d.ts.map +1 -1
  3. package/dist/change-feed.js +21 -4
  4. package/dist/change-feed.js.map +1 -1
  5. package/dist/class.d.ts.map +1 -1
  6. package/dist/class.js +5 -3
  7. package/dist/class.js.map +1 -1
  8. package/dist/dispatch/bus.d.ts.map +1 -1
  9. package/dist/dispatch/bus.js +11 -6
  10. package/dist/dispatch/bus.js.map +1 -1
  11. package/dist/index.js +2 -2
  12. package/dist/manifest/static-manifest.js +2 -2
  13. package/dist/manifest/static-manifest.js.map +1 -1
  14. package/dist/manifest/store.js +1 -1
  15. package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
  16. package/dist/manifest/test-manifest-stub.js +132 -2
  17. package/dist/manifest/test-manifest-stub.js.map +1 -1
  18. package/dist/manifest.json +2 -2
  19. package/dist/migrations/backfill-tracker.d.ts +1 -0
  20. package/dist/migrations/backfill-tracker.d.ts.map +1 -1
  21. package/dist/migrations/backfill-tracker.js +9 -3
  22. package/dist/migrations/backfill-tracker.js.map +1 -1
  23. package/dist/migrations/differ.d.ts +18 -0
  24. package/dist/migrations/differ.d.ts.map +1 -1
  25. package/dist/migrations/differ.js +28 -5
  26. package/dist/migrations/differ.js.map +1 -1
  27. package/dist/migrations/generator.d.ts +8 -0
  28. package/dist/migrations/generator.d.ts.map +1 -1
  29. package/dist/migrations/generator.js +5 -1
  30. package/dist/migrations/generator.js.map +1 -1
  31. package/dist/migrations/orchestrate.d.ts +10 -0
  32. package/dist/migrations/orchestrate.d.ts.map +1 -1
  33. package/dist/migrations/orchestrate.js +8 -2
  34. package/dist/migrations/orchestrate.js.map +1 -1
  35. package/dist/migrations/tracker.d.ts.map +1 -1
  36. package/dist/migrations/tracker.js +4 -2
  37. package/dist/migrations/tracker.js.map +1 -1
  38. package/dist/registry/schema-builder.d.ts +13 -6
  39. package/dist/registry/schema-builder.d.ts.map +1 -1
  40. package/dist/registry/schema-builder.js +54 -15
  41. package/dist/registry/schema-builder.js.map +1 -1
  42. package/dist/registry.d.ts +3 -2
  43. package/dist/registry.d.ts.map +1 -1
  44. package/dist/registry.js +4 -4
  45. package/dist/registry.js.map +1 -1
  46. package/dist/schema/code-generator.js +123 -0
  47. package/dist/schema/code-generator.js.map +1 -0
  48. package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
  49. package/dist/schema/ddl/base-strategy.js +2 -2
  50. package/dist/schema/ddl/base-strategy.js.map +1 -1
  51. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  52. package/dist/schema/ddl/duckdb-strategy.js +2 -3
  53. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  54. package/dist/schema/ddl/index.js +26 -2
  55. package/dist/schema/ddl/index.js.map +1 -1
  56. package/dist/schema/ddl/materialize-manifest.d.ts +36 -0
  57. package/dist/schema/ddl/materialize-manifest.d.ts.map +1 -0
  58. package/dist/schema/ddl/materialize-manifest.js +361 -0
  59. package/dist/schema/ddl/materialize-manifest.js.map +1 -0
  60. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  61. package/dist/schema/ddl/postgres-strategy.js +1 -1
  62. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  63. package/dist/schema/index.js +12 -0
  64. package/dist/schema/override-system.js +205 -0
  65. package/dist/schema/override-system.js.map +1 -0
  66. package/dist/schema/schema-aggregator.d.ts.map +1 -1
  67. package/dist/schema/schema-aggregator.js +261 -0
  68. package/dist/schema/schema-aggregator.js.map +1 -0
  69. package/dist/schema/schema-manager.js +11 -1
  70. package/dist/schema/schema-manager.js.map +1 -1
  71. package/dist/schema/utils.d.ts +1 -0
  72. package/dist/schema/utils.d.ts.map +1 -1
  73. package/dist/schema/utils.js +2 -1
  74. package/dist/schema/utils.js.map +1 -1
  75. package/dist/schema.d.ts +2 -0
  76. package/dist/schema.d.ts.map +1 -0
  77. package/dist/schema.js +13 -0
  78. package/dist/smrt-knowledge.json +5 -4
  79. package/dist/system/compatibility.d.ts +25 -0
  80. package/dist/system/compatibility.d.ts.map +1 -1
  81. package/dist/system/compatibility.js +61 -1
  82. package/dist/system/compatibility.js.map +1 -1
  83. package/dist/system/index.js +2 -2
  84. package/dist/system/schema.d.ts +17 -9
  85. package/dist/system/schema.d.ts.map +1 -1
  86. package/dist/system/schema.js +25 -11
  87. package/dist/system/schema.js.map +1 -1
  88. package/dist/testing/database.d.ts.map +1 -1
  89. package/dist/testing/database.js +5 -2
  90. package/dist/testing/database.js.map +1 -1
  91. package/package.json +10 -5
@@ -0,0 +1,361 @@
1
+ //#region src/schema/ddl/materialize-manifest.ts
2
+ function skipSQLTrivia(value, start = 0) {
3
+ let index = start;
4
+ while (index < value.length) {
5
+ if (/\s/.test(value[index])) {
6
+ index += 1;
7
+ continue;
8
+ }
9
+ if (value.startsWith("--", index)) {
10
+ const newline = value.indexOf("\n", index + 2);
11
+ return newline === -1 ? value.length : skipSQLTrivia(value, newline + 1);
12
+ }
13
+ if (value.startsWith("/*", index)) {
14
+ let depth = 1;
15
+ index += 2;
16
+ while (index < value.length && depth > 0) if (value.startsWith("/*", index)) {
17
+ depth += 1;
18
+ index += 2;
19
+ } else if (value.startsWith("*/", index)) {
20
+ depth -= 1;
21
+ index += 2;
22
+ } else index += 1;
23
+ if (depth > 0) return value.length;
24
+ continue;
25
+ }
26
+ break;
27
+ }
28
+ return index;
29
+ }
30
+ function getSQLIdentifierEnd(value, start) {
31
+ if (value[start] === "\"") {
32
+ for (let index = start + 1; index < value.length; index += 1) {
33
+ if (value[index] !== "\"") continue;
34
+ if (value[index + 1] === "\"") {
35
+ index += 1;
36
+ continue;
37
+ }
38
+ return index + 1;
39
+ }
40
+ return null;
41
+ }
42
+ const identifier = value.slice(start).match(/^[a-z_][a-z0-9_]*/i)?.[0];
43
+ return identifier ? start + identifier.length : null;
44
+ }
45
+ /** @internal */
46
+ function getSQLDefinitionIdentifier(definition) {
47
+ const start = skipSQLTrivia(definition);
48
+ const end = getSQLIdentifierEnd(definition, start);
49
+ if (end === null) return null;
50
+ const raw = definition.slice(start, end);
51
+ const quoted = raw.startsWith("\"");
52
+ return {
53
+ name: quoted ? raw.slice(1, -1).replaceAll("\"\"", "\"") : raw,
54
+ start,
55
+ end,
56
+ quoted
57
+ };
58
+ }
59
+ /** @internal */
60
+ function isSQLTableConstraintDefinition(definition) {
61
+ const identifier = getSQLDefinitionIdentifier(definition);
62
+ return identifier !== null && !identifier.quoted && /^(?:CONSTRAINT|PRIMARY|UNIQUE|FOREIGN|CHECK|EXCLUDE|LIKE|NOT)$/i.test(identifier.name);
63
+ }
64
+ function consumeSQLKeyword(value, start, keyword) {
65
+ const keywordStart = skipSQLTrivia(value, start);
66
+ if (value.slice(keywordStart, keywordStart + keyword.length).toUpperCase() !== keyword) return null;
67
+ const next = value[keywordStart + keyword.length] ?? "";
68
+ return /[a-z0-9_$]/i.test(next) ? null : keywordStart + keyword.length;
69
+ }
70
+ function hasPostgresCreateTableHeader(value, start) {
71
+ let cursor = consumeSQLKeyword(value, start, "CREATE");
72
+ if (cursor === null) return false;
73
+ const localityEnd = consumeSQLKeyword(value, cursor, "GLOBAL") ?? consumeSQLKeyword(value, cursor, "LOCAL");
74
+ if (localityEnd !== null) {
75
+ const temporaryEnd = consumeSQLKeyword(value, localityEnd, "TEMPORARY") ?? consumeSQLKeyword(value, localityEnd, "TEMP");
76
+ if (temporaryEnd === null) return false;
77
+ cursor = temporaryEnd;
78
+ } else cursor = consumeSQLKeyword(value, cursor, "TEMPORARY") ?? consumeSQLKeyword(value, cursor, "TEMP") ?? consumeSQLKeyword(value, cursor, "UNLOGGED") ?? cursor;
79
+ return consumeSQLKeyword(value, cursor, "TABLE") !== null;
80
+ }
81
+ function hasExplicitTimestampQualifier(value, timestampEnd) {
82
+ let cursor = skipSQLTrivia(value, timestampEnd);
83
+ if (value[cursor] === "(") {
84
+ const precisionEnd = findBalancedParenthesisEnd(value, cursor);
85
+ if (precisionEnd === null) return true;
86
+ cursor = precisionEnd;
87
+ }
88
+ const qualifierEnd = consumeSQLKeyword(value, cursor, "WITH") ?? consumeSQLKeyword(value, cursor, "WITHOUT");
89
+ if (qualifierEnd === null) return false;
90
+ const timeEnd = consumeSQLKeyword(value, qualifierEnd, "TIME");
91
+ if (timeEnd === null) return false;
92
+ return consumeSQLKeyword(value, timeEnd, "ZONE") !== null;
93
+ }
94
+ function visitSQLStructure(value, visitor) {
95
+ let inSingleQuote = false;
96
+ let singleQuoteBackslashEscapes = false;
97
+ let inDoubleQuote = false;
98
+ let dollarQuoteTag = null;
99
+ let inLineComment = false;
100
+ let blockCommentDepth = 0;
101
+ for (let index = 0; index < value.length; index += 1) {
102
+ const char = value[index];
103
+ const next = index + 1 < value.length ? value[index + 1] : "";
104
+ if (inLineComment) {
105
+ if (char === "\n" || char === "\r") inLineComment = false;
106
+ continue;
107
+ }
108
+ if (blockCommentDepth > 0) {
109
+ if (char === "/" && next === "*") {
110
+ blockCommentDepth += 1;
111
+ index += 1;
112
+ } else if (char === "*" && next === "/") {
113
+ blockCommentDepth -= 1;
114
+ index += 1;
115
+ }
116
+ continue;
117
+ }
118
+ if (dollarQuoteTag) {
119
+ if (value.startsWith(dollarQuoteTag, index)) {
120
+ index += dollarQuoteTag.length - 1;
121
+ dollarQuoteTag = null;
122
+ }
123
+ continue;
124
+ }
125
+ if (inSingleQuote) {
126
+ if (char === "'" && next === "'") index += 1;
127
+ else if (char === "'") {
128
+ let precedingBackslashes = 0;
129
+ for (let offset = index - 1; offset >= 0 && value[offset] === "\\"; offset -= 1) precedingBackslashes += 1;
130
+ if (!singleQuoteBackslashEscapes || precedingBackslashes % 2 === 0) {
131
+ inSingleQuote = false;
132
+ singleQuoteBackslashEscapes = false;
133
+ }
134
+ }
135
+ continue;
136
+ }
137
+ if (inDoubleQuote) {
138
+ if (char === "\"" && next === "\"") index += 1;
139
+ else if (char === "\"") inDoubleQuote = false;
140
+ continue;
141
+ }
142
+ if (char === "-" && next === "-") {
143
+ inLineComment = true;
144
+ index += 1;
145
+ continue;
146
+ }
147
+ if (char === "/" && next === "*") {
148
+ blockCommentDepth = 1;
149
+ index += 1;
150
+ continue;
151
+ }
152
+ if (char === "'") {
153
+ inSingleQuote = true;
154
+ const prefix = index > 0 ? value[index - 1] : "";
155
+ const beforePrefix = index > 1 ? value[index - 2] : "";
156
+ singleQuoteBackslashEscapes = /^e$/i.test(prefix) && !/[a-z0-9_$]/i.test(beforePrefix);
157
+ continue;
158
+ }
159
+ if (char === "\"") {
160
+ inDoubleQuote = true;
161
+ continue;
162
+ }
163
+ if (!inSingleQuote && !inDoubleQuote && char === "$") {
164
+ const dollarTag = value.slice(index).match(/^\$(?:[a-z_][a-z0-9_]*)?\$/i)?.[0];
165
+ if (dollarTag) {
166
+ dollarQuoteTag = dollarTag;
167
+ index += dollarTag.length - 1;
168
+ continue;
169
+ }
170
+ }
171
+ if (visitor(char, index) === false) return;
172
+ }
173
+ }
174
+ /**
175
+ * Build a conservative comparison key for one DDL definition.
176
+ *
177
+ * SQL trivia and unquoted token case are insignificant. Quoted literals,
178
+ * dollar-quoted bodies, and quoted identifiers remain byte-for-byte distinct,
179
+ * so comparison never conflates predicates such as `'a b'` and `'a b'`.
180
+ *
181
+ * @internal
182
+ */
183
+ function getSQLDefinitionComparisonKey(value) {
184
+ const tokens = [];
185
+ for (let index = 0; index < value.length;) {
186
+ const char = value[index];
187
+ const next = value[index + 1] ?? "";
188
+ if (/\s/.test(char)) {
189
+ index += 1;
190
+ continue;
191
+ }
192
+ if (char === "-" && next === "-") {
193
+ const newline = value.indexOf("\n", index + 2);
194
+ index = newline === -1 ? value.length : newline + 1;
195
+ continue;
196
+ }
197
+ if (char === "/" && next === "*") {
198
+ let depth = 1;
199
+ index += 2;
200
+ while (index < value.length && depth > 0) if (value.startsWith("/*", index)) {
201
+ depth += 1;
202
+ index += 2;
203
+ } else if (value.startsWith("*/", index)) {
204
+ depth -= 1;
205
+ index += 2;
206
+ } else index += 1;
207
+ continue;
208
+ }
209
+ if (char === "\"") {
210
+ const start = index;
211
+ index += 1;
212
+ while (index < value.length) if (value[index] === "\"" && value[index + 1] === "\"") index += 2;
213
+ else if (value[index] === "\"") {
214
+ index += 1;
215
+ break;
216
+ } else index += 1;
217
+ tokens.push(value.slice(start, index));
218
+ continue;
219
+ }
220
+ if (char === "'") {
221
+ const start = index;
222
+ const prefix = index > 0 ? value[index - 1] : "";
223
+ const beforePrefix = index > 1 ? value[index - 2] : "";
224
+ const backslashEscapes = /^e$/i.test(prefix) && !/[a-z0-9_$]/i.test(beforePrefix);
225
+ index += 1;
226
+ while (index < value.length) {
227
+ if (value[index] === "'" && value[index + 1] === "'") {
228
+ index += 2;
229
+ continue;
230
+ }
231
+ if (value[index] === "'") {
232
+ let precedingBackslashes = 0;
233
+ for (let offset = index - 1; offset > start && value[offset] === "\\"; offset -= 1) precedingBackslashes += 1;
234
+ if (!backslashEscapes || precedingBackslashes % 2 === 0) {
235
+ index += 1;
236
+ break;
237
+ }
238
+ }
239
+ index += 1;
240
+ }
241
+ tokens.push(value.slice(start, index));
242
+ continue;
243
+ }
244
+ if (char === "$") {
245
+ const tag = value.slice(index).match(/^\$(?:[a-z_][a-z0-9_]*)?\$/i)?.[0];
246
+ if (tag) {
247
+ const start = index;
248
+ const closing = value.indexOf(tag, index + tag.length);
249
+ index = closing === -1 ? value.length : closing + tag.length;
250
+ tokens.push(value.slice(start, index));
251
+ continue;
252
+ }
253
+ }
254
+ const word = value.slice(index).match(/^[a-z_][a-z0-9_$]*/i)?.[0];
255
+ if (word) {
256
+ tokens.push(word.toLowerCase());
257
+ index += word.length;
258
+ continue;
259
+ }
260
+ tokens.push(char);
261
+ index += 1;
262
+ }
263
+ return tokens.join("\0");
264
+ }
265
+ function getTopLevelDefinitionRanges(body) {
266
+ const ranges = [];
267
+ let segmentStart = 0;
268
+ let parenDepth = 0;
269
+ visitSQLStructure(body, (char, index) => {
270
+ if (char === "(") parenDepth += 1;
271
+ if (char === ")" && parenDepth > 0) parenDepth -= 1;
272
+ if (char === "," && parenDepth === 0) {
273
+ ranges.push([segmentStart, index]);
274
+ segmentStart = index + 1;
275
+ }
276
+ });
277
+ ranges.push([segmentStart, body.length]);
278
+ return ranges;
279
+ }
280
+ function findBalancedParenthesisEnd(value, openingParen) {
281
+ let depth = 0;
282
+ let closingParen = null;
283
+ visitSQLStructure(value.slice(openingParen), (char, relativeIndex) => {
284
+ if (char === "(") depth += 1;
285
+ else if (char === ")") {
286
+ depth -= 1;
287
+ if (depth === 0) {
288
+ closingParen = openingParen + relativeIndex + 1;
289
+ return false;
290
+ }
291
+ }
292
+ });
293
+ return closingParen;
294
+ }
295
+ /** @internal */
296
+ function findCreateTableBodyRange(ddl, statementStart = 0) {
297
+ let openingParen = null;
298
+ let closingParen = null;
299
+ let depth = 0;
300
+ visitSQLStructure(ddl.slice(statementStart), (char, relativeIndex) => {
301
+ const index = statementStart + relativeIndex;
302
+ if (char === "(") {
303
+ if (openingParen === null) openingParen = index;
304
+ depth += 1;
305
+ } else if (char === ")" && openingParen !== null) {
306
+ depth -= 1;
307
+ if (depth === 0) {
308
+ closingParen = index;
309
+ return false;
310
+ }
311
+ }
312
+ });
313
+ return openingParen !== null && closingParen !== null ? [openingParen, closingParen] : null;
314
+ }
315
+ /** @internal */
316
+ function tokenizeSQLDDLBody(body) {
317
+ return getTopLevelDefinitionRanges(body).map(([start, end]) => body.slice(start, end).trim()).filter(Boolean);
318
+ }
319
+ /**
320
+ * Make cached engine-neutral manifest DDL safe for direct execution.
321
+ *
322
+ * Cached DDL may contain custom table constraints and dialect-specific clauses,
323
+ * so this does not regenerate the whole statement from structured columns. For
324
+ * PostgreSQL it performs a span-preserving rewrite of only a column
325
+ * definition's leading bare TIMESTAMP token. Explicit WITH/WITHOUT TIME ZONE
326
+ * declarations, comments, dollar-quoted/string literals, identifiers,
327
+ * separators, and table constraints remain untouched.
328
+ */
329
+ function materializeManifestDDLForEngine(ddl, engine) {
330
+ const statementStart = skipSQLTrivia(ddl);
331
+ if (engine !== "postgres" || !hasPostgresCreateTableHeader(ddl, statementStart)) return ddl;
332
+ const bodyRange = findCreateTableBodyRange(ddl, statementStart);
333
+ if (!bodyRange) return ddl;
334
+ const [openingParen, closingParen] = bodyRange;
335
+ const body = ddl.slice(openingParen + 1, closingParen);
336
+ let changed = false;
337
+ let cursor = 0;
338
+ let materializedBody = "";
339
+ for (const [start, end] of getTopLevelDefinitionRanges(body)) {
340
+ materializedBody += body.slice(cursor, start);
341
+ const definition = body.slice(start, end);
342
+ const identifier = getSQLDefinitionIdentifier(definition);
343
+ const isTableConstraint = isSQLTableConstraintDefinition(definition);
344
+ const columnStart = identifier?.start ?? skipSQLTrivia(definition);
345
+ const typeStart = identifier ? skipSQLTrivia(definition, identifier.end) : columnStart;
346
+ const timestampEnd = consumeSQLKeyword(definition, typeStart, "TIMESTAMP");
347
+ const converted = identifier && !isTableConstraint && typeStart > identifier.end && timestampEnd !== null && !hasExplicitTimestampQualifier(definition, timestampEnd) ? `${definition.slice(0, typeStart)}TIMESTAMPTZ${definition.slice(typeStart + 9)}` : definition;
348
+ changed ||= converted !== definition;
349
+ materializedBody += converted;
350
+ cursor = end;
351
+ }
352
+ materializedBody += body.slice(cursor);
353
+ if (!changed) return ddl;
354
+ const prefix = ddl.slice(0, openingParen + 1);
355
+ const suffix = ddl.slice(closingParen);
356
+ return `${prefix}${materializedBody}${suffix}`;
357
+ }
358
+ //#endregion
359
+ export { findCreateTableBodyRange, getSQLDefinitionComparisonKey, getSQLDefinitionIdentifier, isSQLTableConstraintDefinition, materializeManifestDDLForEngine, tokenizeSQLDDLBody };
360
+
361
+ //# sourceMappingURL=materialize-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize-manifest.js","names":[],"sources":["../../../src/schema/ddl/materialize-manifest.ts"],"sourcesContent":["import type { DatabaseEngine } from './types.js';\n\nfunction skipSQLTrivia(value: string, start = 0): number {\n let index = start;\n while (index < value.length) {\n if (/\\s/.test(value[index])) {\n index += 1;\n continue;\n }\n if (value.startsWith('--', index)) {\n const newline = value.indexOf('\\n', index + 2);\n return newline === -1 ? value.length : skipSQLTrivia(value, newline + 1);\n }\n if (value.startsWith('/*', index)) {\n let depth = 1;\n index += 2;\n while (index < value.length && depth > 0) {\n if (value.startsWith('/*', index)) {\n depth += 1;\n index += 2;\n } else if (value.startsWith('*/', index)) {\n depth -= 1;\n index += 2;\n } else {\n index += 1;\n }\n }\n if (depth > 0) return value.length;\n continue;\n }\n break;\n }\n return index;\n}\n\nfunction getSQLIdentifierEnd(value: string, start: number): number | null {\n if (value[start] === '\"') {\n for (let index = start + 1; index < value.length; index += 1) {\n if (value[index] !== '\"') continue;\n if (value[index + 1] === '\"') {\n index += 1;\n continue;\n }\n return index + 1;\n }\n return null;\n }\n\n const identifier = value.slice(start).match(/^[a-z_][a-z0-9_]*/i)?.[0];\n return identifier ? start + identifier.length : null;\n}\n\n/** @internal */\nexport function getSQLDefinitionIdentifier(\n definition: string,\n): { name: string; start: number; end: number; quoted: boolean } | null {\n const start = skipSQLTrivia(definition);\n const end = getSQLIdentifierEnd(definition, start);\n if (end === null) return null;\n const raw = definition.slice(start, end);\n const quoted = raw.startsWith('\"');\n return {\n name: quoted ? raw.slice(1, -1).replaceAll('\"\"', '\"') : raw,\n start,\n end,\n quoted,\n };\n}\n\n/** @internal */\nexport function isSQLTableConstraintDefinition(definition: string): boolean {\n const identifier = getSQLDefinitionIdentifier(definition);\n return (\n identifier !== null &&\n !identifier.quoted &&\n /^(?:CONSTRAINT|PRIMARY|UNIQUE|FOREIGN|CHECK|EXCLUDE|LIKE|NOT)$/i.test(\n identifier.name,\n )\n );\n}\n\nfunction consumeSQLKeyword(\n value: string,\n start: number,\n keyword: string,\n): number | null {\n const keywordStart = skipSQLTrivia(value, start);\n const candidate = value.slice(keywordStart, keywordStart + keyword.length);\n if (candidate.toUpperCase() !== keyword) return null;\n const next = value[keywordStart + keyword.length] ?? '';\n return /[a-z0-9_$]/i.test(next) ? null : keywordStart + keyword.length;\n}\n\nfunction hasPostgresCreateTableHeader(value: string, start: number): boolean {\n let cursor = consumeSQLKeyword(value, start, 'CREATE');\n if (cursor === null) return false;\n\n const localityEnd =\n consumeSQLKeyword(value, cursor, 'GLOBAL') ??\n consumeSQLKeyword(value, cursor, 'LOCAL');\n if (localityEnd !== null) {\n const temporaryEnd =\n consumeSQLKeyword(value, localityEnd, 'TEMPORARY') ??\n consumeSQLKeyword(value, localityEnd, 'TEMP');\n if (temporaryEnd === null) return false;\n cursor = temporaryEnd;\n } else {\n cursor =\n consumeSQLKeyword(value, cursor, 'TEMPORARY') ??\n consumeSQLKeyword(value, cursor, 'TEMP') ??\n consumeSQLKeyword(value, cursor, 'UNLOGGED') ??\n cursor;\n }\n\n return consumeSQLKeyword(value, cursor, 'TABLE') !== null;\n}\n\nfunction hasExplicitTimestampQualifier(\n value: string,\n timestampEnd: number,\n): boolean {\n let cursor = skipSQLTrivia(value, timestampEnd);\n if (value[cursor] === '(') {\n const precisionEnd = findBalancedParenthesisEnd(value, cursor);\n if (precisionEnd === null) return true;\n cursor = precisionEnd;\n }\n\n const qualifierEnd =\n consumeSQLKeyword(value, cursor, 'WITH') ??\n consumeSQLKeyword(value, cursor, 'WITHOUT');\n if (qualifierEnd === null) return false;\n const timeEnd = consumeSQLKeyword(value, qualifierEnd, 'TIME');\n if (timeEnd === null) return false;\n return consumeSQLKeyword(value, timeEnd, 'ZONE') !== null;\n}\n\nfunction visitSQLStructure(\n value: string,\n visitor: (char: string, index: number) => boolean | undefined,\n): void {\n let inSingleQuote = false;\n let singleQuoteBackslashEscapes = false;\n let inDoubleQuote = false;\n let dollarQuoteTag: string | null = null;\n let inLineComment = false;\n let blockCommentDepth = 0;\n\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index];\n const next = index + 1 < value.length ? value[index + 1] : '';\n\n if (inLineComment) {\n if (char === '\\n' || char === '\\r') inLineComment = false;\n continue;\n }\n\n if (blockCommentDepth > 0) {\n if (char === '/' && next === '*') {\n blockCommentDepth += 1;\n index += 1;\n } else if (char === '*' && next === '/') {\n blockCommentDepth -= 1;\n index += 1;\n }\n continue;\n }\n\n if (dollarQuoteTag) {\n if (value.startsWith(dollarQuoteTag, index)) {\n index += dollarQuoteTag.length - 1;\n dollarQuoteTag = null;\n }\n continue;\n }\n\n if (inSingleQuote) {\n if (char === \"'\" && next === \"'\") {\n index += 1;\n } else if (char === \"'\") {\n let precedingBackslashes = 0;\n for (\n let offset = index - 1;\n offset >= 0 && value[offset] === '\\\\';\n offset -= 1\n ) {\n precedingBackslashes += 1;\n }\n if (!singleQuoteBackslashEscapes || precedingBackslashes % 2 === 0) {\n inSingleQuote = false;\n singleQuoteBackslashEscapes = false;\n }\n }\n continue;\n }\n\n if (inDoubleQuote) {\n if (char === '\"' && next === '\"') {\n index += 1;\n } else if (char === '\"') {\n inDoubleQuote = false;\n }\n continue;\n }\n\n if (char === '-' && next === '-') {\n inLineComment = true;\n index += 1;\n continue;\n }\n\n if (char === '/' && next === '*') {\n blockCommentDepth = 1;\n index += 1;\n continue;\n }\n\n if (char === \"'\") {\n inSingleQuote = true;\n const prefix = index > 0 ? value[index - 1] : '';\n const beforePrefix = index > 1 ? value[index - 2] : '';\n singleQuoteBackslashEscapes =\n /^e$/i.test(prefix) && !/[a-z0-9_$]/i.test(beforePrefix);\n continue;\n }\n\n if (char === '\"') {\n inDoubleQuote = true;\n continue;\n }\n\n if (!inSingleQuote && !inDoubleQuote && char === '$') {\n const dollarTag = value\n .slice(index)\n .match(/^\\$(?:[a-z_][a-z0-9_]*)?\\$/i)?.[0];\n if (dollarTag) {\n dollarQuoteTag = dollarTag;\n index += dollarTag.length - 1;\n continue;\n }\n }\n\n if (visitor(char, index) === false) return;\n }\n}\n\n/**\n * Build a conservative comparison key for one DDL definition.\n *\n * SQL trivia and unquoted token case are insignificant. Quoted literals,\n * dollar-quoted bodies, and quoted identifiers remain byte-for-byte distinct,\n * so comparison never conflates predicates such as `'a b'` and `'a b'`.\n *\n * @internal\n */\nexport function getSQLDefinitionComparisonKey(value: string): string {\n const tokens: string[] = [];\n\n for (let index = 0; index < value.length; ) {\n const char = value[index];\n const next = value[index + 1] ?? '';\n\n if (/\\s/.test(char)) {\n index += 1;\n continue;\n }\n\n if (char === '-' && next === '-') {\n const newline = value.indexOf('\\n', index + 2);\n index = newline === -1 ? value.length : newline + 1;\n continue;\n }\n\n if (char === '/' && next === '*') {\n let depth = 1;\n index += 2;\n while (index < value.length && depth > 0) {\n if (value.startsWith('/*', index)) {\n depth += 1;\n index += 2;\n } else if (value.startsWith('*/', index)) {\n depth -= 1;\n index += 2;\n } else {\n index += 1;\n }\n }\n continue;\n }\n\n if (char === '\"') {\n const start = index;\n index += 1;\n while (index < value.length) {\n if (value[index] === '\"' && value[index + 1] === '\"') {\n index += 2;\n } else if (value[index] === '\"') {\n index += 1;\n break;\n } else {\n index += 1;\n }\n }\n tokens.push(value.slice(start, index));\n continue;\n }\n\n if (char === \"'\") {\n const start = index;\n const prefix = index > 0 ? value[index - 1] : '';\n const beforePrefix = index > 1 ? value[index - 2] : '';\n const backslashEscapes =\n /^e$/i.test(prefix) && !/[a-z0-9_$]/i.test(beforePrefix);\n index += 1;\n while (index < value.length) {\n if (value[index] === \"'\" && value[index + 1] === \"'\") {\n index += 2;\n continue;\n }\n if (value[index] === \"'\") {\n let precedingBackslashes = 0;\n for (\n let offset = index - 1;\n offset > start && value[offset] === '\\\\';\n offset -= 1\n ) {\n precedingBackslashes += 1;\n }\n if (!backslashEscapes || precedingBackslashes % 2 === 0) {\n index += 1;\n break;\n }\n }\n index += 1;\n }\n tokens.push(value.slice(start, index));\n continue;\n }\n\n if (char === '$') {\n const tag = value.slice(index).match(/^\\$(?:[a-z_][a-z0-9_]*)?\\$/i)?.[0];\n if (tag) {\n const start = index;\n const closing = value.indexOf(tag, index + tag.length);\n index = closing === -1 ? value.length : closing + tag.length;\n tokens.push(value.slice(start, index));\n continue;\n }\n }\n\n const word = value.slice(index).match(/^[a-z_][a-z0-9_$]*/i)?.[0];\n if (word) {\n tokens.push(word.toLowerCase());\n index += word.length;\n continue;\n }\n\n tokens.push(char);\n index += 1;\n }\n\n return tokens.join('\\0');\n}\n\nfunction getTopLevelDefinitionRanges(body: string): Array<[number, number]> {\n const ranges: Array<[number, number]> = [];\n let segmentStart = 0;\n let parenDepth = 0;\n\n visitSQLStructure(body, (char, index) => {\n if (char === '(') parenDepth += 1;\n if (char === ')' && parenDepth > 0) parenDepth -= 1;\n if (char === ',' && parenDepth === 0) {\n ranges.push([segmentStart, index]);\n segmentStart = index + 1;\n }\n return undefined;\n });\n\n ranges.push([segmentStart, body.length]);\n return ranges;\n}\n\nfunction findBalancedParenthesisEnd(\n value: string,\n openingParen: number,\n): number | null {\n let depth = 0;\n let closingParen: number | null = null;\n visitSQLStructure(value.slice(openingParen), (char, relativeIndex) => {\n if (char === '(') {\n depth += 1;\n } else if (char === ')') {\n depth -= 1;\n if (depth === 0) {\n closingParen = openingParen + relativeIndex + 1;\n return false;\n }\n }\n return undefined;\n });\n return closingParen;\n}\n\n/** @internal */\nexport function findCreateTableBodyRange(\n ddl: string,\n statementStart = 0,\n): [number, number] | null {\n let openingParen: number | null = null;\n let closingParen: number | null = null;\n let depth = 0;\n\n visitSQLStructure(ddl.slice(statementStart), (char, relativeIndex) => {\n const index = statementStart + relativeIndex;\n if (char === '(') {\n if (openingParen === null) openingParen = index;\n depth += 1;\n } else if (char === ')' && openingParen !== null) {\n depth -= 1;\n if (depth === 0) {\n closingParen = index;\n return false;\n }\n }\n return undefined;\n });\n\n return openingParen !== null && closingParen !== null\n ? [openingParen, closingParen]\n : null;\n}\n\n/** @internal */\nexport function tokenizeSQLDDLBody(body: string): string[] {\n return getTopLevelDefinitionRanges(body)\n .map(([start, end]) => body.slice(start, end).trim())\n .filter(Boolean);\n}\n\n/**\n * Make cached engine-neutral manifest DDL safe for direct execution.\n *\n * Cached DDL may contain custom table constraints and dialect-specific clauses,\n * so this does not regenerate the whole statement from structured columns. For\n * PostgreSQL it performs a span-preserving rewrite of only a column\n * definition's leading bare TIMESTAMP token. Explicit WITH/WITHOUT TIME ZONE\n * declarations, comments, dollar-quoted/string literals, identifiers,\n * separators, and table constraints remain untouched.\n */\nexport function materializeManifestDDLForEngine(\n ddl: string,\n engine: DatabaseEngine,\n): string {\n const statementStart = skipSQLTrivia(ddl);\n if (\n engine !== 'postgres' ||\n !hasPostgresCreateTableHeader(ddl, statementStart)\n ) {\n return ddl;\n }\n\n const bodyRange = findCreateTableBodyRange(ddl, statementStart);\n if (!bodyRange) return ddl;\n const [openingParen, closingParen] = bodyRange;\n\n const body = ddl.slice(openingParen + 1, closingParen);\n let changed = false;\n let cursor = 0;\n let materializedBody = '';\n for (const [start, end] of getTopLevelDefinitionRanges(body)) {\n materializedBody += body.slice(cursor, start);\n const definition = body.slice(start, end);\n const identifier = getSQLDefinitionIdentifier(definition);\n const isTableConstraint = isSQLTableConstraintDefinition(definition);\n const columnStart = identifier?.start ?? skipSQLTrivia(definition);\n const typeStart = identifier\n ? skipSQLTrivia(definition, identifier.end)\n : columnStart;\n const timestampEnd = consumeSQLKeyword(definition, typeStart, 'TIMESTAMP');\n const converted =\n identifier &&\n !isTableConstraint &&\n typeStart > identifier.end &&\n timestampEnd !== null &&\n !hasExplicitTimestampQualifier(definition, timestampEnd)\n ? `${definition.slice(0, typeStart)}TIMESTAMPTZ${definition.slice(typeStart + 'TIMESTAMP'.length)}`\n : definition;\n changed ||= converted !== definition;\n materializedBody += converted;\n cursor = end;\n }\n materializedBody += body.slice(cursor);\n\n if (!changed) return ddl;\n\n const prefix = ddl.slice(0, openingParen + 1);\n const suffix = ddl.slice(closingParen);\n return `${prefix}${materializedBody}${suffix}`;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-strategy.d.ts","sourceRoot":"","sources":["../../../src/schema/ddl/postgres-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAc;IAE7C;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM;IAelC;;;;;OAKG;IACH,SAAS,CAAC,wBAAwB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,iBAAiB,GACzB,MAAM;IAkDT;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;;OAGG;IACH,oBAAoB,IAAI,OAAO;CAGhC"}
1
+ {"version":3,"file":"postgres-strategy.d.ts","sourceRoot":"","sources":["../../../src/schema/ddl/postgres-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAc;IAE7C;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM;IAkBlC;;;;;OAKG;IACH,SAAS,CAAC,wBAAwB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,iBAAiB,GACzB,MAAM;IAkDT;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;;OAGG;IACH,oBAAoB,IAAI,OAAO;CAGhC"}
@@ -10,7 +10,7 @@ var PostgresStrategy = class extends BaseDDLStrategy {
10
10
  mapType(type) {
11
11
  switch (type) {
12
12
  case "JSON": return "JSONB";
13
- case "TIMESTAMP": return "TIMESTAMP";
13
+ case "TIMESTAMP": return "TIMESTAMPTZ";
14
14
  case "REAL": return "DOUBLE PRECISION";
15
15
  case "UUID": return "uuid";
16
16
  default: return super.mapType(type);
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-strategy.js","names":[],"sources":["../../../src/schema/ddl/postgres-strategy.ts"],"sourcesContent":["/**\n * PostgreSQL DDL Strategy\n *\n * PostgreSQL-specific DDL generation with the following characteristics:\n * - JSONB type for JSON columns (more efficient than JSON)\n * - Native BOOLEAN type\n * - Full trigger support with PL/pgSQL functions\n * - UNIQUE can be inline or separate indexes\n * - Supports CAST in DEFAULT values\n */\n\nimport type { SQLDataType, TriggerDefinition } from '../types.js';\nimport { BaseDDLStrategy } from './base-strategy.js';\nimport type { DatabaseEngine } from './types.js';\n\nexport class PostgresStrategy extends BaseDDLStrategy {\n readonly engine: DatabaseEngine = 'postgres';\n\n /**\n * Map types for PostgreSQL\n * - JSON → JSONB (more efficient, supports indexing)\n * - TIMESTAMP → TIMESTAMP WITH TIME ZONE (best practice)\n */\n mapType(type: SQLDataType): string {\n switch (type) {\n case 'JSON':\n return 'JSONB'; // PostgreSQL JSONB is more efficient\n case 'TIMESTAMP':\n return 'TIMESTAMP'; // Could use TIMESTAMPTZ for timezone-aware\n case 'REAL':\n return 'DOUBLE PRECISION'; // PostgreSQL convention\n case 'UUID':\n return 'uuid'; // Native 16-byte uuid (R11) — vs 37-byte text\n default:\n return super.mapType(type);\n }\n }\n\n /**\n * Generate trigger for PostgreSQL\n *\n * PostgreSQL triggers require a separate function, then the trigger.\n * For updated_at timestamps, we generate both.\n */\n protected generateTriggerStatement(\n tableName: string,\n trigger: TriggerDefinition,\n ): string {\n // For simple triggers, we need to create a function first\n // This is PostgreSQL's two-step trigger process\n\n // Check if this is an updated_at trigger\n if (trigger.name.includes('updated_at')) {\n // Generate the function and trigger for updated_at\n const functionName = `update_${tableName}_updated_at`;\n\n let sql = `-- Function for ${trigger.name}\\n`;\n sql += `CREATE OR REPLACE FUNCTION ${functionName}()\\n`;\n sql += `RETURNS TRIGGER AS $$\\n`;\n sql += `BEGIN\\n`;\n sql += ` NEW.updated_at = CURRENT_TIMESTAMP;\\n`;\n sql += ` RETURN NEW;\\n`;\n sql += `END;\\n`;\n sql += `$$ LANGUAGE plpgsql;\\n\\n`;\n\n sql += `-- Trigger\\n`;\n sql += `DROP TRIGGER IF EXISTS \"${trigger.name}\" ON \"${tableName}\";\\n`;\n sql += `CREATE TRIGGER \"${trigger.name}\"\\n`;\n sql += `${trigger.when} ${trigger.event} ON \"${tableName}\"\\n`;\n sql += `FOR EACH ROW\\n`;\n sql += `EXECUTE FUNCTION ${functionName}();`;\n\n return sql;\n }\n\n // For custom triggers, use the provided body\n let sql = `DROP TRIGGER IF EXISTS \"${trigger.name}\" ON \"${tableName}\";\\n`;\n sql += `CREATE TRIGGER \"${trigger.name}\"\\n`;\n sql += `${trigger.when} ${trigger.event} ON \"${tableName}\"\\n`;\n sql += `FOR EACH ROW\\n`;\n\n if (trigger.condition) {\n sql += `WHEN (${trigger.condition})\\n`;\n }\n\n // PostgreSQL triggers need EXECUTE FUNCTION or EXECUTE PROCEDURE\n // If body contains a function call, use it directly\n if (trigger.body.includes('EXECUTE')) {\n sql += trigger.body;\n } else {\n // Wrap in anonymous DO block or assume it's a function name\n sql += `EXECUTE FUNCTION ${trigger.body}`;\n }\n\n return `${sql};`;\n }\n\n /**\n * PostgreSQL supports triggers\n */\n supportsTriggers(): boolean {\n return true;\n }\n\n /**\n * PostgreSQL doesn't require inline UNIQUE for UPSERT\n * Both inline and separate indexes work with ON CONFLICT\n */\n requiresInlineUnique(): boolean {\n return false;\n }\n}\n"],"mappings":";;AAeA,IAAa,mBAAb,cAAsC,gBAAgB;CACpD,SAAkC;;;;;;CAOlC,QAAQ,MAA2B;EACjC,QAAQ,MAAR;GACE,KAAK,QACH,OAAO;GACT,KAAK,aACH,OAAO;GACT,KAAK,QACH,OAAO;GACT,KAAK,QACH,OAAO;GACT,SACE,OAAO,MAAM,QAAQ,IAAI;EAC7B;CACF;;;;;;;CAQA,yBACE,WACA,SACQ;EAKR,IAAI,QAAQ,KAAK,SAAS,YAAY,GAAG;GAEvC,MAAM,eAAe,UAAU,UAAU;GAEzC,IAAI,MAAM,mBAAmB,QAAQ,KAAK;GAC1C,OAAO,8BAA8B,aAAa;GAClD,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;GAEP,OAAO;GACP,OAAO,2BAA2B,QAAQ,KAAK,QAAQ,UAAU;GACjE,OAAO,mBAAmB,QAAQ,KAAK;GACvC,OAAO,GAAG,QAAQ,KAAK,GAAG,QAAQ,MAAM,OAAO,UAAU;GACzD,OAAO;GACP,OAAO,oBAAoB,aAAa;GAExC,OAAO;EACT;EAGA,IAAI,MAAM,2BAA2B,QAAQ,KAAK,QAAQ,UAAU;EACpE,OAAO,mBAAmB,QAAQ,KAAK;EACvC,OAAO,GAAG,QAAQ,KAAK,GAAG,QAAQ,MAAM,OAAO,UAAU;EACzD,OAAO;EAEP,IAAI,QAAQ,WACV,OAAO,SAAS,QAAQ,UAAU;EAKpC,IAAI,QAAQ,KAAK,SAAS,SAAS,GACjC,OAAO,QAAQ;OAGf,OAAO,oBAAoB,QAAQ;EAGrC,OAAO,GAAG,IAAI;CAChB;;;;CAKA,mBAA4B;EAC1B,OAAO;CACT;;;;;CAMA,uBAAgC;EAC9B,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"postgres-strategy.js","names":[],"sources":["../../../src/schema/ddl/postgres-strategy.ts"],"sourcesContent":["/**\n * PostgreSQL DDL Strategy\n *\n * PostgreSQL-specific DDL generation with the following characteristics:\n * - JSONB type for JSON columns (more efficient than JSON)\n * - Native BOOLEAN type\n * - Full trigger support with PL/pgSQL functions\n * - UNIQUE can be inline or separate indexes\n * - Supports CAST in DEFAULT values\n */\n\nimport type { SQLDataType, TriggerDefinition } from '../types.js';\nimport { BaseDDLStrategy } from './base-strategy.js';\nimport type { DatabaseEngine } from './types.js';\n\nexport class PostgresStrategy extends BaseDDLStrategy {\n readonly engine: DatabaseEngine = 'postgres';\n\n /**\n * Map types for PostgreSQL\n * - JSON → JSONB (more efficient, supports indexing)\n * - TIMESTAMP → TIMESTAMP WITH TIME ZONE (best practice)\n */\n mapType(type: SQLDataType): string {\n switch (type) {\n case 'JSON':\n return 'JSONB'; // PostgreSQL JSONB is more efficient\n case 'TIMESTAMP':\n // JavaScript Date represents an instant. Preserve that contract in\n // PostgreSQL instead of discarding the ISO offset in a timezone-naive\n // TIMESTAMP column and reinterpreting the wall time at hydration.\n return 'TIMESTAMPTZ';\n case 'REAL':\n return 'DOUBLE PRECISION'; // PostgreSQL convention\n case 'UUID':\n return 'uuid'; // Native 16-byte uuid (R11) — vs 37-byte text\n default:\n return super.mapType(type);\n }\n }\n\n /**\n * Generate trigger for PostgreSQL\n *\n * PostgreSQL triggers require a separate function, then the trigger.\n * For updated_at timestamps, we generate both.\n */\n protected generateTriggerStatement(\n tableName: string,\n trigger: TriggerDefinition,\n ): string {\n // For simple triggers, we need to create a function first\n // This is PostgreSQL's two-step trigger process\n\n // Check if this is an updated_at trigger\n if (trigger.name.includes('updated_at')) {\n // Generate the function and trigger for updated_at\n const functionName = `update_${tableName}_updated_at`;\n\n let sql = `-- Function for ${trigger.name}\\n`;\n sql += `CREATE OR REPLACE FUNCTION ${functionName}()\\n`;\n sql += `RETURNS TRIGGER AS $$\\n`;\n sql += `BEGIN\\n`;\n sql += ` NEW.updated_at = CURRENT_TIMESTAMP;\\n`;\n sql += ` RETURN NEW;\\n`;\n sql += `END;\\n`;\n sql += `$$ LANGUAGE plpgsql;\\n\\n`;\n\n sql += `-- Trigger\\n`;\n sql += `DROP TRIGGER IF EXISTS \"${trigger.name}\" ON \"${tableName}\";\\n`;\n sql += `CREATE TRIGGER \"${trigger.name}\"\\n`;\n sql += `${trigger.when} ${trigger.event} ON \"${tableName}\"\\n`;\n sql += `FOR EACH ROW\\n`;\n sql += `EXECUTE FUNCTION ${functionName}();`;\n\n return sql;\n }\n\n // For custom triggers, use the provided body\n let sql = `DROP TRIGGER IF EXISTS \"${trigger.name}\" ON \"${tableName}\";\\n`;\n sql += `CREATE TRIGGER \"${trigger.name}\"\\n`;\n sql += `${trigger.when} ${trigger.event} ON \"${tableName}\"\\n`;\n sql += `FOR EACH ROW\\n`;\n\n if (trigger.condition) {\n sql += `WHEN (${trigger.condition})\\n`;\n }\n\n // PostgreSQL triggers need EXECUTE FUNCTION or EXECUTE PROCEDURE\n // If body contains a function call, use it directly\n if (trigger.body.includes('EXECUTE')) {\n sql += trigger.body;\n } else {\n // Wrap in anonymous DO block or assume it's a function name\n sql += `EXECUTE FUNCTION ${trigger.body}`;\n }\n\n return `${sql};`;\n }\n\n /**\n * PostgreSQL supports triggers\n */\n supportsTriggers(): boolean {\n return true;\n }\n\n /**\n * PostgreSQL doesn't require inline UNIQUE for UPSERT\n * Both inline and separate indexes work with ON CONFLICT\n */\n requiresInlineUnique(): boolean {\n return false;\n }\n}\n"],"mappings":";;AAeA,IAAa,mBAAb,cAAsC,gBAAgB;CACpD,SAAkC;;;;;;CAOlC,QAAQ,MAA2B;EACjC,QAAQ,MAAR;GACE,KAAK,QACH,OAAO;GACT,KAAK,aAIH,OAAO;GACT,KAAK,QACH,OAAO;GACT,KAAK,QACH,OAAO;GACT,SACE,OAAO,MAAM,QAAQ,IAAI;EAC7B;CACF;;;;;;;CAQA,yBACE,WACA,SACQ;EAKR,IAAI,QAAQ,KAAK,SAAS,YAAY,GAAG;GAEvC,MAAM,eAAe,UAAU,UAAU;GAEzC,IAAI,MAAM,mBAAmB,QAAQ,KAAK;GAC1C,OAAO,8BAA8B,aAAa;GAClD,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;GACP,OAAO;GAEP,OAAO;GACP,OAAO,2BAA2B,QAAQ,KAAK,QAAQ,UAAU;GACjE,OAAO,mBAAmB,QAAQ,KAAK;GACvC,OAAO,GAAG,QAAQ,KAAK,GAAG,QAAQ,MAAM,OAAO,UAAU;GACzD,OAAO;GACP,OAAO,oBAAoB,aAAa;GAExC,OAAO;EACT;EAGA,IAAI,MAAM,2BAA2B,QAAQ,KAAK,QAAQ,UAAU;EACpE,OAAO,mBAAmB,QAAQ,KAAK;EACvC,OAAO,GAAG,QAAQ,KAAK,GAAG,QAAQ,MAAM,OAAO,UAAU;EACzD,OAAO;EAEP,IAAI,QAAQ,WACV,OAAO,SAAS,QAAQ,UAAU;EAKpC,IAAI,QAAQ,KAAK,SAAS,SAAS,GACjC,OAAO,QAAQ;OAGf,OAAO,oBAAoB,QAAQ;EAGrC,OAAO,GAAG,IAAI;CAChB;;;;CAKA,mBAA4B;EAC1B,OAAO;CACT;;;;;CAMA,uBAAgC;EAC9B,OAAO;CACT;AACF"}
@@ -0,0 +1,12 @@
1
+ import { BaseDDLStrategy } from "./ddl/base-strategy.js";
2
+ import { DuckDBStrategy } from "./ddl/duckdb-strategy.js";
3
+ import { JsonDuckDBStrategy } from "./ddl/json-duckdb-strategy.js";
4
+ import { PostgresStrategy } from "./ddl/postgres-strategy.js";
5
+ import { SQLiteStrategy } from "./ddl/sqlite-strategy.js";
6
+ import { detectEngine, generateDDLForEngine, generateMultiEngineDDL, getAllStrategies, getDDLStrategy } from "./ddl/index.js";
7
+ import { SchemaGenerator } from "./generator.js";
8
+ import { SchemaManager, createSchemaManager } from "./schema-manager.js";
9
+ import { SchemaCodeGenerator } from "./code-generator.js";
10
+ import { SchemaOverrideSystem } from "./override-system.js";
11
+ import { SchemaAggregator } from "./schema-aggregator.js";
12
+ export { BaseDDLStrategy, DuckDBStrategy, JsonDuckDBStrategy, PostgresStrategy, SQLiteStrategy, SchemaAggregator, SchemaCodeGenerator, SchemaGenerator, SchemaManager, SchemaOverrideSystem, createSchemaManager, detectEngine, generateDDLForEngine, generateMultiEngineDDL, getAllStrategies, getDDLStrategy };
@@ -0,0 +1,205 @@
1
+ import { createHash } from "node:crypto";
2
+ //#region src/schema/override-system.ts
3
+ /**
4
+ * Schema override system for SMRT objects
5
+ * Allows packages and applications to extend base schemas
6
+ */
7
+ var SchemaOverrideSystem = class SchemaOverrideSystem {
8
+ /**
9
+ * Apply schema override to base schema
10
+ */
11
+ static applyOverride(baseSchema, override) {
12
+ const overriddenSchema = {
13
+ ...baseSchema,
14
+ version: SchemaOverrideSystem.generateOverrideVersion(baseSchema, override),
15
+ packageName: override.packageName
16
+ };
17
+ if (override.addColumns) overriddenSchema.columns = {
18
+ ...overriddenSchema.columns,
19
+ ...override.addColumns
20
+ };
21
+ if (override.removeColumns) for (const columnName of override.removeColumns) delete overriddenSchema.columns[columnName];
22
+ if (override.addIndexes) overriddenSchema.indexes = [...overriddenSchema.indexes, ...override.addIndexes];
23
+ if (override.removeIndexes) overriddenSchema.indexes = overriddenSchema.indexes.filter((index) => !override.removeIndexes?.includes(index.name));
24
+ if (override.addTriggers) overriddenSchema.triggers = [...overriddenSchema.triggers, ...override.addTriggers];
25
+ if (override.removeTriggers) overriddenSchema.triggers = overriddenSchema.triggers.filter((trigger) => !override.removeTriggers?.includes(trigger.name));
26
+ overriddenSchema.foreignKeys = SchemaOverrideSystem.extractForeignKeys(overriddenSchema.columns);
27
+ overriddenSchema.dependencies = SchemaOverrideSystem.extractDependencies(overriddenSchema);
28
+ return overriddenSchema;
29
+ }
30
+ /**
31
+ * Create a schema override for extending a base schema
32
+ */
33
+ static createOverride(tableName, packageName, extensions) {
34
+ return {
35
+ tableName,
36
+ packageName,
37
+ ...extensions
38
+ };
39
+ }
40
+ /**
41
+ * Merge multiple schema overrides
42
+ */
43
+ static mergeOverrides(baseSchema, overrides) {
44
+ let currentSchema = baseSchema;
45
+ for (const override of overrides) if (override.tableName === baseSchema.tableName) currentSchema = SchemaOverrideSystem.applyOverride(currentSchema, override);
46
+ return currentSchema;
47
+ }
48
+ /**
49
+ * Create praeco-specific content schema override
50
+ */
51
+ static createPraecoContentOverride() {
52
+ return SchemaOverrideSystem.createOverride("contents", "praeco", {
53
+ addColumns: {
54
+ council_id: {
55
+ type: "TEXT",
56
+ foreignKey: {
57
+ table: "councils",
58
+ column: "id",
59
+ onDelete: "CASCADE",
60
+ onUpdate: "CASCADE"
61
+ },
62
+ description: "Reference to council this content belongs to"
63
+ },
64
+ meeting_id: {
65
+ type: "TEXT",
66
+ foreignKey: {
67
+ table: "meetings",
68
+ column: "id",
69
+ onDelete: "CASCADE",
70
+ onUpdate: "CASCADE"
71
+ },
72
+ description: "Reference to meeting this content relates to"
73
+ },
74
+ content_type: {
75
+ type: "TEXT",
76
+ notNull: true,
77
+ defaultValue: "'document'",
78
+ description: "Type of content: document, agenda, minutes, etc."
79
+ },
80
+ source_url: {
81
+ type: "TEXT",
82
+ description: "Original URL where content was found"
83
+ },
84
+ extraction_metadata: {
85
+ type: "JSON",
86
+ description: "Metadata about content extraction process"
87
+ },
88
+ analysis_results: {
89
+ type: "JSON",
90
+ description: "AI analysis results for content"
91
+ },
92
+ priority_score: {
93
+ type: "INTEGER",
94
+ defaultValue: "0",
95
+ description: "Priority score for content processing"
96
+ }
97
+ },
98
+ addIndexes: [
99
+ {
100
+ name: "idx_contents_council_id",
101
+ columns: ["council_id"],
102
+ description: "Index for council-based content queries"
103
+ },
104
+ {
105
+ name: "idx_contents_meeting_id",
106
+ columns: ["meeting_id"],
107
+ description: "Index for meeting-based content queries"
108
+ },
109
+ {
110
+ name: "idx_contents_type_priority",
111
+ columns: ["content_type", "priority_score"],
112
+ description: "Compound index for content type and priority queries"
113
+ }
114
+ ]
115
+ });
116
+ }
117
+ /**
118
+ * Create praeco-specific meeting schema override
119
+ */
120
+ static createPraecoMeetingOverride() {
121
+ return SchemaOverrideSystem.createOverride("meetings", "praeco", {
122
+ addColumns: {
123
+ agenda_url: {
124
+ type: "TEXT",
125
+ description: "URL to meeting agenda document"
126
+ },
127
+ minutes_url: {
128
+ type: "TEXT",
129
+ description: "URL to meeting minutes document"
130
+ },
131
+ video_url: {
132
+ type: "TEXT",
133
+ description: "URL to meeting video recording"
134
+ },
135
+ meeting_status: {
136
+ type: "TEXT",
137
+ notNull: true,
138
+ defaultValue: "'scheduled'",
139
+ description: "Status: scheduled, in_progress, completed, cancelled"
140
+ },
141
+ ai_summary: {
142
+ type: "JSON",
143
+ description: "AI-generated summary of meeting content"
144
+ },
145
+ key_decisions: {
146
+ type: "JSON",
147
+ description: "Key decisions made in the meeting"
148
+ },
149
+ action_items: {
150
+ type: "JSON",
151
+ description: "Action items from the meeting"
152
+ }
153
+ },
154
+ addIndexes: [{
155
+ name: "idx_meetings_status_date",
156
+ columns: ["meeting_status", "date"],
157
+ description: "Index for status and date-based meeting queries"
158
+ }]
159
+ });
160
+ }
161
+ /**
162
+ * Generate version for overridden schema
163
+ */
164
+ static generateOverrideVersion(baseSchema, override) {
165
+ const overrideContent = JSON.stringify({
166
+ baseVersion: baseSchema.version,
167
+ override: {
168
+ addColumns: override.addColumns,
169
+ removeColumns: override.removeColumns,
170
+ addIndexes: override.addIndexes,
171
+ removeIndexes: override.removeIndexes,
172
+ addTriggers: override.addTriggers,
173
+ removeTriggers: override.removeTriggers
174
+ },
175
+ packageName: override.packageName
176
+ });
177
+ return createHash("sha256").update(overrideContent).digest("hex").substring(0, 8);
178
+ }
179
+ /**
180
+ * Extract foreign keys from columns
181
+ */
182
+ static extractForeignKeys(columns) {
183
+ const foreignKeys = [];
184
+ for (const [columnName, columnDef] of Object.entries(columns)) if (columnDef.foreignKey) foreignKeys.push({
185
+ column: columnName,
186
+ referencesTable: columnDef.foreignKey.table,
187
+ referencesColumn: columnDef.foreignKey.column,
188
+ onDelete: columnDef.foreignKey.onDelete,
189
+ onUpdate: columnDef.foreignKey.onUpdate
190
+ });
191
+ return foreignKeys;
192
+ }
193
+ /**
194
+ * Extract dependencies from schema
195
+ */
196
+ static extractDependencies(schema) {
197
+ const dependencies = /* @__PURE__ */ new Set();
198
+ for (const fk of schema.foreignKeys) dependencies.add(fk.referencesTable);
199
+ return Array.from(dependencies);
200
+ }
201
+ };
202
+ //#endregion
203
+ export { SchemaOverrideSystem };
204
+
205
+ //# sourceMappingURL=override-system.js.map