@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,69 @@
1
+ import { authoredError as e } from "./dbCause.js";
2
+ //#region src/relations.ts
3
+ var t = (e) => typeof e == "function" ? e : () => e, n = (e, n) => ({
4
+ kind: "one",
5
+ target: t(e),
6
+ ...n?.foreignKey === void 0 ? {} : { foreignKey: n.foreignKey },
7
+ ...n?.sourceKey === void 0 ? {} : { sourceKey: n.sourceKey }
8
+ }), r = (e, n) => ({
9
+ kind: "many",
10
+ target: t(e),
11
+ ...n?.foreignKey === void 0 ? {} : { foreignKey: n.foreignKey },
12
+ ...n?.sourceKey === void 0 ? {} : { sourceKey: n.sourceKey }
13
+ }), i = (e, n) => ({
14
+ kind: "manyToMany",
15
+ target: t(e),
16
+ through: t(n.through),
17
+ sourceKey: n.sourceKey,
18
+ targetKey: n.targetKey
19
+ }), a = {
20
+ one: n,
21
+ many: r,
22
+ manyToMany: i
23
+ }, o = (e, t) => {
24
+ let n = typeof e == "function" ? e : () => e;
25
+ return {
26
+ source: n,
27
+ relations: t(a, n())
28
+ };
29
+ }, s = (e) => {
30
+ if (typeof e != "object" || !e) return !1;
31
+ let t = e;
32
+ return typeof t.source != "function" || typeof t.relations != "object" || t.relations === null ? !1 : Object.keys(t.relations).length === 0;
33
+ }, c = (e) => {
34
+ if (typeof e != "object" || !e) return !1;
35
+ let t = e;
36
+ if (typeof t.source != "function" || typeof t.relations != "object" || t.relations === null) return !1;
37
+ let n = Object.values(t.relations);
38
+ return n.length !== 0 && n.every((e) => {
39
+ let t = e?.kind;
40
+ return t === "one" || t === "many" || t === "manyToMany";
41
+ });
42
+ }, l = n, u = r, d = i, f = (t, n, r) => {
43
+ let i = [];
44
+ for (let [e, r] of Object.entries(t.fields)) r.type === "reference" && r.references?.()?.tableName === n && i.push(e);
45
+ if (i.length === 0) throw e(`relation '${r}': no reference() column on '${t.tableName}' points at '${n}'. Either add one or pass an explicit { foreignKey: '<column>' }.`);
46
+ if (i.length > 1) throw e(`relation '${r}': multiple reference() columns on '${t.tableName}' point at '${n}' (${i.join(", ")}). Pass an explicit { foreignKey: '<column>' } to disambiguate.`);
47
+ return i[0];
48
+ }, p = (e) => Object.keys(e.fields), m = (e, t) => Object.prototype.hasOwnProperty.call(e.fields, t), h = (e) => {
49
+ let t = p(e);
50
+ return t.length === 0 ? "<none>" : t.join(", ");
51
+ }, g = (t, n, r) => {
52
+ let i = `relation '${n}' on '${t.tableName}'`, a = r.target();
53
+ if (r.kind === "manyToMany") {
54
+ let n = r.through();
55
+ for (let [o, s] of [["sourceKey", r.sourceKey], ["targetKey", r.targetKey]]) if (!m(n, s)) throw e(`${i}: manyToMany ${o} '${s}' is not a column on the junction table '${n.tableName}'. Both keys name columns on the JUNCTION — ${o === "sourceKey" ? `sourceKey points at '${t.tableName}'` : `targetKey points at '${a.tableName}'`}. Columns on '${n.tableName}': ${h(n)}.`);
56
+ } else {
57
+ if (r.sourceKey !== void 0 && r.foreignKey !== void 0) throw e(`${i}: pass EITHER sourceKey OR foreignKey, not both. sourceKey '${r.sourceKey}' names a column on the source ('${t.tableName}') holding the target's id; foreignKey '${r.foreignKey}' names a column on the target ('${a.tableName}') holding the source's id. With both, foreignKey is ignored.`);
58
+ if (r.sourceKey !== void 0 && !m(t, r.sourceKey)) {
59
+ let n = m(a, r.sourceKey);
60
+ throw e(`${i}: sourceKey '${r.sourceKey}' is not a column on the source table '${t.tableName}'.` + (n ? ` It IS a column on the target '${a.tableName}' — you probably want { foreignKey: '${r.sourceKey}' }, which names the column on the TARGET that holds this table's id.` : ` Columns on '${t.tableName}': ${h(t)}.`));
61
+ }
62
+ if (r.foreignKey !== void 0 && !m(a, r.foreignKey)) {
63
+ let n = m(t, r.foreignKey);
64
+ throw e(`${i}: foreignKey '${r.foreignKey}' is not a column on the target table '${a.tableName}'.` + (n ? ` It IS a column on the source '${t.tableName}' — you want { sourceKey: '${r.foreignKey}' }. foreignKey names the column on the TARGET that points back at this table; sourceKey names the column on THIS table that holds the target's id.` : ` Columns on '${a.tableName}': ${h(a)}.`));
65
+ }
66
+ }
67
+ };
68
+ //#endregion
69
+ export { f as inferForeignKey, s as isEmptyRelationsSpec, c as isRelationsSpec, u as many, d as manyToMany, l as one, o as relations, g as validateRelationConfig };
@@ -0,0 +1,38 @@
1
+ import { getTable as e } from "./tableRegistry.js";
2
+ import { isRelationsSpec as t, validateRelationConfig as n } from "./relations.js";
3
+ //#region src/relationsRegistry.ts
4
+ var r = Symbol.for("@voltro/database/relationsRegistry"), i = globalThis, a = i[r] ?? (i[r] = /* @__PURE__ */ new Map()), o = (e) => {
5
+ let t = e.source(), n = a.get(t.tableName);
6
+ if (n === void 0) a.set(t.tableName, new Map(Object.entries(e.relations)));
7
+ else for (let [r, i] of Object.entries(e.relations)) {
8
+ let e = n.get(r);
9
+ if (e !== i) {
10
+ if (e !== void 0) throw Error(`duplicate relation '${r}' on '${t.tableName}': two relations() blocks both declare it. Pick distinct names or merge them into one block.`);
11
+ n.set(r, i);
12
+ }
13
+ }
14
+ }, s = (e) => {
15
+ let n = 0;
16
+ for (let r of Object.values(e)) t(r) && (o(r), n += 1);
17
+ return n;
18
+ }, c = (e) => {
19
+ let t = a.get(e);
20
+ if (t !== void 0) return Object.fromEntries(t);
21
+ }, l = (e, t) => a.get(e)?.get(t), u = () => {
22
+ let e = [];
23
+ for (let [t, n] of a) for (let [r, i] of n) e.push({
24
+ source: t,
25
+ name: r,
26
+ relation: i
27
+ });
28
+ return e;
29
+ }, d = () => {
30
+ for (let [t, r] of a) {
31
+ let i = e(t);
32
+ if (i !== void 0) for (let [e, t] of r) n(i, e, t);
33
+ }
34
+ }, f = () => {
35
+ a.clear();
36
+ };
37
+ //#endregion
38
+ export { u as allRegisteredRelations, f as clearRelationsRegistry, l as getRelation, c as getRelations, s as registerDiscoveredRelations, o as registerRelations, d as validateRegisteredRelations };
@@ -0,0 +1,53 @@
1
+ import { boolean as e, id as t, text as n, timestamp as r } from "./columns.js";
2
+ import { table as i } from "./table.js";
3
+ //#region src/replaceInProgress.ts
4
+ var a = "_voltro_replace_in_progress", o = i(a, {
5
+ id: t({ prefix: "repl" }),
6
+ startedAt: r().default("now"),
7
+ kind: n().default("replace"),
8
+ tables: n().nullable(),
9
+ capture: n().nullable(),
10
+ via: n(),
11
+ staging: n().nullable(),
12
+ heartbeatAt: r().nullable(),
13
+ resumable: e().nullable()
14
+ }).nonReactive(), s = 30, c = (e, t) => {
15
+ let n = typeof t == "number" && Number.isFinite(t) && t > 0 ? t : 30, r = Number(e.VOLTRO_STAGING_STALE_MINUTES);
16
+ return (Number.isFinite(r) && r > 0 ? r : n) * 6e4;
17
+ }, l = (e) => e == null || String(e).length === 0 ? [] : String(e).split(",").map((e) => e.trim()).filter((e) => e.length > 0), u = (e) => {
18
+ let t = e.filter((e) => e.kind !== "staging");
19
+ if (t.length !== 0) return `refusing to start: a destructive data operation did not finish.
20
+ ${t.map((e) => {
21
+ let t = Number.isFinite(e.startedAt.getTime()) ? `${Math.max(0, Math.round((Date.now() - e.startedAt.getTime()) / 6e4))} minute(s) ago` : "at an unknown time";
22
+ if (e.kind === "restore") return ` - a native \`restore\` over ${e.via} began loading an artefact over this database ${t}\n and never reported finishing.\n` + (e.capture === null ? " The artefact it was loading was not recorded. Re-run the restore from your backup\n directory, or restore from another backup." : ` The artefact it was loading:\n ${e.capture}\n Re-run it to completion with: voltro data restore ${e.capture} --allow-live`);
23
+ let n = e.tables === null ? "this database" : `${e.tables} table(s)`;
24
+ return ` - a \`replace\` over ${e.via} began emptying ${n} ${t} and never reported finishing.\n` + (e.capture === null ? " NO capture was taken for that run, so the previous rows are not recoverable from here.\n Restore from your own backup." : ` The target's previous rows were captured first:\n ${e.capture}\n Restore them with: voltro data import ${e.capture} --mode replace`);
25
+ }).join("\n")}\n
26
+ A half-replaced database is indistinguishable from an empty one from the inside — every table
27
+ exists, every constraint holds, and every query returns nothing without erroring. That is why
28
+ this is a refusal and not a warning.
29
+
30
+ When the data is back, or you have decided the current state is correct, clear the marker:
31
+ voltro data clear-replace-marker --yes`;
32
+ }, d = (e, t, n) => {
33
+ let r = e.filter((e) => e.kind === "staging");
34
+ return {
35
+ all: r,
36
+ droppable: r.filter((e) => {
37
+ if (e.resumable) return !1;
38
+ let r = (e.heartbeatAt ?? e.startedAt).getTime();
39
+ return Number.isFinite(r) ? t - r >= n : !1;
40
+ })
41
+ };
42
+ }, f = (e, t) => {
43
+ if (e.all.length === 0) return;
44
+ let n = new Set(e.droppable.map((e) => e.id));
45
+ return `staged data-import leftovers:
46
+ ${e.all.map((e) => {
47
+ let r = e.heartbeatAt ?? e.startedAt, i = Number.isFinite(r.getTime()) ? `${Math.max(0, Math.round((t - r.getTime()) / 6e4))} minute(s) ago` : "at an unknown time", a = `${e.staging.length} staging table(s) from a \`replace\` over ${e.via}, last active ${i}`;
48
+ return n.has(e.id) ? ` - ${a} — DROPPED: the run is not resumable and has been silent long enough that nothing is loading into them.` : e.resumable ? ` - ${a} — KEPT: the run was started \`--no-atomic\`, so these ARE its resume point. Re-run the same\n bundle to continue from them, or drop them with: voltro data clear-staging --yes` : ` - ${a} — KEPT: still beating, so an import is loading into them right now.`;
49
+ }).join("\n")}\n The target of a staged \`replace\` is untouched until one short swap at the end, so none of this is
50
+ a reason to refuse the boot — it is a reason to know the disk is holding a copy of a bundle.`;
51
+ };
52
+ //#endregion
53
+ export { a as REPLACE_IN_PROGRESS_TABLE, s as STAGING_STALE_MINUTES, o as _voltroReplaceInProgressTable, u as interruptedReplaceRefusal, f as stagingLeftoverReport, d as stagingLeftovers, l as stagingNamesOf, c as stagingStaleAfterMs };
@@ -0,0 +1,26 @@
1
+ //#region src/requiredColumns.ts
2
+ var e = (e, t) => {
3
+ let n = [], r = t;
4
+ for (let [t, i] of Object.entries(e.fields)) {
5
+ let e = i;
6
+ e.nullable || e.hasDefault || e.idScheme !== void 0 || e.generatedAs !== void 0 || (r[t] === void 0 || r[t] === null) && n.push(t);
7
+ }
8
+ return n;
9
+ }, t = (e, t, n) => {
10
+ let r = n, i = /* @__PURE__ */ new Set();
11
+ if (e !== void 0) {
12
+ let n = e.fields;
13
+ for (let e of new Set(t)) Object.getOwnPropertyDescriptor(n, e)?.enumerable && n[e]?.generatedAs !== void 0 && i.add(e);
14
+ }
15
+ let a = [];
16
+ for (let e of t) i.has(e) || (r[e] === void 0 ? a.push({
17
+ column: e,
18
+ reason: "absent"
19
+ }) : r[e] === null && a.push({
20
+ column: e,
21
+ reason: "null"
22
+ }));
23
+ return a;
24
+ };
25
+ //#endregion
26
+ export { t as missingConflictColumns, e as missingRequiredColumns };
@@ -0,0 +1,52 @@
1
+ import { resolveTenantNamespace as e } from "./namespace.js";
2
+ //#region src/residency.ts
3
+ var t = class extends Error {
4
+ _tag = "TenantResidencyUnresolved";
5
+ constructor(e) {
6
+ super(e), this.name = "TenantResidencyUnresolved";
7
+ }
8
+ }, n = class extends Error {
9
+ region;
10
+ _tag = "TenantRegionUnavailable";
11
+ constructor(e, t) {
12
+ super(t), this.region = e, this.name = "TenantRegionUnavailable";
13
+ }
14
+ }, r, i = (e) => {
15
+ if (e.homes === void 0) throw Error("setResidencyConfig: `homes` is required");
16
+ if (e.servableRegions === void 0 || e.servableRegions.length === 0) throw Error("setResidencyConfig: `servableRegions` must be non-empty (which regions this deployment serves)");
17
+ let t = /* @__PURE__ */ new Map();
18
+ for (let n of e.homes) {
19
+ let e = t.get(n.tenantId);
20
+ if (e !== void 0 && e !== n.region) throw Error(`residency: tenant '${n.tenantId}' is mapped to two regions ('${e}' and '${n.region}')`);
21
+ t.set(n.tenantId, n.region);
22
+ }
23
+ return r = e, e;
24
+ }, a = () => r, o = () => {
25
+ r = void 0;
26
+ }, s = (e, n) => {
27
+ if (e == null || e === "") throw new t("residency is on but the request has no resolvable tenant — refusing to bind a store");
28
+ let r = n.homes.find((t) => t.tenantId === e);
29
+ if (r === void 0) throw new t(`no residency home is mapped for tenant '${e}' — refusing to fall back to a default store (would be a residency violation)`);
30
+ return r;
31
+ }, c = (e, t) => t.servableRegions.includes(e), l = (t, n) => {
32
+ let r = s(t.tenantId, n), i = e(r.tenantId);
33
+ return {
34
+ region: r.region,
35
+ namespace: i,
36
+ ...r.connectionKey === void 0 ? {} : { connectionKey: r.connectionKey }
37
+ };
38
+ }, u = (e, t, r) => {
39
+ let i = l(e, t);
40
+ if (!c(i.region, t)) throw new n(i.region, `tenant homed in region '${i.region}', which this deployment does not serve — route to the home region`);
41
+ let a = r.get(i.region);
42
+ if (a === void 0) throw new n(i.region, `no store handle for home region '${i.region}' (declared servable but not wired) — failing closed`);
43
+ return {
44
+ store: a,
45
+ placement: i
46
+ };
47
+ }, d = async (e, t, n, r) => {
48
+ let { store: i, placement: a } = u(e, t, n);
49
+ return await r(i, a), a;
50
+ };
51
+ //#endregion
52
+ export { n as TenantRegionUnavailable, t as TenantResidencyUnresolved, u as bindResidentStore, o as clearResidencyConfig, a as getResidencyConfig, c as isRegionServable, d as provisionResidentTenant, l as residentPlacement, s as resolveTenantHome, i as setResidencyConfig };
@@ -0,0 +1,62 @@
1
+ import { and as e, isNull as t, lt as n, or as r } from "./predicate.js";
2
+ import { Effect as i, Metric as a } from "effect";
3
+ //#region src/retention.ts
4
+ var o = {
5
+ app: 3,
6
+ plugin: 2,
7
+ framework: 1
8
+ }, s = /* @__PURE__ */ new Map(), c = [], l = () => [...c], u = (e) => {
9
+ if (!e.table || !e.timeColumn) throw Error("registerRetention: table + timeColumn are required");
10
+ if (!Number.isFinite(e.ttlMs) || e.ttlMs <= 0) throw Error(`registerRetention(${e.table}): ttlMs must be a positive number`);
11
+ let t = e.source ?? "app", n = s.get(e.table);
12
+ if (n !== void 0) {
13
+ let r = n.source ?? "app", i = n.ttlMs !== e.ttlMs || n.timeColumn !== e.timeColumn || JSON.stringify(n.activityColumns ?? []) !== JSON.stringify(e.activityColumns ?? []) || JSON.stringify(n.where) !== JSON.stringify(e.where);
14
+ if (o[t] < o[r]) {
15
+ i && c.push({
16
+ table: e.table,
17
+ kept: {
18
+ source: r,
19
+ ttlMs: n.ttlMs
20
+ },
21
+ dropped: {
22
+ source: t,
23
+ ttlMs: e.ttlMs
24
+ }
25
+ });
26
+ return;
27
+ }
28
+ i && r !== t && c.push({
29
+ table: e.table,
30
+ kept: {
31
+ source: t,
32
+ ttlMs: e.ttlMs
33
+ },
34
+ dropped: {
35
+ source: r,
36
+ ttlMs: n.ttlMs
37
+ }
38
+ });
39
+ }
40
+ s.set(e.table, {
41
+ ...e,
42
+ source: t
43
+ });
44
+ }, d = () => [...s.values()], f = () => {
45
+ s.clear(), c.length = 0;
46
+ }, p = async (o, s, c, l = 2e4) => {
47
+ let u = new Date(s - o.ttlMs), d = (o.activityColumns ?? []).map((e) => r(t(e), n(e, u))), f = d.length === 0 ? o.where : e(...o.where === void 0 ? [] : [o.where], ...d), p = 0;
48
+ try {
49
+ for (;;) {
50
+ let e = await c(o.table, o.timeColumn, u, l, f);
51
+ if (p += e, e < l) break;
52
+ }
53
+ } catch (e) {
54
+ throw i.runSync(a.update(a.withAttributes(h, { table: o.table }), 1)), e;
55
+ }
56
+ return p > 0 && i.runSync(a.update(a.withAttributes(m, { table: o.table }), p)), p;
57
+ }, m = a.counter("voltro_retention_swept_rows_total"), h = a.counter("voltro_retention_sweep_errors_total"), g = (e, t) => {
58
+ let n = Number(e ?? String(t));
59
+ return (Number.isFinite(n) && n > 0 ? n : t) * 36e5;
60
+ };
61
+ //#endregion
62
+ export { f as clearRetentions, l as listRetentionConflicts, d as listRetentions, u as registerRetention, g as retentionTtlMsFromEnv, p as sweepRetention };
@@ -0,0 +1,123 @@
1
+ import { getTable as e } from "./tableRegistry.js";
2
+ import { decodeSqliteDecimal as t, encodeSqliteDecimal as n } from "./sqliteDecimal.js";
3
+ import { sqliteDecimalStorageShape as r } from "./sqliteDecimalStorage.js";
4
+ import { projectedColumn as i } from "./projectedColumn.js";
5
+ import { hasEncodedSqliteDecimalSource as a } from "./sqliteDecimalQueryPlan.js";
6
+ import { sqliteDecimalOutputs as o } from "./sqliteDecimalOutputs.js";
7
+ import { resolveSqliteDecimalSources as s } from "./sqliteDecimalCte.js";
8
+ //#region src/rowDecoder.ts
9
+ var c = (e) => e == null || typeof e == "boolean" ? e : typeof e == "number" ? e !== 0 : typeof e == "bigint" ? e !== 0n : typeof e == "string" ? e === "1" || e.toLowerCase() === "true" : e, l = (e) => {
10
+ if (e == null || typeof e != "string") return e;
11
+ let t = e.trim();
12
+ if (t.length === 0) return e;
13
+ let n = t.charAt(0);
14
+ if (n !== "{" && n !== "[" && n !== "\"" && n !== "t" && n !== "f" && n !== "n" && (n < "0" || n > "9") && n !== "-") return e;
15
+ try {
16
+ return JSON.parse(t);
17
+ } catch {
18
+ return e;
19
+ }
20
+ }, u = (e) => e == null || typeof e == "string" ? e : typeof e == "number" || typeof e == "bigint" ? String(e) : e, d = (e) => {
21
+ if (e == null) return e;
22
+ let t = typeof e == "number" ? e : Number(e);
23
+ if (!Number.isSafeInteger(t)) throw RangeError("Numeric id must be a safe integer; use a string id scheme for larger identities");
24
+ return t;
25
+ }, f = (e) => e.type === "id" && e.idScheme?.kind === "numeric" || e.type === "reference" && e.referenceStorage === "numeric", p = (e) => {
26
+ if (e == null || e instanceof Date) return e;
27
+ if (typeof e == "string" || typeof e == "number") {
28
+ let t = new Date(e);
29
+ return isNaN(t.getTime()) ? e : t;
30
+ }
31
+ return e;
32
+ }, m = (e, t, n) => {
33
+ if (e == null) return e;
34
+ switch (t) {
35
+ case "boolean": return n === "postgres" ? e : c(e);
36
+ case "vector": return n === "postgres" ? l(e) : e;
37
+ case "json": return n === "postgres" || n === "mysql" || n === "mariadb" ? e : l(e);
38
+ case "array": return n === "postgres" ? e : l(e);
39
+ case "timestamp":
40
+ case "date": return n === "sqlite" || n === "turso" ? p(e) : e;
41
+ case "decimal":
42
+ if ((n === "sqlite" || n === "turso") && typeof e == "string" && /^D\d+$/.test(e)) throw TypeError("Decimal storage representation does not match the application schema; stop access and complete the verified storage migration before reading this column");
43
+ return u(e);
44
+ case "bigint": return u(e);
45
+ default: return e;
46
+ }
47
+ }, h = (e, t) => {
48
+ switch (e) {
49
+ case "boolean": return t !== "postgres";
50
+ case "vector": return t === "postgres";
51
+ case "json": return t !== "postgres" && t !== "mysql" && t !== "mariadb";
52
+ case "array": return t !== "postgres";
53
+ case "timestamp":
54
+ case "date": return t === "sqlite" || t === "turso";
55
+ case "decimal":
56
+ case "bigint": return !0;
57
+ default: return !1;
58
+ }
59
+ }, g = (t, n, r, c) => {
60
+ if (t.length === 0) return t;
61
+ if (c && (r === "sqlite" || r === "turso") && a(c)) {
62
+ let e = o(s(c, void 0), void 0);
63
+ return t.map((t) => {
64
+ let n = { ...t };
65
+ for (let t of e) {
66
+ let e = t.decimal ? "decimal" : t.definition?.type;
67
+ e && t.name in n && (n[t.name] = m(n[t.name], e, r));
68
+ }
69
+ return n;
70
+ });
71
+ }
72
+ if (c?.joinedProjection?.length) {
73
+ let e = c.joinedProjection.map((e) => ({
74
+ outputKey: e.outputKey,
75
+ definition: i(e.source, c)
76
+ }));
77
+ return t.map((t) => {
78
+ let n = { ...t };
79
+ for (let { outputKey: t, definition: i } of e) i && t in n && (n[t] = f(i) ? d(n[t]) : m(n[t], i.type, r));
80
+ return n;
81
+ });
82
+ }
83
+ let l = c?.sourceTable?.tableName === n ? c.sourceTable : e(n);
84
+ return l === void 0 ? t : _(t, l, r);
85
+ }, _ = (e, n, i) => {
86
+ if (e.length === 0) return e;
87
+ let a = n.fields, o = [];
88
+ for (let [e, t] of Object.entries(a)) {
89
+ let n = i === "sqlite" || i === "turso" ? r(t) : void 0, a = t.type, s = f(t);
90
+ (s || n || h(a, i) || e in Object.prototype) && o.push({
91
+ key: e,
92
+ type: a,
93
+ ...s ? { numericId: !0 } : {},
94
+ ...n ? { decimalShape: n } : {}
95
+ });
96
+ }
97
+ return e.map((e) => {
98
+ let n = { ...e };
99
+ for (let { key: e, type: r, decimalShape: a, numericId: s } of o) e in n && (n[e] = s ? d(n[e]) : a && n[e] !== null && n[e] !== void 0 ? t(n[e], a) : m(n[e], r, i));
100
+ return n;
101
+ });
102
+ }, v = (e, t) => {
103
+ if (e == null) return e;
104
+ switch (t) {
105
+ case "json": return JSON.stringify(e);
106
+ case "array":
107
+ case "vector": return typeof e == "string" ? e : JSON.stringify(e);
108
+ default: return e;
109
+ }
110
+ }, y = (t, i, a = ["json", "array"]) => {
111
+ let o = e(i);
112
+ if (o === void 0) return t;
113
+ let s = o.fields, c = null;
114
+ for (let [e, i] of Object.entries(s)) if (a.includes(i.type) && e in t) {
115
+ let a = i.type === "decimal" ? r(i) : void 0, o = t[e];
116
+ if (a && o != null && typeof o != "string") throw TypeError("Encoded decimal writes require exact decimal strings");
117
+ let s = a && typeof o == "string" ? n(o, a) : v(o, i.type);
118
+ s !== t[e] && (c === null && (c = { ...t }), c[e] = s);
119
+ }
120
+ return c ?? t;
121
+ };
122
+ //#endregion
123
+ export { m as decodeCell, _ as decodeRowsForTable, g as decodeRowsFromSchema, y as encodeRowForSchema };
package/dist/rowKey.js ADDED
@@ -0,0 +1,8 @@
1
+ //#region src/rowKey.ts
2
+ var e = (e) => typeof e == "string" || typeof e == "number" ? [["id", e]] : Object.entries(e).map(([e, t]) => [e, t]), t = (e) => {
3
+ if (typeof e == "string" || typeof e == "number") return String(e);
4
+ let t = Object.entries(e);
5
+ return t.length === 1 ? String(t[0][1]) : t.slice().sort(([e], [t]) => e < t ? -1 : +(e > t)).map(([e, t]) => `${e}=${String(t)}`).join("\0");
6
+ }, n = (e) => typeof e == "object";
7
+ //#endregion
8
+ export { n as isCompositeRowKey, e as rowKeyColumns, t as rowKeyIdentity };
@@ -0,0 +1,31 @@
1
+ import { timestampMs as e } from "./wire.js";
2
+ import { Schema as t } from "effect";
3
+ //#region src/rowSchema.ts
4
+ var n = (n) => {
5
+ let r = (() => {
6
+ switch (n.type) {
7
+ case "id": return n.idScheme?.kind === "numeric" ? t.Number : t.String;
8
+ case "text":
9
+ case "enum": return t.String;
10
+ case "reference": return n.referenceStorage === "numeric" ? t.Number : t.String;
11
+ case "integer":
12
+ case "real":
13
+ case "decimal": return t.Number;
14
+ case "boolean": return t.Boolean;
15
+ case "timestamp":
16
+ case "date": return e;
17
+ case "bigint": return t.BigIntFromString;
18
+ case "bytes": return t.Uint8ArrayFromBase64;
19
+ case "array": return t.Array(t.Unknown);
20
+ case "interval": return t.String;
21
+ default: return t.Unknown;
22
+ }
23
+ })();
24
+ return n.nullable === !0 ? t.NullOr(r) : r;
25
+ }, r = (e, r) => {
26
+ let i = new Set(r?.omit ?? []), a = {};
27
+ for (let [t, r] of Object.entries(e.fields)) i.has(t) || (a[t] = n(r));
28
+ return t.Struct(a);
29
+ };
30
+ //#endregion
31
+ export { n as columnSchema, r as rowSchema };
@@ -0,0 +1,8 @@
1
+ //#region src/scopeKey.ts
2
+ var e = 255, t = (e) => e == null ? "null" : typeof e == "string" || typeof e == "number" || typeof e == "boolean" ? JSON.stringify(e) : Array.isArray(e) ? `[${e.map(t).join(",")}]` : typeof e == "object" ? `{${Object.entries(e).filter(([, e]) => e !== void 0).sort(([e], [t]) => e < t ? -1 : +(e > t)).map(([e, n]) => `${JSON.stringify(e)}:${t(n)}`).join(",")}}` : JSON.stringify(String(e)), n = (e) => {
3
+ if (e == null) return null;
4
+ let n = typeof e == "string" ? e : t(e);
5
+ return n.length > 255 ? n.slice(0, 255) : n;
6
+ };
7
+ //#endregion
8
+ export { e as SCOPE_KEY_MAX_LENGTH, n as scopeKeyOf };
package/dist/seed.js ADDED
@@ -0,0 +1,33 @@
1
+ //#region src/seed.ts
2
+ var e = (e, t) => ({
3
+ name: e,
4
+ body: t
5
+ }), t = (t) => {
6
+ if (t.lifecycle === "cron" && !t.cron) throw Error(`seed "${t.id}": lifecycle 'cron' requires a 'cron' field`);
7
+ if (t.lifecycle === "onSchemaChange" && (!t.watchedTables || t.watchedTables.length === 0)) throw Error(`seed "${t.id}": lifecycle 'onSchemaChange' requires at least one entry in 'watchedTables'`);
8
+ let n = t.steps({ step: e });
9
+ if (n.length === 0) throw Error(`seed "${t.id}": at least one step is required`);
10
+ return {
11
+ __seed: !0,
12
+ id: t.id,
13
+ name: t.name,
14
+ lifecycle: t.lifecycle,
15
+ ...t.cron === void 0 ? {} : { cron: t.cron },
16
+ ...t.timezone === void 0 ? {} : { timezone: t.timezone },
17
+ ...t.watchedTables === void 0 ? {} : { watchedTables: t.watchedTables },
18
+ ...t.fingerprint === void 0 ? {} : { fingerprint: t.fingerprint },
19
+ steps: n
20
+ };
21
+ }, n = (e) => typeof e == "object" && !!e && "__seed" in e && e.__seed === !0, r = null, i = null, a = (e) => {
22
+ r = e;
23
+ }, o = (e) => {
24
+ i = e;
25
+ }, s = async (e) => {
26
+ let t = r;
27
+ t !== null && await t(e);
28
+ }, c = async (e) => {
29
+ let t = i;
30
+ t !== null && e.changedTables.length !== 0 && await t(e);
31
+ };
32
+ //#endregion
33
+ export { t as defineSeed, c as fireSchemaChangeSeeds, s as fireTenantCreateSeeds, n as isSeedDefinition, o as setSchemaChangeSeedHook, a as setTenantCreateSeedHook };
@@ -0,0 +1,26 @@
1
+ //#region src/snowflake.ts
2
+ var e = 10n, t = 12n, n = (1n << e) - 1n, r = (1n << t) - 1n, i = t, a = 22n, o = () => {
3
+ let e = process.env.SNOWFLAKE_MACHINE_ID;
4
+ if (e === void 0 || e === "") return 0n;
5
+ let t = BigInt(e);
6
+ if (t < 0n || t > n) throw Error(`SNOWFLAKE_MACHINE_ID out of range: ${e}. Must be 0..${n}.`);
7
+ return t;
8
+ }, s = () => ({
9
+ lastMs: 0n,
10
+ sequence: 0n,
11
+ machineId: o()
12
+ }), c, l = () => (c === void 0 && (c = s()), c), u = () => {
13
+ c = s();
14
+ }, d = () => BigInt(Date.now()), f = (e) => {
15
+ let t = d();
16
+ for (; t <= e;) t = d();
17
+ return t;
18
+ }, p = () => {
19
+ let e = l(), t = d();
20
+ t === e.lastMs ? (e.sequence = e.sequence + 1n & r, e.sequence === 0n && (t = f(e.lastMs), e.lastMs = t)) : (e.sequence = 0n, e.lastMs = t);
21
+ let n = t - 1704067200000n;
22
+ if (n < 0n) throw Error("Snowflake timestamp before configured epoch (2024-01-01). System clock is wrong.");
23
+ return (n << a | e.machineId << i | e.sequence).toString();
24
+ };
25
+ //#endregion
26
+ export { p as generateSnowflake, u as resetSnowflake };
package/dist/sql.d.ts CHANGED
@@ -359,7 +359,12 @@ export declare interface ApplyPlanCtx {
359
359
  * migration was a framework bug. `voltro db apply` did exactly that for two
360
360
  * releases while `voltro dev`'s boot diff, which uses this parameter, did not.
361
361
  */
362
- readonly replan: (sql: SqlClient.SqlClient) => Effect.Effect<MigrationPlan, SqlError, SqlClient.SqlClient>;
362
+ /** The applier supplies `execution` for every pre-execution replan (including
363
+ * interrupted-run reconciliation), and `convergence` only after planned work.
364
+ * Review-bound callers must validate full content in the execution phase before
365
+ * projecting their transactional/online subsets. Ordinary auto-diff callers
366
+ * can ignore the phase and adapt to the current schema as before. */
367
+ readonly replan: (sql: SqlClient.SqlClient, phase?: 'execution' | 'convergence') => Effect.Effect<MigrationPlan, SqlError, SqlClient.SqlClient>;
363
368
  /**
364
369
  * The declared store charset — the one `replan` plans with. Here only for
365
370
  * the resume ledger this creates before the first statement: nothing declares
@@ -3581,11 +3586,17 @@ declare type IndexField = string | {
3581
3586
  declare type IndexKind = 'btree' | 'gist' | 'gin' | 'brin' | 'hnsw';
3582
3587
 
3583
3588
  /**
3584
- * Per-kind tuning options. Today only `hnsw` carries knobs (the pgvector
3585
- * build-time params). `m` controls graph connectivity (default 16),
3589
+ * Per-kind tuning options. GIN supports a single-column operator class;
3590
+ * HNSW supports pgvector build-time parameters. `m` controls graph connectivity (default 16),
3586
3591
  * `efConstruction` the build-time candidate-list size (default 64).
3587
3592
  */
3588
3593
  declare interface IndexKindOptions {
3594
+ /** PostgreSQL GIN operator class for a single plain column.
3595
+ * `gin_trgm_ops` installs pg_trgm and accelerates contains/ILIKE searches.
3596
+ */
3597
+ readonly gin?: {
3598
+ readonly opclass: string;
3599
+ };
3589
3600
  readonly hnsw?: {
3590
3601
  readonly m?: number;
3591
3602
  readonly efConstruction?: number;
@@ -4780,6 +4791,11 @@ export declare const publishSqliteDecimalStage: (sql: SqlClient.SqlClient, reque
4780
4791
  declare interface QueryDescriptor<R = Row> {
4781
4792
  readonly table: string;
4782
4793
  readonly predicate: Predicate | undefined;
4794
+ /** Additional reactive invalidation sources, independent of SQL row selection.
4795
+ * The runtime merges a query handler's declared `source` tables here. Changes
4796
+ * to these tables recheck subscription authority even when SQL rows do not
4797
+ * change. They add no joins, predicates or cross-tenant access. */
4798
+ readonly dependentTables?: ReadonlyArray<string>;
4783
4799
  /** Phantom — carries the row type through to `store.query()`. Never set at
4784
4800
  * runtime; erased entirely by the compiler. */
4785
4801
  readonly __row?: R;
@@ -5070,6 +5086,11 @@ declare interface QueryDescriptor<R = Row> {
5070
5086
  declare interface QueryDescriptor_2<R = Row_2> {
5071
5087
  readonly table: string;
5072
5088
  readonly predicate: Predicate_2 | undefined;
5089
+ /** Additional reactive invalidation sources, independent of SQL row selection.
5090
+ * The runtime merges a query handler's declared `source` tables here. Changes
5091
+ * to these tables recheck subscription authority even when SQL rows do not
5092
+ * change. They add no joins, predicates or cross-tenant access. */
5093
+ readonly dependentTables?: ReadonlyArray<string>;
5073
5094
  /** Phantom — carries the row type through to `store.query()`. Never set at
5074
5095
  * runtime; erased entirely by the compiler. */
5075
5096
  readonly __row?: R;
@@ -5688,7 +5709,7 @@ declare type Row = Readonly<Record<string, unknown>>;
5688
5709
  declare type Row_2 = Readonly<Record<string, unknown>>;
5689
5710
 
5690
5711
  /** Addresses one row: its `id`, or the columns of an explicit primary key. */
5691
- declare type RowKey = string | Readonly<Record<string, unknown>>;
5712
+ declare type RowKey = string | number | Readonly<Record<string, unknown>>;
5692
5713
 
5693
5714
  /** The context a rule predicate is evaluated with. */
5694
5715
  declare interface RuleContext {
@@ -6868,8 +6889,8 @@ declare interface TableIndex {
6868
6889
  */
6869
6890
  readonly kind?: IndexKind;
6870
6891
  /**
6871
- * Per-kind tuning knobs. Today only `hnsw` (`kindOptions.hnsw.{m,
6872
- * efConstruction, opclass}`). Ignored for kinds that don't read it.
6892
+ * Per-kind tuning: `gin.opclass` or `hnsw.{m, efConstruction, opclass}`.
6893
+ * Ignored for kinds that do not read the corresponding options.
6873
6894
  */
6874
6895
  readonly kindOptions?: IndexKindOptions;
6875
6896
  /**