@voltro/database 0.85.0 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +666 -0
  2. package/THIRD-PARTY-NOTICES.md +5 -33
  3. package/dist/actorSnapshot.js +24 -0
  4. package/dist/arrayCodec.js +32 -0
  5. package/dist/branchExecute.js +92 -0
  6. package/dist/branching.js +106 -0
  7. package/dist/bulkInsertLimits.js +25 -0
  8. package/dist/cdcOffsetsTables.js +19 -0
  9. package/dist/changeEvent.js +41 -0
  10. package/dist/changeIdentity.js +65 -0
  11. package/dist/changeListenerCeiling.js +6 -0
  12. package/dist/columnReference.js +17 -0
  13. package/dist/columns.js +326 -0
  14. package/dist/committedChanges.js +12 -0
  15. package/dist/connectionIdentity.js +36 -0
  16. package/dist/coreTables.js +16 -0
  17. package/dist/coreTablesRegistry.js +10 -0
  18. package/dist/dataTransfers.js +22 -0
  19. package/dist/dbCause.js +222 -0
  20. package/dist/dbMetrics.js +65 -0
  21. package/dist/decimalComparison.js +27 -0
  22. package/dist/derivedRowId.js +5 -0
  23. package/dist/dialect.js +13 -0
  24. package/dist/enumRenames.js +14 -0
  25. package/dist/expiresMixin.js +10 -0
  26. package/dist/fieldEncryption.js +98 -0
  27. package/dist/frameworkTables.js +130 -0
  28. package/dist/ginIndex.js +10 -0
  29. package/dist/hybridSearch.js +32 -0
  30. package/dist/idGenerator.js +57 -0
  31. package/dist/identifierLimits.js +32 -0
  32. package/dist/index.d.ts +18 -7
  33. package/dist/index.js +81 -2183
  34. package/dist/indexAudit.js +61 -0
  35. package/dist/indexStorage.js +58 -0
  36. package/dist/joinCompiler.js +160 -0
  37. package/dist/jsonEagerCompiler.js +436 -0
  38. package/dist/jsonPatch.js +78 -0
  39. package/dist/jsonPatchAssignments.js +13 -0
  40. package/dist/jsonPath.js +31 -0
  41. package/dist/localFirstMixin.js +12 -0
  42. package/dist/migrate.js +624 -0
  43. package/dist/migration.js +18 -0
  44. package/dist/migrations/appliedAtValue.js +8 -0
  45. package/dist/migrations/applier.js +1085 -0
  46. package/dist/migrations/boot.js +196 -0
  47. package/dist/migrations/branchRehearsal.js +122 -0
  48. package/dist/migrations/columnEquivalence.js +85 -0
  49. package/dist/migrations/ddlError.js +63 -0
  50. package/dist/migrations/declaredSchema.js +162 -0
  51. package/dist/migrations/deployLedger.js +19 -0
  52. package/dist/migrations/fileBased.js +57 -0
  53. package/dist/migrations/fileContext.js +482 -0
  54. package/dist/migrations/fileDiscovery.js +318 -0
  55. package/dist/migrations/fileSkipProbes.js +117 -0
  56. package/dist/migrations/fingerprint.js +46 -0
  57. package/dist/migrations/fkCycles.js +13 -0
  58. package/dist/migrations/frameworkLiveTables.js +25 -0
  59. package/dist/migrations/idempotentDdl.js +37 -0
  60. package/dist/migrations/indexPredicate.js +50 -0
  61. package/dist/migrations/introspect.js +799 -0
  62. package/dist/migrations/ledgerRows.js +4 -0
  63. package/dist/migrations/lock.js +91 -0
  64. package/dist/migrations/migrationTableExists.js +9 -0
  65. package/dist/migrations/mssqlColumnDependencies.js +76 -0
  66. package/dist/migrations/mysqlEngine.js +11 -0
  67. package/dist/migrations/namespaceCharset.js +51 -0
  68. package/dist/migrations/planner.js +775 -0
  69. package/dist/migrations/postgresEnumCheck.js +19 -0
  70. package/dist/migrations/reactiveTriggerDrift.js +58 -0
  71. package/dist/migrations/reactiveTriggerName.js +7 -0
  72. package/dist/migrations/renameMssqlCheck.js +21 -0
  73. package/dist/migrations/restoreSnapshots.js +206 -0
  74. package/dist/migrations/resumeLedger.js +86 -0
  75. package/dist/migrations/rollingDeploy.js +106 -0
  76. package/dist/migrations/schemaOwner.js +46 -0
  77. package/dist/migrations/schemaOwnerTable.js +4 -0
  78. package/dist/migrations/softDropSnapshot.js +20 -0
  79. package/dist/migrations/sqlLimit.js +8 -0
  80. package/dist/migrations/sqliteConstraintIdentity.js +97 -0
  81. package/dist/migrations/sqliteDecimalBoot.js +22 -0
  82. package/dist/migrations/sqliteDecimalPublication.js +257 -0
  83. package/dist/migrations/sqliteDecimalReview.js +30 -0
  84. package/dist/migrations/sqliteDecimalReviewStore.js +188 -0
  85. package/dist/migrations/sqliteDecimalReviewWitness.js +38 -0
  86. package/dist/migrations/sqliteDecimalSource.js +69 -0
  87. package/dist/migrations/sqliteDecimalStage.js +135 -0
  88. package/dist/migrations/sqliteForeignKeys.js +38 -0
  89. package/dist/migrations/sqliteMigrationBatch.js +37 -0
  90. package/dist/migrations/sqliteRetainedSource.js +106 -0
  91. package/dist/migrations/sqliteRowIdentity.js +63 -0
  92. package/dist/migrations/sqliteSchemaEpoch.js +12 -0
  93. package/dist/migrations/sqliteSnapshotGuard.js +10 -0
  94. package/dist/migrations/sqliteTableContents.js +34 -0
  95. package/dist/migrations/sqliteTableCopy.js +25 -0
  96. package/dist/migrations/sqliteTableSchema.js +19 -0
  97. package/dist/migrations/sqliteWitnessProtection.js +8 -0
  98. package/dist/migrations/squash.js +81 -0
  99. package/dist/migrations/storeCharset.js +66 -0
  100. package/dist/migrations/transactionCause.js +10 -0
  101. package/dist/migrations/undeclaredCharset.js +40 -0
  102. package/dist/migrations/uniqueActiveLowering.js +39 -0
  103. package/dist/mixins.js +22 -0
  104. package/dist/mssqlDecimalDistinct.js +51 -0
  105. package/dist/mssqlJsonValueCheck.js +42 -0
  106. package/dist/mssqlTextStorage.js +9 -0
  107. package/dist/namespace.js +21 -0
  108. package/dist/{wire-CG2j7J3f.js → optimisticVersion.js} +2 -2
  109. package/dist/pendingAttribution.js +79 -0
  110. package/dist/pluginRef.js +17 -0
  111. package/dist/predicate.js +266 -0
  112. package/dist/predicateCoercion.js +13 -0
  113. package/dist/projectedColumn.js +37 -0
  114. package/dist/queryBuilder.js +382 -0
  115. package/dist/rawSql.js +8 -0
  116. package/dist/relations.js +69 -0
  117. package/dist/relationsRegistry.js +38 -0
  118. package/dist/replaceInProgress.js +53 -0
  119. package/dist/requiredColumns.js +26 -0
  120. package/dist/residency.js +52 -0
  121. package/dist/retention.js +62 -0
  122. package/dist/rowDecoder.js +123 -0
  123. package/dist/rowKey.js +8 -0
  124. package/dist/rowSchema.js +31 -0
  125. package/dist/scopeKey.js +8 -0
  126. package/dist/seed.js +33 -0
  127. package/dist/snowflake.js +26 -0
  128. package/dist/sql.d.ts +27 -6
  129. package/dist/sql.js +41 -6905
  130. package/dist/sqlCompiler.js +343 -0
  131. package/dist/sqlError.js +8 -0
  132. package/dist/sqlPoolCapacity.js +12 -0
  133. package/dist/sqliteDecimal.js +22 -0
  134. package/dist/sqliteDecimalAggregateSql.js +61 -0
  135. package/dist/sqliteDecimalColumnSql.js +108 -0
  136. package/dist/sqliteDecimalConversion.js +15 -0
  137. package/dist/sqliteDecimalCte.js +154 -0
  138. package/dist/sqliteDecimalEncodeSql.js +32 -0
  139. package/dist/sqliteDecimalOutputs.js +74 -0
  140. package/dist/sqliteDecimalPredicate.js +66 -0
  141. package/dist/sqliteDecimalQuery.js +183 -0
  142. package/dist/sqliteDecimalQueryPlan.js +37 -0
  143. package/dist/sqliteDecimalResultSql.js +44 -0
  144. package/dist/sqliteDecimalRowQuery.js +63 -0
  145. package/dist/sqliteDecimalSetQuery.js +50 -0
  146. package/dist/sqliteDecimalSource.js +63 -0
  147. package/dist/sqliteDecimalStorage.js +29 -0
  148. package/dist/sqliteDecimalSubquery.js +19 -0
  149. package/dist/sqliteDecimalWindowQuery.js +196 -0
  150. package/dist/sqliteValueCoercion.js +4 -0
  151. package/dist/stampId.js +21 -0
  152. package/dist/storeErrors.js +9 -0
  153. package/dist/storeRefusal.js +20 -0
  154. package/dist/stream.js +36 -0
  155. package/dist/subjectGraph.js +175 -0
  156. package/dist/table.js +394 -0
  157. package/dist/tableRegistry.js +16 -0
  158. package/dist/tenantSelection.js +27 -0
  159. package/dist/transactionOutcome.js +97 -0
  160. package/dist/transientContention.js +36 -0
  161. package/dist/typedStore.js +16 -0
  162. package/dist/upsertUpdate.js +20 -0
  163. package/dist/vectorEmbedding.js +31 -0
  164. package/dist/view.js +39 -0
  165. package/dist/wire.js +6 -2
  166. package/dist/writeAttribution.js +16 -0
  167. package/dist/writeRecorder.js +37 -0
  168. package/package.json +3 -3
  169. package/dist/replaceInProgress-DC88Pdiy.js +0 -3735
@@ -0,0 +1,624 @@
1
+ import { persistedColumns as e } from "./columns.js";
2
+ import { extractDbCause as t } from "./dbCause.js";
3
+ import { getEnumRenames as n } from "./enumRenames.js";
4
+ import { resolveGinOpclass as r } from "./ginIndex.js";
5
+ import { isMysqlFamily as i, isSqliteFamily as a, quoteIdent as o } from "./dialect.js";
6
+ import { isView as s, viewSql as c } from "./view.js";
7
+ import { encodeSqliteDecimalDefault as l, sqliteDecimalStorageCheck as u, sqliteDecimalStorageShape as d, sqliteDecimalStorageType as f } from "./sqliteDecimalStorage.js";
8
+ import { jsonPathSql as p } from "./jsonPath.js";
9
+ import { fireTenantCreateSeeds as m } from "./seed.js";
10
+ import { pgIndexClauses as h } from "./indexStorage.js";
11
+ import { formatDdlError as g } from "./migrations/ddlError.js";
12
+ import { lowerUniqueActive as _ } from "./migrations/uniqueActiveLowering.js";
13
+ import { findCyclicFkBackEdges as v, fkEdgeKey as y } from "./migrations/fkCycles.js";
14
+ import { withMigrationLock as b } from "./migrations/lock.js";
15
+ import { migrationAtomicityFor as ee } from "./migrations/resumeLedger.js";
16
+ import { mssqlJsonValueCheck as te } from "./mssqlJsonValueCheck.js";
17
+ import { DEFAULT_CDC_CHANNEL as x, NOTIFY_BODY_MARKER as ne, notifyFunctionName as S, reactiveTriggerName as re } from "./migrations/reactiveTriggerName.js";
18
+ import { mssqlTextStorageLength as ie } from "./mssqlTextStorage.js";
19
+ import { Effect as C, Schedule as w } from "effect";
20
+ import { SqlClient as T } from "effect/sql";
21
+ import { createLogger as ae } from "@voltro/logger";
22
+ //#region src/migrate.ts
23
+ var E = ae({ scope: "voltro:migrate" }), D = (e) => {
24
+ E.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
25
+ }, O = (e, t) => {
26
+ let n = a(t) ? d(e) : void 0;
27
+ if (n !== void 0) return f(n);
28
+ let r = e.type;
29
+ if (e.spatial) {
30
+ if (t === "postgres") return `${e.spatial.kind}(${e.spatial.geomKind},${e.spatial.srid})`;
31
+ throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${t}'. PostGIS has no portable equivalent; use store: 'postgres' (or DB_DIALECT=postgres) for spatial columns, or model the value as plain text/json.`);
32
+ }
33
+ if (r === "raw") {
34
+ if (e.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
35
+ return e.rawDdl;
36
+ }
37
+ if (r === "array" && e.arrayElement) {
38
+ if (t === "postgres") return `${O({
39
+ ...e,
40
+ type: e.arrayElement,
41
+ arrayElement: void 0
42
+ }, t)}[]`;
43
+ if (t === "mysql" || t === "mariadb") return "JSON";
44
+ if (t === "mssql") return "NVARCHAR(MAX)";
45
+ if (a(t)) return "TEXT";
46
+ }
47
+ if (r === "vector") {
48
+ let n = e.vectorDim;
49
+ if (n === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
50
+ let r = e.vectorPrecision === "half";
51
+ if (t === "postgres") return r ? `HALFVEC(${n})` : `VECTOR(${n})`;
52
+ if (t === "mysql" || t === "mariadb") return `VECTOR(${n})`;
53
+ if (t === "mssql") return "VARBINARY(MAX)";
54
+ if (a(t)) return "BLOB";
55
+ }
56
+ if (r === "interval") {
57
+ if (t === "postgres") return "INTERVAL";
58
+ if (t === "mysql" || t === "mariadb" || t === "mssql") return "BIGINT";
59
+ if (a(t)) return "INTEGER";
60
+ }
61
+ if (r === "decimal") {
62
+ let n = e.precision, r = e.scale ?? 0;
63
+ if (n === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
64
+ return a(t) ? "NUMERIC" : t === "postgres" ? `NUMERIC(${n}, ${r})` : `DECIMAL(${n}, ${r})`;
65
+ }
66
+ if (r === "bigint") return a(t) ? "INTEGER" : "BIGINT";
67
+ if (r === "enum") {
68
+ if (t === "postgres") return e.enumName ?? "TEXT";
69
+ if (t === "mysql" || t === "mariadb") return `ENUM(${(e.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
70
+ if (t === "mssql") return "NVARCHAR(255)";
71
+ if (a(t)) return "TEXT";
72
+ }
73
+ if (r === "text" && e.maxLength !== void 0 && e.generatedAs === void 0) return t === "mssql" ? `NVARCHAR(${e.maxLength})` : a(t) ? "TEXT" : `VARCHAR(${e.maxLength})`;
74
+ if (a(t)) switch (r) {
75
+ case "id": return "TEXT";
76
+ case "text": return "TEXT";
77
+ case "integer": return "INTEGER";
78
+ case "real": return "REAL";
79
+ case "boolean": return "INTEGER";
80
+ case "timestamp": return "TEXT";
81
+ case "date": return "TEXT";
82
+ case "json": return "TEXT";
83
+ case "bytes": return "BLOB";
84
+ case "reference": return e.referenceStorage === "numeric" ? "INTEGER" : "TEXT";
85
+ case "vector": return "BLOB";
86
+ case "enum": return "TEXT";
87
+ case "array": return "TEXT";
88
+ case "interval": return "INTEGER";
89
+ }
90
+ if (t === "mysql" || t === "mariadb") switch (r) {
91
+ case "id": return "VARCHAR(64)";
92
+ case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : e.generatedAs ? "TEXT" : "LONGTEXT";
93
+ case "integer": return "INT";
94
+ case "real": return "DOUBLE";
95
+ case "boolean": return "TINYINT(1)";
96
+ case "timestamp": return "DATETIME(6)";
97
+ case "date": return "DATE";
98
+ case "json": return "JSON";
99
+ case "bytes": return "LONGBLOB";
100
+ case "reference": return e.referenceStorage === "numeric" ? "BIGINT" : "VARCHAR(64)";
101
+ case "vector": return "VECTOR(0)";
102
+ case "enum": return "TEXT";
103
+ case "array": return "JSON";
104
+ case "interval": return "BIGINT";
105
+ }
106
+ if (t === "mssql") switch (r) {
107
+ case "id": return "NVARCHAR(64)";
108
+ case "text": {
109
+ let t = ie(e, !1);
110
+ return t === void 0 ? "NVARCHAR(MAX)" : `NVARCHAR(${t})`;
111
+ }
112
+ case "integer": return "INT";
113
+ case "real": return "FLOAT";
114
+ case "boolean": return "BIT";
115
+ case "timestamp": return "DATETIME2(6)";
116
+ case "date": return "DATE";
117
+ case "json": return "NVARCHAR(MAX)";
118
+ case "bytes": return "VARBINARY(MAX)";
119
+ case "reference": return e.referenceStorage === "numeric" ? "BIGINT" : "NVARCHAR(64)";
120
+ case "vector": return "VARBINARY(MAX)";
121
+ case "enum": return "NVARCHAR(255)";
122
+ case "array": return "NVARCHAR(MAX)";
123
+ case "interval": return "BIGINT";
124
+ }
125
+ switch (r) {
126
+ case "id": return "TEXT";
127
+ case "text": return "TEXT";
128
+ case "integer": return "INTEGER";
129
+ case "real": return "DOUBLE PRECISION";
130
+ case "boolean": return "BOOLEAN";
131
+ case "timestamp": return "TIMESTAMPTZ";
132
+ case "date": return "DATE";
133
+ case "json": return "JSONB";
134
+ case "bytes": return "BYTEA";
135
+ case "reference": return e.referenceStorage === "numeric" ? "BIGINT" : "TEXT";
136
+ case "vector": return "VECTOR";
137
+ case "enum": return e.enumName ?? "TEXT";
138
+ case "array": return "TEXT[]";
139
+ case "interval": return "INTERVAL";
140
+ }
141
+ }, k = (e, t) => {
142
+ if (!e.hasDefault || e.defaultFactory !== void 0) return null;
143
+ let n = a(t) ? d(e) : void 0, r = n ? l(e.defaultValue, n) : e.defaultValue;
144
+ return r === "now" ? a(t) ? "DEFAULT CURRENT_TIMESTAMP" : t === "mssql" ? "DEFAULT SYSUTCDATETIME()" : t === "mysql" || t === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof r == "boolean" ? t === "postgres" ? `DEFAULT ${r}` : `DEFAULT ${+!!r}` : typeof r == "number" ? `DEFAULT ${r}` : typeof r == "string" ? `DEFAULT '${r.replace(/'/g, "''")}'` : Array.isArray(r) ? A(r, e, t) : typeof r == "object" && r ? j(r, t) : null;
145
+ }, A = (e, t, n) => {
146
+ if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return j(e, n);
147
+ let r = O({
148
+ ...t,
149
+ type: t.arrayElement,
150
+ arrayElement: void 0
151
+ }, n);
152
+ return `DEFAULT '{${e.map((e) => typeof e == "number" || typeof e == "boolean" ? String(e) : `"${String(e).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`).join(",").replace(/'/g, "''")}}'::${r}[]`;
153
+ }, j = (e, t) => {
154
+ let n = JSON.stringify(e).replace(/'/g, "''");
155
+ return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CONVERT('${n}' USING utf8mb4))` : `DEFAULT '${n}'`;
156
+ }, M = (e, t) => {
157
+ switch (t) {
158
+ case "postgres": return `"${e}" BIGSERIAL PRIMARY KEY`;
159
+ case "mysql":
160
+ case "mariadb": return `\`${e}\` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY`;
161
+ case "mssql": return `[${e}] BIGINT IDENTITY(1,1) PRIMARY KEY`;
162
+ case "sqlite": return `"${e}" INTEGER PRIMARY KEY AUTOINCREMENT`;
163
+ case "turso": return `"${e}" INTEGER PRIMARY KEY`;
164
+ }
165
+ }, N = (e, t) => {
166
+ if (e === "restrict" && t === "mssql") return "NO ACTION";
167
+ switch (e) {
168
+ case "cascade": return "CASCADE";
169
+ case "restrict": return "RESTRICT";
170
+ case "setNull": return "SET NULL";
171
+ case "noAction": return "NO ACTION";
172
+ }
173
+ }, P = (e, t, n) => n === null ? o(e, t) : `${o(n, t)}.${o(e, t)}`, oe = 191, se = (e, t) => {
174
+ if (!e || t !== "mysql" && t !== "mariadb") return !1;
175
+ let n = O(e, t).toUpperCase();
176
+ return n.endsWith("TEXT") || n.endsWith("BLOB");
177
+ }, F = (e, t, n) => {
178
+ let r = o(e, n);
179
+ if (n !== "mysql" && n !== "mariadb") return r;
180
+ let i = t[e];
181
+ if (!i) return r;
182
+ let a = O(i, n).toUpperCase();
183
+ return a.endsWith("TEXT") || a.endsWith("BLOB") ? `${r}(${oe})` : r;
184
+ }, ce = (e, t) => {
185
+ if (!e.generatedAs) return null;
186
+ let { expr: n, stored: r } = e.generatedAs;
187
+ return t === "postgres" ? (r || D("[voltro:migrate] generated column requested as VIRTUAL but postgres only supports STORED — promoting"), `GENERATED ALWAYS AS (${n}) STORED`) : t === "mysql" || t === "mariadb" ? `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
188
+ }, le = (e, t, n, r, s = !1, c = !1) => {
189
+ if (t.type === "id" && t.idScheme?.kind === "numeric") return M(e, n);
190
+ let l = n === "mssql" ? ie(t, r?.has(e) ?? !1) : void 0, f = l === void 0 ? O(t, n) : `NVARCHAR(${l})`, p = [o(e, n), f], m = a(n) ? d(t) : void 0;
191
+ m !== void 0 && p.push(`CHECK (${u(o(e, n), m)})`), t.type === "id" && !c && p.push("PRIMARY KEY");
192
+ let h = ce(t, n);
193
+ if (h) p.push(h);
194
+ else {
195
+ t.nullable || p.push("NOT NULL"), t.unique && t.type !== "id" && !se(t, n) && p.push("UNIQUE");
196
+ let e = k(t, n);
197
+ e && p.push(e);
198
+ }
199
+ if (t.type === "reference" && t.references && !s && !i(n)) {
200
+ let e = t.references();
201
+ p.push(`REFERENCES ${o(e.tableName, n)} (${o(t.referenceColumn ?? "id", n)})`);
202
+ let r = N(t.onDelete ?? "restrict", n);
203
+ p.push(`ON DELETE ${r}`);
204
+ let i = N(t.refOnUpdate ?? "noAction", n);
205
+ i !== "NO ACTION" && p.push(`ON UPDATE ${i}`);
206
+ }
207
+ if (t.oneOf && t.oneOf.length > 0) {
208
+ let r = t.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
209
+ p.push(`CHECK (${o(e, n)} IN (${r}))`);
210
+ }
211
+ if (t.checks && t.checks.length > 0) for (let e of t.checks) p.push(`CHECK (${e})`);
212
+ if (t.type === "enum" && t.enumValues && t.enumValues.length > 0 && (n === "mssql" || a(n))) {
213
+ let r = t.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
214
+ p.push(`CHECK (${o(e, n)} IN (${r}))`);
215
+ }
216
+ if (t.type === "json" || t.type === "array") {
217
+ let r = o(e, n);
218
+ a(n) ? p.push(`CHECK (${r} IS NULL OR json_valid(${r}))`) : n === "mssql" && p.push(t.type === "json" ? `CHECK ${te(r)}` : `CHECK (${r} IS NULL OR ISJSON(${r}) = 1)`);
219
+ }
220
+ return p.join(" ");
221
+ }, I = (e, t) => {
222
+ if (t.charset !== void 0) {
223
+ if (!i(e)) throw Error(`store charset '${t.charset}' is declared for a ${e} store — a character set is a MySQL / MariaDB setting. Remove \`charset\` from \`store\` in app.config.ts, or declare \`store: { dialect: 'mysql' | 'mariadb', charset }\`.`);
224
+ return t.charset;
225
+ }
226
+ }, L = (t, n, r = null, a, s) => {
227
+ let c = /* @__PURE__ */ new Set();
228
+ for (let e of t.appliedIndexes ?? []) for (let t of e.fields) typeof t == "string" && c.add(t);
229
+ for (let e of t.appliedUniques ?? []) for (let t of e.fields) typeof t == "string" && c.add(t);
230
+ for (let [e, n] of Object.entries(t.fields)) n.unique && c.add(e);
231
+ let l = t.appliedPrimaryKey ?? [];
232
+ for (let e of l) c.add(e);
233
+ let u = l.length > 0, d = e(t).map((e) => [e, t.fields[e]]).map(([e, t]) => ` ${le(e, t, n, c, a?.has(e) ?? !1, u)}`).join(",\n"), f = (t.appliedUniques ?? []).map((e) => {
234
+ let r = e.fields.map((e) => F(e, t.fields, n)).join(", ");
235
+ return ` CONSTRAINT ${o(e.name, n)} UNIQUE (${r})`;
236
+ }), p = (t.appliedChecks ?? []).map((e) => ` CONSTRAINT ${o(e.name, n)} CHECK (${e.expr})`), m = u ? [` PRIMARY KEY (${l.map((e) => F(e, t.fields, n)).join(", ")})`] : [], h = i(n) ? Object.entries(t.fields).flatMap(([e, i]) => {
237
+ if (i.type !== "reference" || !i.references || (a?.has(e) ?? !1)) return [];
238
+ let s = i.references(), c = N(i.onDelete ?? "restrict", n), l = N(i.refOnUpdate ?? "noAction", n);
239
+ return [` CONSTRAINT ${o(`${t.tableName}_${e}_fkey`, n)} FOREIGN KEY (${o(e, n)}) REFERENCES ${P(s.tableName, n, r)} (${o(i.referenceColumn ?? "id", n)}) ON DELETE ${c}${l === "NO ACTION" ? "" : ` ON UPDATE ${l}`}`];
240
+ }) : [], g = [
241
+ ...m,
242
+ ...f,
243
+ ...p,
244
+ ...h
245
+ ], _ = g.length > 0 ? `${d},\n${g.join(",\n")}` : d, v = P(t.tableName, n, r);
246
+ if (n === "mssql") {
247
+ let e = r === null ? t.tableName : `${r}.${t.tableName}`, n = `CREATE TABLE ${v} (\n${_}\n)`;
248
+ return `IF OBJECT_ID('${e.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${n.replace(/'/g, "''")}');`;
249
+ }
250
+ return [
251
+ `CREATE TABLE IF NOT EXISTS ${v} (`,
252
+ _,
253
+ `)${s !== void 0 && i(n) ? ` DEFAULT CHARACTER SET ${s}` : ""};`
254
+ ].join("\n");
255
+ }, ue = (e, t, n) => {
256
+ let r = `${e}_${t.name}_fts`, i = (e) => o(e, n), a = t.columns.map(i).join(", "), s = t.columns.map((e) => `new.${i(e)}`).join(", "), c = [];
257
+ return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${i(r)} USING fts5(${a}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_ai`)} AFTER INSERT ON ${i(e)} BEGIN\n INSERT INTO ${i(r)} (${a}, row_id) VALUES (${s}, new.${i("id")});\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_ad`)} AFTER DELETE ON ${i(e)} BEGIN\n DELETE FROM ${i(r)} WHERE row_id = old.${i("id")};\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_au`)} AFTER UPDATE ON ${i(e)} BEGIN\n DELETE FROM ${i(r)} WHERE row_id = old.${i("id")};\n INSERT INTO ${i(r)} (${a}, row_id) VALUES (${s}, new.${i("id")});\nEND;`), c;
258
+ }, R = (e) => e.appliedFullText ?? [], z = (e, t) => {
259
+ let n = [];
260
+ for (let r of R(e)) if (t === "postgres") {
261
+ let i = r.config ?? "english", a = `${r.name}_tsv`, s = r.columns.map((e) => {
262
+ let n = r.weights?.[e] ?? "A";
263
+ return `setweight(to_tsvector('${i}', coalesce(${o(e, t)}, '')), '${n}')`;
264
+ }).join(" || ");
265
+ n.push(`ALTER TABLE ${o(e.tableName, t)} ADD COLUMN IF NOT EXISTS ${o(a, t)} tsvector GENERATED ALWAYS AS (${s}) STORED;`), n.push(`CREATE INDEX IF NOT EXISTS ${o(r.name, t)} ON ${o(e.tableName, t)} USING GIN (${o(a, t)});`);
266
+ } else if (t === "mysql" || t === "mariadb") {
267
+ let i = r.columns.map((e) => o(e, t)).join(", ");
268
+ n.push(`CREATE FULLTEXT INDEX ${o(r.name, t)} ON ${o(e.tableName, t)} (${i});`);
269
+ } else t === "turso" || t === "sqlite" && n.push(...ue(e.tableName, r, t));
270
+ return n;
271
+ }, B = (e, t, n = null) => {
272
+ let r = [], i = /* @__PURE__ */ new Set(), s = (e) => e.join("\0"), c = (e, n, r, i, a) => {
273
+ if (t === "postgres") return h(r, i, a);
274
+ let o = {
275
+ using: "",
276
+ withSuffix: "",
277
+ skip: !1
278
+ };
279
+ return r === void 0 || r === "btree" ? o : r === "gist" ? (D(`[voltro:migrate] index '${e}' on '${n}': ${t} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), o) : r === "gin" ? t === "mysql" || t === "mariadb" ? (D(`[voltro:migrate] gin index '${e}' on '${n}': ${t} has no GIN access method — skipping (a btree on a JSON/array column can't serve containment/membership queries). Add a generated column for the specific path you query, then index that with .expressionIndex(...).`), {
280
+ using: "",
281
+ withSuffix: "",
282
+ skip: !0
283
+ }) : (D(`[voltro:migrate] gin index '${e}' on '${n}': ${t} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), o) : r === "brin" ? (D(`[voltro:migrate] brin index '${e}' on '${n}': ${t} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), o) : r === "hnsw" ? (D(`[voltro:migrate] hnsw index '${e}' on '${n}': ${t} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
284
+ using: "",
285
+ withSuffix: "",
286
+ skip: !0
287
+ }) : o;
288
+ }, l = (n, r) => {
289
+ let i = n.map((n) => {
290
+ if (typeof n == "string") return F(n, e.fields, t);
291
+ if ("jsonPath" in n) {
292
+ let { column: e, path: r, numeric: i } = n.jsonPath;
293
+ return `(${p(e, r, t, { numeric: i })})`;
294
+ }
295
+ return `(${n.expr})`;
296
+ });
297
+ return r !== void 0 && i.length === 1 ? `${i[0]} ${r}` : i.join(", ");
298
+ }, u = (r, i, s, u, d, f = "", p = !1) => {
299
+ let m = s[0], h = typeof m == "string" ? e.fields[m]?.vectorPrecision : void 0, g = c(r, i, u, d, h);
300
+ if (g.skip) return "";
301
+ let _ = l(s, g.opclass), v = `${g.withSuffix}${f}`, y = P(i, t, n), b = p ? "UNIQUE " : "";
302
+ return a(t) && n !== null ? `CREATE ${b}INDEX IF NOT EXISTS ${P(r, t, n)} ON ${o(i, t)} (${_})${v};` : t === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${r}') CREATE ${b}INDEX ${o(r, t)} ON ${y}${g.using} (${_})${v};` : t === "mysql" ? `CREATE ${b}INDEX ${o(r, t)} ON ${y}${g.using} (${_})${v};` : `CREATE ${b}INDEX IF NOT EXISTS ${o(r, t)} ON ${y}${g.using} (${_})${v};`;
303
+ };
304
+ if (t === "mysql" || t === "mariadb") for (let [n, i] of Object.entries(e.fields)) {
305
+ if (!i.unique || i.type === "id" || !se(i, t)) continue;
306
+ let a = u(`${e.tableName}_${n}_key`, e.tableName, [n], void 0, void 0, "", !0);
307
+ a && r.push(a);
308
+ }
309
+ if (r.push(...z(e, t)), t === "mssql") for (let n of R(e)) D(`[voltro:migrate] full-text index '${n.name}' on '${e.tableName}': ${t} uses a ranked LIKE search at query time (no native FTS catalog required).`);
310
+ for (let n of e.appliedIndexes) {
311
+ if ((t === "mariadb" || t === "mssql") && n.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
312
+ D(`[voltro:migrate] json-path index '${n.name}' on '${e.tableName}': ${t} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${t}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
313
+ continue;
314
+ }
315
+ if (n.unique && n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${n.name}' on '${e.tableName}' cannot be expressed on ${t} — it has no partial (WHERE) index support, and a full UNIQUE index would forbid re-creating a soft-deleted row. Use a generated STORED column (text().generatedAs("CASE WHEN \`deletedAt\` IS NULL THEN <key> ELSE NULL END", { stored: true })) + .unique([...]) on ${t}, or move this table to postgres/sqlite/mssql.`);
316
+ let a = "";
317
+ n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb" ? D(`[voltro:migrate] partial index '${n.name}' on '${e.tableName}': ${t} does not support WHERE clauses on indexes — emitting a full index`) : a = ` WHERE ${n.where}`);
318
+ let o = u(n.name, e.tableName, n.fields, n.kind, n.kindOptions, a, n.unique === !0);
319
+ o !== "" && r.push(o), n.fields.every((e) => typeof e == "string") && i.add(s(n.fields));
320
+ }
321
+ for (let [t, n] of Object.entries(e.fields)) {
322
+ if (n.type !== "reference" || n.fkAutoIndex === !1 || i.has(s([t]))) continue;
323
+ let a = `${e.tableName}_${t}_idx`;
324
+ r.push(u(a, e.tableName, [t])), i.add(s([t]));
325
+ }
326
+ return r.join("\n");
327
+ }, V = (e, t, n = x) => {
328
+ if (t !== "postgres") return [];
329
+ let r = e.filter((e) => e.isReactive !== !1);
330
+ return r.length === 0 ? [] : [H(n), ...r.map((e) => de(e, n))];
331
+ }, H = (e) => `
332
+ CREATE OR REPLACE FUNCTION ${S(e)}() RETURNS trigger AS $$
333
+ DECLARE
334
+ payload TEXT;
335
+ pk JSONB;
336
+ BEGIN
337
+ -- ${ne}
338
+ payload := json_build_object(
339
+ 'table', TG_TABLE_NAME,
340
+ -- The schema the write landed in. Under namespace isolation this is
341
+ -- the tenant schema (tenant_<id>); the CDC consumer uses it so a
342
+ -- write in tenant A namespace does not spuriously wake tenant B
343
+ -- subscriptions on the same-named table. public/absent = shared.
344
+ 'schema', TG_TABLE_SCHEMA,
345
+ 'op', lower(TG_OP),
346
+ 'old', CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE row_to_json(OLD) END,
347
+ 'new', CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE row_to_json(NEW) END
348
+ )::text;
349
+ -- pg_notify hard-caps the payload at 8000 bytes; a wide reactive row
350
+ -- (big text / json column) would otherwise abort the write itself. So the
351
+ -- images are dropped — but the PRIMARY KEY is kept, and that is the whole
352
+ -- difference between a slow path and data loss. A subscription re-queries on
353
+ -- an image-less event and is fine either way; a TAP (search index, analytics
354
+ -- mirror, cdc-out, row history) has nothing to apply and used to drop the
355
+ -- change silently and forever. With the key, the CDC consumer re-reads the
356
+ -- row before anyone sees the event (sql-postgres/src/cdcRehydrate.ts).
357
+ --
358
+ -- to_jsonb(REC) -> 'id' rather than REC.id: a reactive table with no id
359
+ -- column would make the field reference a runtime error inside the trigger,
360
+ -- which aborts the user's WRITE. The json operator answers NULL instead.
361
+ IF octet_length(payload) >= 7999 THEN
362
+ pk := CASE WHEN TG_OP = 'DELETE' THEN to_jsonb(OLD) -> 'id' ELSE to_jsonb(NEW) -> 'id' END;
363
+ payload := json_build_object(
364
+ 'table', TG_TABLE_NAME,
365
+ 'schema', TG_TABLE_SCHEMA,
366
+ 'op', lower(TG_OP),
367
+ 'old', NULL,
368
+ 'new', NULL,
369
+ 'id', pk,
370
+ 'oversized', true
371
+ )::text;
372
+ -- A key can itself be oversized (a pathological text primary key). Carrying
373
+ -- it would put us back where the cap check started: pg_notify raises, and
374
+ -- the trigger takes the user's write down with it. Dropping it costs the
375
+ -- re-read and is counted as unrecovered — never the write.
376
+ IF octet_length(payload) >= 7999 THEN
377
+ payload := json_build_object(
378
+ 'table', TG_TABLE_NAME,
379
+ 'schema', TG_TABLE_SCHEMA,
380
+ 'op', lower(TG_OP),
381
+ 'old', NULL,
382
+ 'new', NULL,
383
+ 'oversized', true
384
+ )::text;
385
+ END IF;
386
+ END IF;
387
+ PERFORM pg_notify('${e}', payload);
388
+ RETURN COALESCE(NEW, OLD);
389
+ END;
390
+ $$ LANGUAGE plpgsql;
391
+ `.trim(), U = (e, t) => {
392
+ let n = re(e.tableName, t);
393
+ return [
394
+ `ALTER TABLE "${e.tableName}" REPLICA IDENTITY FULL`,
395
+ `DROP TRIGGER IF EXISTS ${n} ON "${e.tableName}"`,
396
+ `CREATE TRIGGER ${n}\nAFTER INSERT OR UPDATE OR DELETE ON "${e.tableName}"\nFOR EACH ROW EXECUTE FUNCTION ${S(t)}()`
397
+ ];
398
+ }, de = (e, t) => U(e, t).map((e) => `${e};`).join("\n"), fe = (e) => {
399
+ if (e.dialect !== "postgres") return [];
400
+ let t = e.channel ?? "framework_changes", n = new Map(e.tables.map((e) => [e.tableName, e])), r = [], i = e.missing.filter((e) => n.has(e));
401
+ (i.length > 0 || e.functionOutdated === !0) && r.push(H(t));
402
+ for (let e of i) r.push(...U(n.get(e), t));
403
+ for (let n of e.stale) r.push(`DROP TRIGGER IF EXISTS ${re(n, t)} ON "${n}"`), r.push(`ALTER TABLE "${n}" REPLICA IDENTITY DEFAULT`);
404
+ return r;
405
+ }, W = (e, t) => {
406
+ if (t !== "postgres") return;
407
+ let n = [];
408
+ return pe(e) && n.push("CREATE EXTENSION IF NOT EXISTS vector;"), me(e) && n.push("CREATE EXTENSION IF NOT EXISTS postgis;"), e.some((e) => e.appliedIndexes?.some((e) => e.kind === "gin" && r(e.kindOptions)?.split(".").at(-1) === "gin_trgm_ops")) && n.push("CREATE EXTENSION IF NOT EXISTS pg_trgm;"), n.length === 0 ? void 0 : n.join("\n");
409
+ }, pe = (e) => e.some((e) => Object.values(e.fields).some((e) => e.type === "vector")), me = (e) => e.some((e) => Object.values(e.fields).some((e) => e.spatial !== void 0)), G = (e) => {
410
+ let t = new Map(e.map((e) => [e.tableName, e])), n = v(e.map((e) => e.tableName), (e) => {
411
+ let n = t.get(e);
412
+ return n ? Object.entries(n.fields).flatMap(([e, n]) => {
413
+ if (n.type !== "reference" || !n.references) return [];
414
+ let r = n.references().tableName;
415
+ return t.has(r) ? [{
416
+ column: e,
417
+ target: r
418
+ }] : [];
419
+ }) : [];
420
+ }), r = [];
421
+ for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type === "reference" && i.references && n.has(y(t.tableName, e)) && r.push({
422
+ table: t.tableName,
423
+ column: e,
424
+ targetTable: i.references().tableName,
425
+ targetColumn: i.referenceColumn ?? "id",
426
+ onDelete: i.onDelete ?? "restrict",
427
+ onUpdate: i.refOnUpdate ?? "noAction"
428
+ });
429
+ let i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(), o = [], s = (e) => {
430
+ if (!(i.has(e.tableName) || a.has(e.tableName))) {
431
+ a.add(e.tableName);
432
+ for (let [r, i] of Object.entries(e.fields)) {
433
+ if (i.type !== "reference" || !i.references || n.has(y(e.tableName, r))) continue;
434
+ let a = i.references();
435
+ if (a.tableName === e.tableName) continue;
436
+ let o = t.get(a.tableName);
437
+ o && s(o);
438
+ }
439
+ a.delete(e.tableName), i.add(e.tableName), o.push(e);
440
+ }
441
+ };
442
+ for (let t of e) s(t);
443
+ return {
444
+ sorted: o,
445
+ cyclic: r
446
+ };
447
+ }, K = (e, t, n = null) => {
448
+ let r = (e) => n ? `${o(n, t)}.${o(e, t)}` : o(e, t), i = `${e.table}_${e.column}_fkey`, a = N(e.onDelete, t), s = N(e.onUpdate, t), c = ` ON DELETE ${a}${s === "NO ACTION" ? "" : ` ON UPDATE ${s}`}`, l = (n) => `ADD CONSTRAINT ${o(i, t)} FOREIGN KEY ${n ? "IF NOT EXISTS " : ""}(${o(e.column, t)}) REFERENCES ${r(e.targetTable)} (${o(e.targetColumn, t)})${c}`;
449
+ switch (t) {
450
+ case "postgres": return [
451
+ "DO $$ BEGIN",
452
+ " IF NOT EXISTS (SELECT 1 FROM pg_constraint",
453
+ ` WHERE conname = '${i}' AND conrelid = '${n ? `${n}.` : ""}${e.table}'::regclass) THEN`,
454
+ ` ALTER TABLE ${r(e.table)} ${l(!1)};`,
455
+ " END IF;",
456
+ "END $$;"
457
+ ].join("\n");
458
+ case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${i}') ALTER TABLE ${r(e.table)} ${l(!1)};`;
459
+ case "mariadb": return `ALTER TABLE ${r(e.table)} ${l(!0)};`;
460
+ default: return `ALTER TABLE ${r(e.table)} ${l(!1)};`;
461
+ }
462
+ }, q = (e, t) => {
463
+ let n = /* @__PURE__ */ new Map();
464
+ if (a(t)) return n;
465
+ for (let t of e) {
466
+ let e = n.get(t.table) ?? /* @__PURE__ */ new Set();
467
+ e.add(t.column), n.set(t.table, e);
468
+ }
469
+ return n;
470
+ }, J = (e, t) => {
471
+ if (t !== "postgres") return "";
472
+ let n = /* @__PURE__ */ new Map();
473
+ for (let t of e) for (let e of Object.values(t.fields)) {
474
+ if (e.type !== "enum" || !e.enumName || !e.enumValues) continue;
475
+ let t = n.get(e.enumName);
476
+ if (t && t.length !== e.enumValues.length) throw Error(`dbEnum '${e.enumName}' declared with conflicting value sets across tables. Define it once and re-use the handle.`);
477
+ n.set(e.enumName, e.enumValues);
478
+ }
479
+ if (n.size === 0) return "";
480
+ let r = [];
481
+ for (let [e, t] of n) {
482
+ let n = t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
483
+ r.push(`DO $$ BEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '${e.replace(/'/g, "''")}') THEN\n CREATE TYPE "${e}" AS ENUM (${n});\n END IF;\nEND $$;`);
484
+ }
485
+ return r.join("\n\n");
486
+ }, Y = (e, t) => {
487
+ if (t !== "postgres") return "";
488
+ let r = /* @__PURE__ */ new Set();
489
+ for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && r.add(e.enumName);
490
+ let i = [];
491
+ for (let e of r) for (let { from: t, to: r } of n(e)) {
492
+ let n = e.replace(/'/g, "''"), a = t.replace(/'/g, "''"), o = r.replace(/'/g, "''");
493
+ i.push(`DO \$\$
494
+ DECLARE
495
+ type_oid oid;
496
+ BEGIN
497
+ SELECT oid INTO type_oid FROM pg_type WHERE typname = '${n}';\n IF type_oid IS NOT NULL\n AND EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${a}')\n AND NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${o}') THEN\n IF current_setting('server_version_num')::int >= 140000 THEN\n EXECUTE format('ALTER TYPE %I RENAME VALUE %L TO %L', '${n}', '${a}', '${o}');\n ELSE\n UPDATE pg_enum SET enumlabel = '${o}' WHERE enumtypid = type_oid AND enumlabel = '${a}';\n END IF;\n END IF;\nEND $$;`);
498
+ }
499
+ return i.join("\n\n");
500
+ }, he = (e) => {
501
+ let t = [], n = [];
502
+ for (let r of e) s(r) ? n.push(r) : t.push(r);
503
+ return {
504
+ tables: t,
505
+ views: n
506
+ };
507
+ }, X = (e, t, n = x, r = {}) => {
508
+ let i = I(t, r), { tables: o, views: s } = he(e), { sorted: l, cyclic: u } = G(o.map((e) => _(e, t))), d = q(u, t), f = [], p = W(l, t);
509
+ p && f.push(p);
510
+ let m = Y(l, t);
511
+ m && f.push(m);
512
+ let h = J(l, t);
513
+ h && f.push(h);
514
+ for (let e of l) f.push(L(e, t, null, d.get(e.tableName), i));
515
+ if (!a(t)) for (let e of u) f.push(K(e, t));
516
+ for (let e of l) {
517
+ let n = B(e, t);
518
+ n && f.push(n);
519
+ }
520
+ f.push(...V(l, t, n));
521
+ for (let e of s) f.push(c(e, t));
522
+ return f.join("\n\n") + "\n";
523
+ }, ge = (e, t, n, r) => {
524
+ let i = o(e, t);
525
+ switch (t) {
526
+ case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${i};`;
527
+ case "mysql":
528
+ case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${i}${r === void 0 ? "" : ` CHARACTER SET ${r}`};`;
529
+ case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${i.replace(/'/g, "''")}');`;
530
+ case "turso":
531
+ case "sqlite": return `ATTACH DATABASE '${(n ?? `${e}.db`).replace(/'/g, "''")}' AS ${i};`;
532
+ }
533
+ }, _e = (e, t, n, r, i = {}) => {
534
+ let s = I(t, i), c = ge(n, t, r, s), l = o(n, t);
535
+ if (t === "postgres") return `${c}\n\nSET search_path TO ${l};\n\n${X(e, t)}`;
536
+ let { sorted: u, cyclic: d } = G(e), f = q(d, t), p = [c];
537
+ for (let e of u) p.push(L(e, t, n, f.get(e.tableName), s));
538
+ if (!a(t)) for (let e of d) p.push(K(e, t, n));
539
+ for (let e of u) {
540
+ let r = B(e, t, n);
541
+ r && p.push(r);
542
+ }
543
+ return p.join("\n\n") + "\n";
544
+ }, ve = (e, t, n = x) => {
545
+ let { sorted: r, cyclic: i } = G(e), a = q(i, t), o = [], s = Y(r, t);
546
+ s && o.push(s);
547
+ let c = J(r, t);
548
+ c && o.push(c);
549
+ for (let e of r) o.push(L(e, t, null, a.get(e.tableName)));
550
+ for (let e of i) o.push(K(e, t));
551
+ return o.push(...V(r, t, n)), o.join("\n\n") + "\n";
552
+ }, ye = (e, t = "postgres", n = x, r = {}) => C.gen(function* () {
553
+ let i = yield* T.SqlClient, a = X(e, t, n, { charset: r.charset });
554
+ yield* b(i, Q(i, a, t, r.retryFilter), { schema: r.lockSchema }).pipe(C.orDie);
555
+ }), be = (e, n) => {
556
+ let r = t(n);
557
+ return (r.dbErrno === 1061 || r.dbCode === "ER_DUP_KEYNAME") && /^\s*CREATE\s+(UNIQUE\s+|FULLTEXT\s+)?INDEX\b/i.test(e);
558
+ }, xe = (e, t) => C.gen(function* () {
559
+ let n = yield* T.SqlClient, r = e.flatMap((e) => z(e, t));
560
+ for (let e of r) yield* n.unsafe(e).pipe(C.catchIf((t) => be(e, t), () => C.succeed([])));
561
+ return r.length;
562
+ }), Se = (e, t) => {
563
+ let n = e.trimStart();
564
+ return !!(/\bIF NOT EXISTS\b/i.test(n) || /^CREATE OR REPLACE\b/i.test(n) || /^DROP\s[\s\S]*?\bIF EXISTS\b/i.test(n) || /^SET search_path\b/i.test(n) || /^USE[\s`"[]/i.test(n) || /^IF\s/i.test(n) || (t === "mysql" || t === "mariadb") && /^CREATE\s+(UNIQUE\s+|FULLTEXT\s+)?INDEX\b/i.test(n));
565
+ }, Ce = () => {
566
+ let e = Number(process.env.VOLTRO_MIGRATION_DDL_RETRIES);
567
+ return Number.isInteger(e) && e >= 0 ? e : 4;
568
+ }, we = (e) => ({
569
+ while: e,
570
+ times: Ce(),
571
+ schedule: w.jittered(w.exponential("50 millis"))
572
+ }), Z = class {
573
+ statement;
574
+ error;
575
+ _tag = "DdlStatementFailed";
576
+ constructor(e, t) {
577
+ this.statement = e, this.error = t;
578
+ }
579
+ }, Te = (e, t, n) => C.gen(function* () {
580
+ let r = yield* T.SqlClient, i = n !== void 0 && ee(t) === "per-operation";
581
+ for (let a of Ee(e)) {
582
+ process.env.VOLTRO_MIGRATE_DEBUG === "1" && E.debug(`EXEC: ${a.replace(/\s+/g, " ").slice(0, 180)}`);
583
+ let e = r.unsafe(a).pipe(C.catchIf((e) => be(a, e), () => C.succeed([])), C.tapError((e) => C.sync(() => {
584
+ E.error(g("migrate: statement failed", a, e, 200));
585
+ })));
586
+ yield* (i && n !== void 0 && Se(a, t) ? e.pipe(C.retry(we((e) => n(e) === "retry"))) : e).pipe(C.mapError((e) => new Z(a, e)));
587
+ }
588
+ }), Q = (e, t, n, r) => {
589
+ let i = n === "turso" ? Te(t, n, r) : e.withTransaction(Te(t, n, r)), a = (e) => e instanceof Z ? e.error : e;
590
+ return (r !== void 0 && ee(n) === "plan-transactional" ? i.pipe(C.retry(we((e) => r(a(e)) === "retry"))) : i).pipe(C.catch((e) => C.die(e instanceof Z ? Error(g("migrate: statement failed", e.statement, e.error, 400).trim()) : e)));
591
+ }, Ee = (e) => {
592
+ let t = [], n = "", r = !1;
593
+ for (let i = 0; i < e.length; i++) {
594
+ if (e.slice(i, i + 2) === "$$") {
595
+ r = !r, n += "$$", i += 1;
596
+ continue;
597
+ }
598
+ let a = e[i], o = /^\s*CREATE\s+(?:TEMP(?:ORARY)?\s+)?TRIGGER\b[\s\S]*\bBEGIN\b/i.test(n) && !/\bEND\s*$/i.test(n);
599
+ if (a === ";" && !r && o) n += a;
600
+ else if (a === ";" && !r) {
601
+ let e = n.trim();
602
+ e.length > 0 && t.push(e), n = "";
603
+ } else n += a;
604
+ }
605
+ let i = n.trim();
606
+ return i.length > 0 && t.push(i), t;
607
+ }, De = async (e, t, n = "postgres", r = {}) => {
608
+ await C.runPromise(ye(e, n, void 0, r).pipe(C.provide(t)));
609
+ }, $ = (e, t, n = "postgres", r = {}) => C.gen(function* () {
610
+ let i = _e(e, n, t, void 0, { charset: r.charset }), a = yield* T.SqlClient;
611
+ yield* b(a, Q(a, i, n, r.retryFilter), { schema: r.lockSchema }).pipe(C.orDie);
612
+ }), Oe = async (e, t, n, r = "postgres", i = {}) => {
613
+ await C.runPromise($(e, t, r, i).pipe(C.provide(n))), await m({
614
+ namespace: t,
615
+ dialect: r
616
+ });
617
+ }, ke = async (e, t, n = "postgres", r = {}) => {
618
+ await C.runPromise(C.gen(function* () {
619
+ let t = ve(e, n), i = yield* T.SqlClient;
620
+ yield* b(i, Q(i, t, n, r.retryFilter), { schema: r.lockSchema }).pipe(C.orDie);
621
+ }).pipe(C.provide(t)));
622
+ };
623
+ //#endregion
624
+ export { x as DEFAULT_CDC_CHANNEL, $ as applyNamespacedSchema, ye as applySchema, A as defaultArrayClause, k as defaultClause, j as defaultJsonClause, ve as emitFrameworkBootstrapSql, ge as emitNamespaceProvisionDdl, _e as emitNamespacedSchemaSql, X as emitSchemaSql, xe as ensureFullTextIndexes, Y as enumRenameBlocks, J as enumTypeBlocks, W as extensionBlocks, z as fullTextIndexSql, Se as isReRunSafeDdl, M as numericIdSql, Oe as provisionTenantNamespace, fe as reactiveTriggerRepairSql, ke as runFrameworkBootstrap, De as runMigrate, Ee as splitStatements, O as sqlType };
@@ -0,0 +1,18 @@
1
+ //#region src/migration.ts
2
+ var e = (e, { apply: t, undo: n }) => ({
3
+ name: e,
4
+ apply: t,
5
+ undo: n
6
+ }), t = (t) => {
7
+ if (!/^\d{14}_/.test(t.id)) throw Error(`migration "${t.id}": id must start with a 14-digit timestamp followed by "_" (convention: <YYYYMMDDHHMMSS>_<slug>)`);
8
+ let n = t.steps({ step: e });
9
+ if (n.length === 0) throw Error(`migration "${t.id}": at least one step is required`);
10
+ return {
11
+ __migration: !0,
12
+ id: t.id,
13
+ name: t.name,
14
+ steps: n
15
+ };
16
+ }, n = (e) => typeof e == "object" && !!e && "__migration" in e && e.__migration === !0;
17
+ //#endregion
18
+ export { t as defineMigration, n as isMigrationDefinition };
@@ -0,0 +1,8 @@
1
+ import { Effect as e } from "effect";
2
+ //#region src/migrations/appliedAtValue.ts
3
+ var t = (t, n) => t.onDialectOrElse({
4
+ mysql: () => e.succeed(n.replace("T", " ").replace("Z", "")),
5
+ orElse: () => e.succeed(n)
6
+ });
7
+ //#endregion
8
+ export { t as appliedAtValue };