@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,65 @@
1
+ import { Context as e, Metric as t } from "effect";
2
+ //#region src/dbMetrics.ts
3
+ var n = t.exponentialBoundaries({
4
+ start: 5e-4,
5
+ factor: 2,
6
+ count: 16
7
+ }), r = t.counter("voltro_db_queries_total", { description: "Database operations executed, by dialect and operation kind." }), i = t.histogram("voltro_db_query_duration_seconds", {
8
+ boundaries: n,
9
+ description: "Database operation duration in seconds, by dialect and operation kind."
10
+ }), a = t.counter("voltro_db_errors_total", { description: "Database operations that threw, by dialect and operation kind." }), o = t.gauge("voltro_db_operations_in_flight", { description: "Database operations currently executing, by dialect. An upper bound on connections the framework holds; NOT the driver pool's waiting count." }), s = t.counter("voltro_db_eager_fallback_total", { description: "Eager-load queries that degraded from the single-roundtrip JSON-agg path to the multi-query walker, by dialect and reason." }), c = e.empty(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = (e, n) => {
11
+ let o = `${e}\u0000${n}`, s = l.get(o);
12
+ if (s !== void 0) return s;
13
+ let c = (r) => t.withAttributes(t.withAttributes(r, { dialect: e }), { op: n }), u = {
14
+ count: c(r),
15
+ duration: c(i),
16
+ errors: c(a)
17
+ };
18
+ return l.set(o, u), u;
19
+ }, p = () => {
20
+ l.clear(), u.clear(), d.clear();
21
+ }, m = () => l.size + u.size + d.size, h = /* @__PURE__ */ new Map(), g = (e, n) => {
22
+ let r = Math.max(0, (h.get(e) ?? 0) + n);
23
+ r === 0 ? h.delete(e) : h.set(e, r);
24
+ let i = u.get(e);
25
+ i === void 0 && (i = t.withAttributes(o, { dialect: e }), u.set(e, i)), i.updateUnsafe(r, c);
26
+ }, _ = (e) => h.get(e) ?? 0, v = (e) => {
27
+ let t = f(String(e.dialect), e.op);
28
+ t.count.updateUnsafe(1, c), t.duration.updateUnsafe(e.durationMs / 1e3, c), e.status === "error" && t.errors.updateUnsafe(1, c);
29
+ }, y = async (e, t, n) => {
30
+ let r = performance.now(), i = String(e);
31
+ g(i, 1);
32
+ let a = "ok";
33
+ try {
34
+ return await n();
35
+ } catch (e) {
36
+ throw a = "error", e;
37
+ } finally {
38
+ g(i, -1), v({
39
+ dialect: e,
40
+ op: t,
41
+ durationMs: performance.now() - r,
42
+ status: a
43
+ });
44
+ }
45
+ }, b = 3e5, x = (e) => {
46
+ if (e == null || e === "") return;
47
+ let t = Number(e);
48
+ return Number.isFinite(t) && t >= 0 ? Math.floor(t) : void 0;
49
+ }, S = (e, t = process.env) => x(t.VOLTRO_DB_EAGER_FALLBACK_WARN_INTERVAL_MS) ?? x(e) ?? 3e5, C = (e, n = {}) => {
50
+ let r = S(n.warnIntervalMs), i = n.now ?? (() => Date.now()), a = /* @__PURE__ */ new Map();
51
+ return (n) => {
52
+ let o = `${n.dialect} ${n.reason}`, l = d.get(o);
53
+ l === void 0 && (l = t.withAttributes(t.withAttributes(s, { dialect: String(n.dialect) }), { reason: n.reason }), d.set(o, l)), l.updateUnsafe(1, c);
54
+ let u = `${n.table}\u0000${n.reason}`, f = a.get(u), p = f === void 0 || r > 0 && i() - f >= r, m = {
55
+ table: n.table,
56
+ dialect: String(n.dialect),
57
+ reason: n.reason,
58
+ metric: "voltro_db_eager_fallback_total",
59
+ ...n.error === void 0 ? {} : { err: n.error }
60
+ };
61
+ p ? (a.set(u, i()), e.warn(n.reason === "execute-failed" ? "eager-load JSON-agg statement FAILED and was re-run on the multi-query walker — this query pays both paths on every call. Counted as voltro_db_eager_fallback_total{reason=\"execute-failed\"}." : "eager-load could not be compiled to a single JSON-agg roundtrip and is running on the multi-query walker — one round trip per relation level, on every call. Counted as voltro_db_eager_fallback_total{reason=\"not-compilable\"}.", m)) : e.debug("eager-load is still running on the walker (rate-limited; the counter is not)", m);
62
+ };
63
+ };
64
+ //#endregion
65
+ export { b as DEFAULT_EAGER_FALLBACK_WARN_INTERVAL_MS, m as dbMetricTagCacheSizeForTests, _ as dbOperationsInFlight, C as makeEagerFallbackReporter, y as observeDbOp, v as recordDbOp, p as resetDbMetricTagCacheForTests, S as resolveEagerFallbackWarnIntervalMs };
@@ -0,0 +1,27 @@
1
+ //#region src/decimalComparison.ts
2
+ var e = (e) => {
3
+ if (typeof e != "string" || !/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e)) return null;
4
+ let [t = "", n = ""] = e.replace(/^[+-]/, "").split("."), r = t.replace(/^0+/, "") || "0";
5
+ return {
6
+ negative: e.startsWith("-") && /[1-9]/.test(r + n),
7
+ whole: r,
8
+ fraction: n
9
+ };
10
+ }, t = (t, n) => {
11
+ let r = e(t), i = e(n);
12
+ if (!r || !i) return null;
13
+ if (r.negative !== i.negative) return r.negative ? -1 : 1;
14
+ let a = r.whole.length - i.whole.length;
15
+ if (a === 0 && (a = r.whole === i.whole ? 0 : r.whole > i.whole ? 1 : -1), a === 0) {
16
+ let e = Math.max(r.fraction.length, i.fraction.length), t = r.fraction.padEnd(e, "0"), n = i.fraction.padEnd(e, "0");
17
+ a = t === n ? 0 : t > n ? 1 : -1;
18
+ }
19
+ return a === 0 ? 0 : (a > 0 ? 1 : -1) * (r.negative ? -1 : 1);
20
+ }, n = (t) => {
21
+ let n = e(t);
22
+ if (n === null) return null;
23
+ let r = n.fraction.replace(/0+$/, "");
24
+ return `${n.negative ? "-" : ""}${n.whole}${r ? "." + r : ""}`;
25
+ };
26
+ //#endregion
27
+ export { t as compareDecimal, n as decimalEqualityKey };
@@ -0,0 +1,5 @@
1
+ import { createHash as e } from "node:crypto";
2
+ //#region src/derivedRowId.ts
3
+ var t = 64, n = "\0", r = 32, i = (t, ...i) => `${t}_${e("sha256").update(i.map(String).join(n)).digest("hex").slice(0, r)}`, a = (e) => e.length + 1 + r;
4
+ //#endregion
5
+ export { t as ID_COLUMN_MAX_LENGTH, i as derivedRowId, a as derivedRowIdLength };
@@ -0,0 +1,13 @@
1
+ //#region src/dialect.ts
2
+ var e = (e) => e === "sqlite" || e === "turso", t = (e) => e === "mysql" || e === "mariadb", n = (e, t) => {
3
+ switch (t) {
4
+ case "mysql":
5
+ case "mariadb": return `\`${e.replace(/`/g, "``")}\``;
6
+ case "mssql": return `[${e.replace(/\]/g, "]]")}]`;
7
+ case "postgres":
8
+ case "sqlite":
9
+ case "turso": return `"${e.replace(/"/g, "\"\"")}"`;
10
+ }
11
+ }, r = 1e4;
12
+ //#endregion
13
+ export { r as DEFAULT_ACQUIRE_TIMEOUT_MS, t as isMysqlFamily, e as isSqliteFamily, n as quoteIdent };
@@ -0,0 +1,14 @@
1
+ //#region src/enumRenames.ts
2
+ var e = /* @__PURE__ */ new Map(), t = (t, n) => {
3
+ if (n.from === n.to) throw Error(`declareEnumRename('${t}'): \`from\` and \`to\` are both '${n.from}' — a rename must change the value.`);
4
+ let r = e.get(t) ?? [];
5
+ for (let e of r) {
6
+ if (e.from === n.from && e.to === n.to) return;
7
+ if (e.from === n.from && e.to !== n.to) throw Error(`declareEnumRename('${t}'): value '${n.from}' is already declared as renamed to '${e.to}', cannot also rename it to '${n.to}'.`);
8
+ }
9
+ r.push(n), e.set(t, r);
10
+ }, n = (t) => e.get(t) ?? [], r = () => e, i = () => {
11
+ e.clear();
12
+ };
13
+ //#endregion
14
+ export { r as allEnumRenames, i as clearEnumRenames, t as declareEnumRename, n as getEnumRenames };
@@ -0,0 +1,10 @@
1
+ import { timestamp as e } from "./columns.js";
2
+ import { defineMixin as t } from "./mixins.js";
3
+ //#region src/expiresMixin.ts
4
+ var n = "voltro/expires", r = () => t({
5
+ id: n,
6
+ fields: { expiresAt: e().nullable() },
7
+ behaviors: { defaultWhere: () => "expiresAt IS NULL OR expiresAt > now()" }
8
+ });
9
+ //#endregion
10
+ export { n as VOLTRO_EXPIRES_MIXIN_ID, r as expires };
@@ -0,0 +1,98 @@
1
+ //#region src/fieldEncryption.ts
2
+ var e = "enc:v1:", t = "enc:v2:", n = (e, t, n) => {
3
+ let r = `${e}:${t}`, i = n == null || n === "" ? void 0 : String(n);
4
+ return i === void 0 ? { column: r } : {
5
+ column: r,
6
+ row: `${r}:${i}`
7
+ };
8
+ }, r = (e) => typeof e == "string" && e.startsWith("enc:v2:"), i = class extends Error {
9
+ _tag = "FieldDecryptionError";
10
+ table;
11
+ column;
12
+ reason;
13
+ constructor(e) {
14
+ super(`column "${e.table}.${e.column}" is .encrypted() but could not be decrypted (${e.reason}) — the stored value was most likely encrypted under a different key`), this.name = "FieldDecryptionError", this.table = e.table, this.column = e.column, this.reason = e.reason;
15
+ }
16
+ }, a = (e) => typeof e == "object" && !!e && e._tag === "FieldDecryptionError", o = (e) => typeof e == "string" && (e.startsWith("enc:v1:") || e.startsWith("enc:v2:")), s = (e, t, n) => e.encrypt(JSON.stringify(t), n), c = /* @__PURE__ */ new Set([
17
+ "text",
18
+ "id",
19
+ "reference",
20
+ "enum",
21
+ "uuid",
22
+ "email",
23
+ "url"
24
+ ]), l = (e, t, n, r) => {
25
+ let i = e.decrypt(t, r), a;
26
+ try {
27
+ a = JSON.parse(i);
28
+ } catch {
29
+ return i;
30
+ }
31
+ return typeof a == "string" ? a : n === void 0 || c.has(n) ? i : a;
32
+ }, u = (e, t, n, r) => {
33
+ let i = e.decrypt(t, r);
34
+ try {
35
+ return typeof JSON.parse(i) == "string" ? !1 : n === void 0 || c.has(n) ? !0 : void 0;
36
+ } catch {
37
+ return !0;
38
+ }
39
+ }, d = (e, t) => e.fields[t]?.type, f = (e) => {
40
+ let t = [];
41
+ for (let [n, r] of Object.entries(e.fields)) r.encrypted === !0 && t.push(n);
42
+ return t;
43
+ }, p = "id", m = (e, t, n, r) => {
44
+ if (!t) return e;
45
+ let i = f(t);
46
+ return i.length === 0 ? e : g(e, i, t.tableName ?? "?", n, r);
47
+ }, h = (e, t, n) => {
48
+ if (t === void 0 || e.length === 0) return e;
49
+ let r = f(t);
50
+ if (r.length === 0) return e;
51
+ let i = t.tableName ?? "?";
52
+ return e.map((e) => g(e, r, i, n));
53
+ }, g = (e, t, r, i, a) => {
54
+ let c = a ?? e[p], l = { ...e };
55
+ for (let e of t) {
56
+ let t = l[e];
57
+ if (!(t == null || o(t))) {
58
+ if (!i) throw Error(`column "${r}.${e}" is .encrypted() but no field cipher is registered — set VOLTRO_FIELD_ENCRYPTION_KEY (\`voltro dev\` mints one; \`voltro serve\` refuses to boot without it); in a test, pass \`fieldEncryptionKey\` to makeTestContext`);
59
+ l[e] = s(i, t, n(r, e, c));
60
+ }
61
+ }
62
+ return l;
63
+ }, _ = (e, t, r, a) => {
64
+ if (!t) return e;
65
+ let s = f(t);
66
+ if (s.length === 0 || !r) return e;
67
+ let c = a?.onError ?? "throw", u = t.tableName ?? "?";
68
+ return e.map((e) => {
69
+ let f;
70
+ for (let m of s) {
71
+ let s = e[m];
72
+ if (o(s)) {
73
+ f ??= { ...e };
74
+ try {
75
+ f[m] = l(r, s, d(t, m), n(u, m, a?.rowId ?? e[p]));
76
+ } catch (e) {
77
+ let t = String(e?.message ?? e);
78
+ if (c === "null") {
79
+ f[m] = null, a?.warn?.({
80
+ table: u,
81
+ column: m,
82
+ reason: t
83
+ });
84
+ continue;
85
+ }
86
+ throw new i({
87
+ table: u,
88
+ column: m,
89
+ reason: t
90
+ });
91
+ }
92
+ }
93
+ }
94
+ return f ?? e;
95
+ });
96
+ };
97
+ //#endregion
98
+ export { t as BOUND_ENCRYPTED_PREFIX, e as ENCRYPTED_PREFIX, i as FieldDecryptionError, l as decodeFieldValue, _ as decryptFieldsOnRead, s as encodeFieldValue, m as encryptFieldsForWrite, h as encryptFieldsForWriteBatch, f as encryptedColumnsOf, n as fieldBinding, r as isBoundCiphertext, o as isEncrypted, a as isFieldDecryptionError, u as isRawEncoding };
@@ -0,0 +1,130 @@
1
+ import { id as e, integer as t, json as n, text as r, timestamp as i } from "./columns.js";
2
+ import { table as a } from "./table.js";
3
+ //#region src/frameworkTables.ts
4
+ var o = a("_voltro_migrations", {
5
+ id: e({ prefix: "mig" }),
6
+ name: r(),
7
+ hash: r(),
8
+ status: r(),
9
+ appliedAt: i().nullable(),
10
+ durationMs: t().nullable(),
11
+ errorMessage: r().nullable(),
12
+ errorStack: r().nullable(),
13
+ workflowRunId: r().nullable(),
14
+ createdAt: i().default("now"),
15
+ updatedAt: i().default("now").onUpdate("now")
16
+ }).index(["status"]), s = a("_voltro_seeds", {
17
+ id: e({ prefix: "seed" }),
18
+ name: r(),
19
+ lifecycle: r(),
20
+ fingerprint: r(),
21
+ cronSchedule: r().nullable(),
22
+ watchedTables: n().nullable(),
23
+ nextRunAt: i().nullable(),
24
+ lastRunStatus: r().nullable(),
25
+ lastRunAt: i().nullable(),
26
+ lastRunDurationMs: t().nullable(),
27
+ lastRunRowsTouched: t().nullable(),
28
+ lastError: r().nullable(),
29
+ workflowRunId: r().nullable(),
30
+ createdAt: i().default("now"),
31
+ updatedAt: i().default("now").onUpdate("now")
32
+ }).index(["lifecycle"]).index(["nextRunAt"]), c = a("_voltro_schema_owner", {
33
+ id: e({ prefix: "own" }),
34
+ appName: r(),
35
+ projectName: r().nullable(),
36
+ frameworkVersion: r().nullable(),
37
+ claimedAt: i().default("now")
38
+ }), l = a("_voltro_plugin_migrations", {
39
+ id: e({ prefix: "plm" }),
40
+ plugin_name: r(),
41
+ migration_id: r(),
42
+ description: r().nullable(),
43
+ applied_at: r()
44
+ }), u = a("_voltro_migration_plans", {
45
+ id: e({ prefix: "plan" }),
46
+ fingerprint: r(),
47
+ liveFingerprint: r().nullable(),
48
+ operations: n(),
49
+ rollbackPlan: n().nullable(),
50
+ appliedBy: r(),
51
+ environment: r(),
52
+ source: r(),
53
+ onlineStrategy: r().nullable(),
54
+ durationMs: t(),
55
+ notes: r().nullable(),
56
+ squashedAt: i().nullable(),
57
+ appliedAt: i().default("now"),
58
+ createdAt: i().default("now"),
59
+ updatedAt: i().default("now").onUpdate("now")
60
+ }).index(["fingerprint"]).index(["environment"]), d = a("_voltro_idempotency", {
61
+ id: e({ prefix: "idem" }),
62
+ scope: r(),
63
+ key: r(),
64
+ status: r(),
65
+ response: n().nullable(),
66
+ createdAt: i().default("now")
67
+ }).unique(["scope", "key"]).index(["createdAt"]), f = a("_voltro_devices", {
68
+ id: e({ prefix: "device" }),
69
+ tenantId: r(),
70
+ name: r(),
71
+ scopes: n().nullable(),
72
+ pairingHash: r().maxLength(64).nullable(),
73
+ pairingExpiresAt: i().nullable(),
74
+ accessHash: r().maxLength(64).nullable(),
75
+ accessExpiresAt: i().nullable(),
76
+ refreshHash: r().maxLength(64).nullable(),
77
+ refreshExpiresAt: i().nullable(),
78
+ previousRefreshHash: r().maxLength(64).nullable(),
79
+ generation: t().default(0),
80
+ pairedAt: i().nullable(),
81
+ lastSeenAt: i().nullable(),
82
+ revokedAt: i().nullable(),
83
+ revokedReason: r().nullable(),
84
+ createdBy: r().nullable(),
85
+ metadata: n().nullable(),
86
+ createdAt: i()
87
+ }).index(["tenantId"]).index(["pairingHash"]).index(["accessHash"]).index(["refreshHash"]).index(["previousRefreshHash"]), p = a("_voltro_tenant_selections", {
88
+ id: e({ prefix: "tsel" }),
89
+ subjectType: r().maxLength(32),
90
+ subjectId: r(),
91
+ tenantId: r(),
92
+ selectedAt: i()
93
+ }).primaryKey(["id"]).nonReactive(), m = a("_voltro_kv", {
94
+ id: e({ prefix: "kv" }),
95
+ key: r().unique(),
96
+ value: r().nullable(),
97
+ expiresAt: i().nullable(),
98
+ createdAt: i().default("now"),
99
+ updatedAt: i().default("now").onUpdate("now")
100
+ }).index(["expiresAt"]), h = a("_voltro_api_keys", {
101
+ id: e({ prefix: "apikey" }),
102
+ tenantId: r().nullable(),
103
+ name: r(),
104
+ hashedKey: r().maxLength(64).unique(),
105
+ keyPrefix: r(),
106
+ scopes: n().nullable(),
107
+ lastUsedAt: i().nullable(),
108
+ requestCount: t().default(0),
109
+ expiresAt: i().nullable(),
110
+ revokedAt: i().nullable(),
111
+ createdBy: r().nullable(),
112
+ onBehalfOf: r().nullable(),
113
+ metadata: n().nullable(),
114
+ createdAt: i().default("now")
115
+ }).index("byApiKeyTenant", ["tenantId"]), g = a("_voltro_replica_observations", {
116
+ id: e({ prefix: "robs" }),
117
+ replicaId: r().maxLength(191),
118
+ startedAt: i(),
119
+ version: r().maxLength(64).nullable(),
120
+ kind: r().maxLength(64),
121
+ payload: r().nullable(),
122
+ reachableAt: r().maxLength(255).nullable(),
123
+ writtenAt: i().default("now").onUpdate("now")
124
+ }).unique(["replicaId", "kind"]).index(["kind"]), _ = [
125
+ o,
126
+ s,
127
+ u
128
+ ];
129
+ //#endregion
130
+ export { h as _voltroApiKeysTable, f as _voltroDevicesTable, d as _voltroIdempotencyTable, m as _voltroKvTable, u as _voltroMigrationPlansTable, o as _voltroMigrationsTable, l as _voltroPluginMigrationsTable, g as _voltroReplicaObservationsTable, c as _voltroSchemaOwnerTable, s as _voltroSeedsTable, p as _voltroTenantSelectionsTable, _ as frameworkTables };
@@ -0,0 +1,10 @@
1
+ //#region src/ginIndex.ts
2
+ var e = (e) => {
3
+ let t = e?.gin?.opclass;
4
+ if (t !== void 0) {
5
+ if (!/^[a-z_][a-z0-9_$]*(?:\.[a-z_][a-z0-9_$]*)?$/i.test(t)) throw Error("GIN opclass must be an unquoted, optionally schema-qualified identifier");
6
+ return t.toLowerCase().replace(/^public\./, "");
7
+ }
8
+ };
9
+ //#endregion
10
+ export { e as resolveGinOpclass };
@@ -0,0 +1,32 @@
1
+ import { resolveFullTextIndex as e } from "./queryBuilder.js";
2
+ //#region src/hybridSearch.ts
3
+ var t = (t) => {
4
+ let n = t.alpha ?? .5;
5
+ if (n < 0 || n > 1) throw Error(`hybridSearch: alpha must be in [0, 1], got ${n}`);
6
+ return (r) => {
7
+ let i = Array.isArray(t.vector.query) ? {
8
+ column: t.vector.col,
9
+ queryVector: t.vector.query,
10
+ distance: t.vector.distance ?? "cosine",
11
+ alpha: n
12
+ } : {
13
+ column: t.vector.col,
14
+ queryText: t.vector.query,
15
+ distance: t.vector.distance ?? "cosine",
16
+ alpha: n
17
+ }, a = e(r.table, t.fts.indexName, r.fullTextIndexes);
18
+ return {
19
+ ...r,
20
+ annClause: i,
21
+ fullTextSearch: {
22
+ indexName: t.fts.indexName,
23
+ query: t.fts.query,
24
+ columns: a.columns,
25
+ ...a.config === void 0 ? {} : { config: a.config },
26
+ ...a.weights === void 0 ? {} : { weights: a.weights }
27
+ }
28
+ };
29
+ };
30
+ };
31
+ //#endregion
32
+ export { t as hybridSearch };
@@ -0,0 +1,57 @@
1
+ import { validateTypeIdPrefix as e } from "./identifierLimits.js";
2
+ import { generateSnowflake as t } from "./snowflake.js";
3
+ import { typeid as n } from "typeid-js";
4
+ import { ulid as r } from "ulid";
5
+ //#region src/idGenerator.ts
6
+ var i = (t) => {
7
+ let n = t.toLowerCase();
8
+ if (n.startsWith("_")) throw Error(`cannot derive a typeid prefix for table '${t}' (table names starting with '_' are not valid typeid prefixes). Pick an explicit prefix via id({ prefix: '...' }) or use a different scheme like id({ scheme: 'numeric' }) for internal tables.`);
9
+ if (!/^[a-z][a-z0-9_]*$/.test(n)) throw Error(`cannot derive a typeid prefix from table name '${t}' (must match [a-z][a-z0-9_]*). Pick an explicit prefix via id({ prefix: '...' }).`);
10
+ let r = n.endsWith("ies") && n.length > 3 ? n.slice(0, -3) + "y" : n.endsWith("ses") || n.endsWith("xes") || n.endsWith("zes") || n.endsWith("shes") || n.endsWith("ches") ? n.slice(0, -2) : n.endsWith("s") && n.length > 1 ? n.slice(0, -1) : n;
11
+ return e(r, {
12
+ kind: "derived",
13
+ tableName: t
14
+ }), r;
15
+ }, a = (t, n) => {
16
+ if (t === void 0) return {
17
+ kind: "typeid",
18
+ prefix: i(n)
19
+ };
20
+ if ("prefix" in t && !("scheme" in t)) return e(t.prefix, {
21
+ kind: "explicit",
22
+ tableName: n
23
+ }), {
24
+ kind: "typeid",
25
+ prefix: t.prefix
26
+ };
27
+ if (!("scheme" in t)) throw Error("invalid id() options: no scheme or prefix");
28
+ switch (t.scheme) {
29
+ case "typeid": {
30
+ let r = t.prefix ?? i(n);
31
+ return t.prefix !== void 0 && e(t.prefix, {
32
+ kind: "explicit",
33
+ tableName: n
34
+ }), {
35
+ kind: "typeid",
36
+ prefix: r
37
+ };
38
+ }
39
+ case "ulid": return { kind: "ulid" };
40
+ case "numeric": return { kind: "numeric" };
41
+ case "snowflake": return { kind: "snowflake" };
42
+ case "custom": return {
43
+ kind: "custom",
44
+ generate: t.generate
45
+ };
46
+ }
47
+ }, o = (e, i) => {
48
+ switch (e.kind) {
49
+ case "typeid": return n(e.prefix).toString();
50
+ case "ulid": return r();
51
+ case "numeric": return null;
52
+ case "snowflake": return t();
53
+ case "custom": return e.generate(i);
54
+ }
55
+ };
56
+ //#endregion
57
+ export { i as deriveTypeIdPrefix, o as generateId, a as resolveIdScheme };
@@ -0,0 +1,32 @@
1
+ //#region src/identifierLimits.ts
2
+ var e = 63, t = 60, n = /* @__PURE__ */ new Set(), r = (e) => {
3
+ n.add(e);
4
+ }, i = () => {
5
+ let e = [...n];
6
+ return n.clear(), e;
7
+ }, a = /^[a-zA-Z_][a-zA-Z0-9_]*$/, o = (e, t) => {
8
+ if (t.length === 0) throw Error(`${e} cannot be empty.`);
9
+ if (!a.test(t)) throw Error(`${e} '${t}' contains characters outside [A-Za-z0-9_] or starts with a digit. Dialect-portable identifiers must match /^[a-zA-Z_][a-zA-Z0-9_]*$/.`);
10
+ }, s = (e) => {
11
+ if (o("table name", e), e.length > 63) throw Error(`table name '${e}' is ${e.length} chars — exceeds the 63-byte cross-dialect limit (Postgres floor). Shorten the name; long table names also cascade into auto-named indexes and FK constraints that would themselves exceed the limit.`);
12
+ e.length > 60 && r(`table name '${e}' is ${e.length} chars — ${63 - e.length} from the Postgres 63-byte limit. Auto-named indexes on this table will run out of room fast.`);
13
+ }, c = (e, t) => {
14
+ if (o("column name", t), t.length > 63) throw Error(`column '${t}' on table '${e}' is ${t.length} chars — exceeds the 63-byte cross-dialect limit (Postgres floor). Rename it.`);
15
+ t.length > 60 && r(`column '${t}' on table '${e}' is ${t.length} chars — close to the 63-byte limit.`);
16
+ }, l = (e, t, n, i) => {
17
+ if (o("index name", t), t.length > 63) throw Error(n ? `auto-named index '${t}' on table '${e}' is ${t.length} chars — exceeds the 63-byte cross-dialect limit (Postgres floor). Supply an explicit short name: .index('<shortName>', ${JSON.stringify([...i])}).` : `index name '${t}' on table '${e}' is ${t.length} chars — exceeds the 63-byte cross-dialect limit (Postgres floor). Shorten it.`);
18
+ t.length > 60 && r(`index '${t}' on table '${e}' is ${t.length} chars — ${63 - t.length} from the 63-byte limit. ${n ? `Consider .index('<shortName>', ${JSON.stringify([...i])}) before it grows.` : ""}`);
19
+ }, u = (e, t) => {
20
+ if (e.length === 0) throw Error(`typeid prefix on table '${t.tableName}' cannot be empty.`);
21
+ if (!/^[a-z][a-z0-9_]*$/.test(e)) {
22
+ let n = t.kind === "derived" ? "the derived prefix doesn't match typeid's required shape — supply an explicit one via id({ prefix: '...' })." : "the prefix must match typeid's required shape /^[a-z][a-z0-9_]*$/.";
23
+ throw Error(`typeid prefix '${e}' on table '${t.tableName}' is invalid — ${n}`);
24
+ }
25
+ if (e.length > 63) {
26
+ let n = t.kind === "derived" ? "shorten the table name, or supply an explicit short prefix via id({ prefix: '...' })." : "pick a shorter prefix.";
27
+ throw Error(`typeid prefix '${e}' on table '${t.tableName}' is ${e.length} chars — exceeds the 63-byte limit; ${n}`);
28
+ }
29
+ e.length > 60 && r(`typeid prefix '${e}' on table '${t.tableName}' is ${e.length} chars — close to the 63-byte limit. Generated ids will be even longer (prefix + '_' + 26).`);
30
+ };
31
+ //#endregion
32
+ export { e as MAX_IDENTIFIER_LENGTH, t as WARN_IDENTIFIER_LENGTH, i as drainIdentifierWarnings, c as validateColumnName, l as validateIndexName, s as validateTableName, u as validateTypeIdPrefix };
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ import { YieldableError } from 'effect/Cause';
17
17
 
18
18
  /** The store shape this needs — a read, nothing more. */
19
19
  export declare interface ActorLookupStore {
20
- query: (descriptor: unknown) => Promise<ReadonlyArray<Record<string, unknown>>>;
20
+ query: (descriptor: QueryDescriptor) => Promise<ReadonlyArray<Record<string, unknown>>>;
21
21
  }
22
22
 
23
23
  /** The framework's built-in `actors` core table. */
@@ -4567,11 +4567,17 @@ export declare type IndexField = string | {
4567
4567
  export declare type IndexKind = 'btree' | 'gist' | 'gin' | 'brin' | 'hnsw';
4568
4568
 
4569
4569
  /**
4570
- * Per-kind tuning options. Today only `hnsw` carries knobs (the pgvector
4571
- * build-time params). `m` controls graph connectivity (default 16),
4570
+ * Per-kind tuning options. GIN supports a single-column operator class;
4571
+ * HNSW supports pgvector build-time parameters. `m` controls graph connectivity (default 16),
4572
4572
  * `efConstruction` the build-time candidate-list size (default 64).
4573
4573
  */
4574
4574
  export declare interface IndexKindOptions {
4575
+ /** PostgreSQL GIN operator class for a single plain column.
4576
+ * `gin_trgm_ops` installs pg_trgm and accelerates contains/ILIKE searches.
4577
+ */
4578
+ readonly gin?: {
4579
+ readonly opclass: string;
4580
+ };
4575
4581
  readonly hnsw?: {
4576
4582
  readonly m?: number;
4577
4583
  readonly efConstruction?: number;
@@ -6534,6 +6540,11 @@ Joins extends JoinsMap = {}> {
6534
6540
  export declare interface QueryDescriptor<R = Row> {
6535
6541
  readonly table: string;
6536
6542
  readonly predicate: Predicate | undefined;
6543
+ /** Additional reactive invalidation sources, independent of SQL row selection.
6544
+ * The runtime merges a query handler's declared `source` tables here. Changes
6545
+ * to these tables recheck subscription authority even when SQL rows do not
6546
+ * change. They add no joins, predicates or cross-tenant access. */
6547
+ readonly dependentTables?: ReadonlyArray<string>;
6537
6548
  /** Phantom — carries the row type through to `store.query()`. Never set at
6538
6549
  * runtime; erased entirely by the compiler. */
6539
6550
  readonly __row?: R;
@@ -7470,12 +7481,12 @@ declare type Row_2 = Readonly<Record<string, unknown>>;
7470
7481
  declare type Row_3 = Readonly<Record<string, unknown>>;
7471
7482
 
7472
7483
  /** Addresses one row: its `id`, or the columns of an explicit primary key. */
7473
- export declare type RowKey = string | Readonly<Record<string, unknown>>;
7484
+ export declare type RowKey = string | number | Readonly<Record<string, unknown>>;
7474
7485
 
7475
7486
  /**
7476
7487
  * The `(column, value)` pairs a key addresses, in declaration order.
7477
7488
  *
7478
- * A string is the `id` column — that is the contract every existing caller
7489
+ * A scalar is the `id` column — that is the contract every existing caller
7479
7490
  * relies on, and widening it must not change what a string means.
7480
7491
  */
7481
7492
  export declare const rowKeyColumns: (key: RowKey) => ReadonlyArray<readonly [string, unknown]>;
@@ -8914,8 +8925,8 @@ export declare interface TableIndex {
8914
8925
  */
8915
8926
  readonly kind?: IndexKind;
8916
8927
  /**
8917
- * Per-kind tuning knobs. Today only `hnsw` (`kindOptions.hnsw.{m,
8918
- * efConstruction, opclass}`). Ignored for kinds that don't read it.
8928
+ * Per-kind tuning: `gin.opclass` or `hnsw.{m, efConstruction, opclass}`.
8929
+ * Ignored for kinds that do not read the corresponding options.
8919
8930
  */
8920
8931
  readonly kindOptions?: IndexKindOptions;
8921
8932
  /**