@voltro/sql-mysql 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -39,6 +39,90 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.11.0] — 2026-07-22
43
+
44
+ ### ⚠ BREAKING
45
+
46
+ - **@voltro/client, @voltro/web** — **@voltro/client** — `useSequence` steps gain **`covers`** and **`when`**, and the undo contract that was implicit is now written down.
47
+
48
+ **`covers` — overlapping undos.** Every succeeded step's undo runs, and the runner has no idea whether two of them reverse the same thing. Reported from a Jira rollback: `deleteJiraDraftTicket` deletes the issue *and* discards the draft, so the earlier `discardDraft` undo ran on something already gone. It worked only because discarding is idempotent — and **nothing said that was load-bearing**. For a refund or a cancellation email the double-run is a defect, not a nuisance.
49
+
50
+ ```tsx
51
+ .step('draft', createDraft, { undo: discardDraft })
52
+ .step('jira', createTicket, { undo: deleteJiraDraftTicket, covers: ['draft'] })
53
+ ```
54
+
55
+ `covers` is static rather than a runtime signal on purpose: "this reverse also reverses that one" is a property of the operation, legible where it is defined and checkable against the step names in scope.
56
+
57
+ **When the covering undo FAILS**, the covered steps are neither run nor claimed. Whether the cascade got that far is genuinely unknown — running the covered undo risks the double reverse, skipping it risks an orphan — so both guesses are refused and the steps come back in `compensationUncertain` with the step that was supposed to cover them. Same principle as never compensating the step that failed: surface the ambiguity, don't resolve it by assumption.
58
+
59
+ **`when` — one optional step.** 13 multi-await blocks in one app; only 5 could migrate. Several of the rest were linear *except* for one conditional step ("schedule the summary only if the set changed", `if (assigneeKey) assign else unassign`) and fell back to `try/catch` entirely, though 80% of the flow was a clean pipeline. An optional step is a different shape from a loop, and the scoping was treating them the same.
60
+
61
+ ```tsx
62
+ .step('summary', (c) => scheduleSummary.run({ id: c.save.id }), { when: (c) => c.save.changed })
63
+ ```
64
+
65
+ A skipped step contributes `undefined` to the context — the overload says so, so a later step has to acknowledge it — and gets no undo, since it had no effect to reverse. Loops and real branches still keep their `try/catch`; this deliberately does not widen to them.
66
+
67
+ **The break:** `StepOptions` gained a type parameter — `StepOptions<Result>` became `StepOptions<Ctx, Result>`, because `covers` and `when` both need to see the accumulated context (`covers` is checked against the step names in scope; `when` receives it). Call sites that pass an object literal to `.step()` are unaffected — the type is inferred — but anyone who *named* the type explicitly gets a compile error. Filed BREAKING rather than Added for the same reason a widened union is: the test is "can this turn code that compiled into code that does not", and it can. `@voltro/web` is listed because it re-exports the client surface — the third time in this round that coupling has decided where a change lands.
68
+
69
+ ### Added
70
+
71
+ - **@voltro/cli** — **Repo gate** — a new `Claimed-wiring check` (`scripts/check-claimed-wirings.mjs`, wired into CI and therefore into `pnpm gate`): a doc comment that says something wires a symbol up must be telling the truth.
72
+
73
+ `setSystemStoreHandle`'s comment read *"Process-wide handle, registered by the runtime boot (dev.ts / start.ts)"*. Nothing registered it, in either path, through an entire release — so `runAsSystem` threw for every consumer, and the comment was the only evidence anyone had that it should work. The shape is not rare: a comment gets written when the wiring is planned, the wiring gets deferred, and the comment never finds out. It then reads as documentation of behaviour rather than of intention, and the more confidently it is phrased the less likely anyone is to check it.
74
+
75
+ Two things it does that the obvious version does not, both learned by watching it report the bug as clean:
76
+
77
+ - **it counts real call expressions, not text.** The first version matched regexes and found `setSystemStoreHandle({ … })` inside `runAsSystem`'s own error-message string — a text match cannot tell a call from a sentence about a call. (Precisely the defect fixed in the hand-roll detector one commit earlier, repeated one file later.) - **it checks the NAMED caller, not any caller.** The second version asked "does anything call this"; `@voltro/testing` calls it from a test harness, so the bug read clean again. A claim that the runtime boot registers something is not satisfied by a test helper registering it.
78
+
79
+ Verified the only way this kind of check can be: by removing the wiring and confirming it fails, with the diagnosis that would have saved the original investigation — `called by: packages/testing/src/testContext.ts ← none of these is the boot`.
80
+
81
+ ### Fixed
82
+
83
+ - **@voltro/database, @voltro/runtime, @voltro/sql-postgres, @voltro/sql-mysql, @voltro/sql-sqlite, @voltro/sql-mssql** — **@voltro/database + every store** — a row inserted into a table that declares an `id()` scheme now gets one **at the store**, not only when the caller happened to go through `wrapStoreWithMixinBehaviour`.
84
+
85
+ Id generation was sitting one layer too high. It is the single stamped field that needs no subject — the scheme is a property of the declared table — yet it lived in the subject-aware wrapper. So every insert through an unwrapped store reached the database with `id: null`:
86
+
87
+ ```
88
+ null value in column "id" of relation "_voltro_seeds" violates not-null constraint
89
+ ```
90
+
91
+ That was a real shipped bug in the seed ledger (the runner holds the raw store), and a survey found the same shape waiting elsewhere: `plugin-rbac/userRoleStore.ts`, `plugin-governance/consent.ts`, and `plugin-sso-saml/saml-cache.ts` all insert without an id into tables that declare one. Whether any of them broke came down to how their caller happened to obtain its store — and *"depends on how the caller obtained its store"* is not a contract, it is a coin flip with a NOT NULL constraint on the other side.
92
+
93
+ `stampGeneratedId` now runs in every store's insert path (all four dialects plus the in-memory store, at the private `executeInsert` / `executeInsertMany` / `executeInsertIgnore` choke points that every public and namespace-view path funnels through). It preserves the semantics the wrapper had: an explicit id is never overwritten, a `numeric` scheme deletes the key so the dialect's SERIAL fires, and an unregistered table is passed through untouched rather than guessed at.
94
+
95
+ The wrapper still stamps — it holds the schema registry and does the subject-derived fields in the same pass — and now finds the id already set. That is a floor, not a second implementation of a rule: the invariant is "a row that reaches the database has an id when its table declares a generating scheme", and only the store can promise that for *every* caller, including a `DataStore` someone implemented themselves.
96
+ - **@voltro/cli** — **Repo tests** — the liveness backstop for real-listener / real-child-process suites goes from 60s to 120s, in `vitest.config.ts` and the CI flag that overrides it.
97
+
98
+ Worth stating plainly what this number is, because raising a timeout is the classic way to bury a problem: it asserts nothing about performance. A real `serveApi` boot is ~0.2s in isolation. The value is pure headroom against residual starvation that the four existing mitigations — the unit/integration project split, group ordering, `fileParallelism: false`, per-package mssql databases — cannot reach, because the remaining contention is turbo running two *packages* concurrently alongside the docker stack.
99
+
100
+ 60s tripped twice in one session, on two different files (`connectionServe.test.ts`, then `serveApi.test.ts`), each passing in ~2s alone. Different file each time, always in the same family, never reproducible in isolation: that is the signature of starvation rather than of a slow test, and it cost two full gate runs.
101
+
102
+ What would not be honest is treating a green run afterwards as evidence the contention is gone. It is not. If a third file trips this, the answer is to stop running two heavy packages concurrently — not to raise it again.
103
+ - **@voltro/cli, @voltro/database** — **@voltro/cli, @voltro/database** — two fixes to the 0.10.0 seed ledger, both reported from its first real use, both mine.
104
+
105
+ **The ledger never wrote.** `store.insert('_voltro_seeds', …)` ran against the RAW store, and auto-id lives in `wrapStoreWithMixinBehaviour` — so the row reached postgres with `id: null` and died on the NOT NULL constraint. Every boot reported `ran=1 skipped=0` regardless of fingerprint: the feature shipped doing nothing. The ledger now stamps its own id, derived from `_voltroSeedsTable`'s declared scheme rather than a hardcoded prefix, so it no longer depends on how a caller happens to have wrapped its store.
106
+
107
+ **Worse than the bug was the logging.** I put the failure on `debug` and swallowed it, reasoning that "it degrades to re-running, which is only a performance regression". That reasoning is exactly what made it undiagnosable: a silently unwritten ledger looks identical to a working one whose seeds all changed — no symptom, nothing to grep, and the reporter had to read the error out of a debug stream to find it. Both the write and the read path now warn, naming the table and the consequence.
108
+
109
+ **And the reason it shipped:** the test fake *invented an id* when the row lacked one, making it more permissive than any database. A fake that supplies what the subject under test forgot is not a test — it is the subject testing itself. It now rejects an id-less insert with the real constraint's message; reintroducing the bug fails five cases.
110
+
111
+ **A seed step could not reach a usable store.** `SeedStore` exposed only `query`/`insert`/`update`/`delete`, and `query` took just `{ table, predicate }`. A restore of 1361 rows across 167 tables, multi-pass for FK order, needs an idempotent insert and a read it can page — and `upsertByUnique` is neither: a read per row, and it overwrites what it finds, which is wrong whenever the live row is newer than the snapshot. `SeedStore` now carries **`insertIgnore`** (one statement per row, native on every dialect) and a full descriptor read (`order` / `take` / `skip` / `projection`).
112
+
113
+ Seed reads are unscoped and include soft-deleted rows *by construction* — a seed runs at boot with no request and therefore no subject, so nothing applies a tenant filter or the `deletedAt IS NULL` predicate. That is now documented on the type rather than left to be discovered, since the absence of `.unscoped()` / `.withDeleted()` reads as a missing feature until you know why they cannot exist here.
114
+
115
+ *(`apiSurface: compatible`: the golden churn in `@voltro/database` is two `(undocumented)` markers disappearing because `SeedStore` and its new member gained TSDoc — a comment cannot break a caller. The added `insertIgnore` member is additive for consumers, which is everyone: a `SeedStore` is what `ctx.store` IS, handed to you by the runner. Nobody constructs one, so nobody can be missing a member.)*
116
+ - **@voltro/cli** — **@voltro/cli** — `runAsSystem` now works. The process-wide system store is registered at boot by **both** `voltro dev` and `voltro serve`; until now neither did, so every call threw `no data store available — register one at boot via setSystemStoreHandle`.
117
+
118
+ `setSystemStoreHandle`'s own doc comment reads "registered by the runtime boot (dev.ts / start.ts)". It describes wiring that was never written: the only callers in the repo were its unit test and a note in `@voltro/testing`. So the failure was not a lifecycle-ordering subtlety — the handle was never set at any point, in any command, and `runAsSystem` was unusable for every consumer.
119
+
120
+ Surfaced by someone reporting it as "not registered *yet* at seed time", which implied it worked later. Checking that framing rather than the symptom is what turned a scheduling question into a missing-wiring one. It is registered before the seed runner in dev, since seeds are the earliest thing that can plausibly want it.
121
+
122
+ Same class as the seed lifecycle table and the `@voltro/web` re-export: a documented behaviour with nothing behind it, where the doc is the only evidence anyone has.
123
+
124
+ ---
125
+
42
126
  ## [0.10.0] — 2026-07-22
43
127
 
44
128
  ### ⚠ BREAKING
package/dist/index.js CHANGED
@@ -3,16 +3,16 @@ import { Config as t, Effect as n, Layer as r, ManagedRuntime as i, Option as a,
3
3
  import { EventEmitter as c } from "node:events";
4
4
  import { createLogger as l } from "@voltro/logger";
5
5
  import { SqlClient as u, TransactionConnection as d } from "@effect/sql/SqlClient";
6
- import { CDC_OFFSETS_TABLE as f, EagerCardinalityError as p, _voltroCdcOffsetsTable as m, attachEagerLoads as h, compileEagerJson as g, compilePredicate as _, compileRawFragment as v, compileSelect as y, decodeRowsFromSchema as b, encodeRowForSchema as x, hasEagerLoads as S, qualifyTable as C, raiseChangeListenerCeiling as w, requireTable as T } from "@voltro/database";
6
+ import { CDC_OFFSETS_TABLE as f, EagerCardinalityError as p, _voltroCdcOffsetsTable as m, attachEagerLoads as h, compileEagerJson as g, compilePredicate as _, compileRawFragment as v, compileSelect as y, decodeRowsFromSchema as b, encodeRowForSchema as x, hasEagerLoads as S, qualifyTable as C, raiseChangeListenerCeiling as w, requireTable as T, stampGeneratedId as E, stampGeneratedIds as D } from "@voltro/database";
7
7
  //#region src/sqlLayer.ts
8
- var E = (n) => e.layerConfig({
8
+ var O = (n) => e.layerConfig({
9
9
  host: t.succeed(n.host),
10
10
  port: t.succeed(n.port),
11
11
  username: t.succeed(n.username),
12
12
  password: t.succeed(o.make(n.password)),
13
13
  database: t.succeed(n.database),
14
14
  ...n.maxConnections === void 0 ? {} : { maxConnections: t.succeed(n.maxConnections) }
15
- }), D = (e) => {
15
+ }), k = (e) => {
16
16
  if (e.url) {
17
17
  let t = new URL(e.url);
18
18
  return {
@@ -32,11 +32,11 @@ var E = (n) => e.layerConfig({
32
32
  database: e.database ?? "app",
33
33
  ...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections }
34
34
  };
35
- }, O = (e) => E(D(e)), k = (e, t) => e === null ? !1 : t === null ? !0 : e.filename === t.filename ? e.position > t.position : e.filename > t.filename, A = (e) => e.msSinceProgress < e.stallThresholdMs ? "healthy" : e.primary !== null && !k(e.primary, e.reader) ? "idle-caught-up" : "reconnect", j = (e) => (e instanceof Error ? e.message : String(e ?? "")).includes("schema changed between binlog event and metadata fetch"), M = /* @__PURE__ */ new Set([
35
+ }, A = (e) => O(k(e)), j = (e, t) => e === null ? !1 : t === null ? !0 : e.filename === t.filename ? e.position > t.position : e.filename > t.filename, M = (e) => e.msSinceProgress < e.stallThresholdMs ? "healthy" : e.primary !== null && !j(e.primary, e.reader) ? "idle-caught-up" : "reconnect", N = (e) => (e instanceof Error ? e.message : String(e ?? "")).includes("schema changed between binlog event and metadata fetch"), P = /* @__PURE__ */ new Set([
36
36
  "writerows",
37
37
  "updaterows",
38
38
  "deleterows"
39
- ]), N = /\b(alter|rename|drop|create)\s+(table|column)?/i, P = (e) => new Promise((t) => setTimeout(t, e)), F = async (e) => {
39
+ ]), F = /\b(alter|rename|drop|create)\s+(table|column)?/i, I = (e) => new Promise((t) => setTimeout(t, e)), L = async (e) => {
40
40
  let t = l({ scope: `voltro:${e.variant}:cdc` }), n;
41
41
  try {
42
42
  n = (await import("@vlasky/zongji")).default;
@@ -52,14 +52,14 @@ var E = (n) => e.layerConfig({
52
52
  a = n.binlogName;
53
53
  return;
54
54
  }
55
- if (l === "query" && n.query && N.test(n.query)) {
55
+ if (l === "query" && n.query && F.test(n.query)) {
56
56
  c && (c.tableMap = {});
57
57
  return;
58
58
  }
59
59
  if (n.nextPosition && a && (o = {
60
60
  filename: a,
61
61
  position: n.nextPosition
62
- }, e.onPosition?.(o)), !M.has(l)) return;
62
+ }, e.onPosition?.(o)), !P.has(l)) return;
63
63
  let u = n.tableMap[n.tableId];
64
64
  if (!u || u.parentSchema !== i) return;
65
65
  let d = u.tableName;
@@ -137,8 +137,8 @@ var E = (n) => e.layerConfig({
137
137
  try {
138
138
  c?.stop();
139
139
  } catch {}
140
- if (d++, await P(Math.min(3e4, 500 * 2 ** Math.min(d, 6))), u) return;
141
- let r = o, i = x(n), s = !i && j(n);
140
+ if (d++, await I(Math.min(3e4, 500 * 2 ** Math.min(d, 6))), u) return;
141
+ let r = o, i = x(n), s = !i && N(n);
142
142
  (i || s) && (t.warn(s ? "cdc: un-replayable backlog event (schema moved past it) — jumping to current end + self-heal" : "cdc: binlog gap (purged/failover) — jumping to current end + self-heal"), r = e.resolveStartPosition ? await e.resolveStartPosition().catch(() => null) : null, a = r?.filename ?? null, e.onResync?.());
143
143
  try {
144
144
  await S(r), d = 0, _();
@@ -161,14 +161,14 @@ var E = (n) => e.layerConfig({
161
161
  c?.stop();
162
162
  } catch {}
163
163
  let n = e.resolveStartPosition ? await e.resolveStartPosition().catch(() => null) : null;
164
- a = n?.filename ?? null, e.onResync?.(), await P(500), await S(n);
164
+ a = n?.filename ?? null, e.onResync?.(), await I(500), await S(n);
165
165
  } else throw n;
166
166
  }
167
167
  let w = async () => {
168
168
  if (u || p || Date.now() - f < h) return;
169
169
  let n = null;
170
170
  if (e.resolveStartPosition && (n = await e.resolveStartPosition().catch(() => null)), u || p) return;
171
- let r = A({
171
+ let r = M({
172
172
  msSinceProgress: Date.now() - f,
173
173
  stallThresholdMs: h,
174
174
  primary: n,
@@ -197,7 +197,7 @@ var E = (n) => e.layerConfig({
197
197
  },
198
198
  currentPosition: () => o
199
199
  };
200
- }, I = /* @__PURE__ */ new Set(["1213", "1205"]), L = (e) => {
200
+ }, R = /* @__PURE__ */ new Set(["1213", "1205"]), z = (e) => {
201
201
  let t = e;
202
202
  for (let e = 0; e < 5 && typeof t == "object" && t; e++) {
203
203
  let e = t.errno;
@@ -206,31 +206,31 @@ var E = (n) => e.layerConfig({
206
206
  if (typeof n == "string") return n;
207
207
  t = t.cause;
208
208
  }
209
- }, R = (e) => {
210
- let t = L(e);
211
- return t !== void 0 && I.has(t);
212
- }, z = (e) => R(e) ? "retry" : "noRetry", B = (e) => {
209
+ }, B = (e) => {
210
+ let t = z(e);
211
+ return t !== void 0 && R.has(t);
212
+ }, V = (e) => B(e) ? "retry" : "noRetry", H = (e) => {
213
213
  if (e == null) return "null";
214
214
  let t = typeof e;
215
215
  if (t === "bigint") return `${e}n`;
216
216
  if (t !== "object") return JSON.stringify(e);
217
217
  if (e instanceof Date) return `"${e.toISOString()}"`;
218
- if (Array.isArray(e)) return `[${e.map(B).join(",")}]`;
218
+ if (Array.isArray(e)) return `[${e.map(H).join(",")}]`;
219
219
  let n = e;
220
- return `{${Object.keys(n).sort().map((e) => `${JSON.stringify(e)}:${B(n[e])}`).join(",")}}`;
221
- }, V = (e) => {
222
- let t = B(e), n = 2166136261;
220
+ return `{${Object.keys(n).sort().map((e) => `${JSON.stringify(e)}:${H(n[e])}`).join(",")}}`;
221
+ }, U = (e) => {
222
+ let t = H(e), n = 2166136261;
223
223
  for (let e = 0; e < t.length; e++) n ^= t.charCodeAt(e), n = Math.imul(n, 16777619);
224
224
  return (n >>> 0).toString(36);
225
- }, H = (e, t) => {
225
+ }, W = (e, t) => {
226
226
  let n = setTimeout(e, t);
227
227
  typeof n.unref == "function" && n.unref();
228
- }, U = class {
228
+ }, G = class {
229
229
  variant;
230
230
  ttlMs;
231
231
  schedule;
232
232
  seen = /* @__PURE__ */ new Map();
233
- constructor(e, t = 6e4, n = H) {
233
+ constructor(e, t = 6e4, n = W) {
234
234
  this.variant = e, this.ttlMs = t, this.schedule = n;
235
235
  }
236
236
  key(e) {
@@ -244,7 +244,7 @@ var E = (n) => e.layerConfig({
244
244
  } catch {
245
245
  r = t;
246
246
  }
247
- return `${e.table} ${e.op} ${String(n)} ${V(r)}`;
247
+ return `${e.table} ${e.op} ${String(n)} ${U(r)}`;
248
248
  }
249
249
  admit(e) {
250
250
  let t = this.key(e);
@@ -259,12 +259,12 @@ var E = (n) => e.layerConfig({
259
259
  get pending() {
260
260
  return this.seen.size;
261
261
  }
262
- }, W = async (e) => {
262
+ }, K = async (e) => {
263
263
  let t = e.variant ?? "mysql", n = l({ scope: `voltro:${t}` }), a = e.changeStrategy ?? "inline", o = a;
264
264
  a === "cdc" && !e.cdcConfig && (n.warn("changeStrategy='cdc' requires cdcConfig (serverId + connection); falling back to 'inline'."), o = "inline");
265
- let s = e.tracerLayer ? r.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, c = i.make(s), d = new G(await c.runPromise(u), c, t, o);
265
+ let s = e.tracerLayer ? r.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, c = i.make(s), d = new q(await c.runPromise(u), c, t, o);
266
266
  return o === "cdc" && e.cdcConfig && await d.startCdcConsumer(e.cdcConfig), d;
267
- }, G = class e {
267
+ }, q = class e {
268
268
  sql;
269
269
  runtime;
270
270
  variant;
@@ -280,7 +280,7 @@ var E = (n) => e.layerConfig({
280
280
  cdcStreamName = "default";
281
281
  cdcGate;
282
282
  constructor(e, t, n, r = "inline", i = null, a, o) {
283
- this.sql = e, this.runtime = t, this.variant = n, this.changeStrategy = r, this.namespace = i, this.log = l({ scope: `voltro:${n}` }), this.emitter = a ?? new c(), w(this.emitter), this.cdcGate = o ?? new U(n);
283
+ this.sql = e, this.runtime = t, this.variant = n, this.changeStrategy = r, this.namespace = i, this.log = l({ scope: `voltro:${n}` }), this.emitter = a ?? new c(), w(this.emitter), this.cdcGate = o ?? new G(n);
284
284
  }
285
285
  withNamespace(t) {
286
286
  return t === this.namespace ? this : new e(this.sql, this.runtime, this.variant, this.changeStrategy, t, this.emitter, this.cdcGate);
@@ -311,6 +311,7 @@ var E = (n) => e.layerConfig({
311
311
  return !1;
312
312
  }
313
313
  async executeInsert(e, t, r, i) {
314
+ t = E(e, t);
314
315
  let a = this.sql;
315
316
  if (this.supportsInsertReturning) {
316
317
  let o = a`INSERT INTO ${a(this.nsT(e))} ${a.insert(x(t, e))} RETURNING *`, s = r ? n.provideService(o, d, r) : o, c = (await this.runtime.runPromise(s))[0];
@@ -356,7 +357,7 @@ var E = (n) => e.layerConfig({
356
357
  if (e) return this.runtime.runPromise(t(e));
357
358
  this.inflightTxns++;
358
359
  try {
359
- let e = n.suspend(() => this.sql.withTransaction(n.flatMap(n.serviceOption(d), (e) => a.isNone(e) ? n.fail(/* @__PURE__ */ Error("MysqlStore.insert: TransactionConnection missing.")) : t(e.value)))), i = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(R)), o = e.pipe(n.retry(i), n.withSpan("store.insert", { attributes: {
360
+ let e = n.suspend(() => this.sql.withTransaction(n.flatMap(n.serviceOption(d), (e) => a.isNone(e) ? n.fail(/* @__PURE__ */ Error("MysqlStore.insert: TransactionConnection missing.")) : t(e.value)))), i = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(B)), o = e.pipe(n.retry(i), n.withSpan("store.insert", { attributes: {
360
361
  "db.system": this.variant,
361
362
  "db.operation": r
362
363
  } }));
@@ -366,7 +367,7 @@ var E = (n) => e.layerConfig({
366
367
  }
367
368
  }
368
369
  async executeInsertMany(e, t, r, i) {
369
- if (t.length === 0) return [];
370
+ if (t = D(e, t), t.length === 0) return [];
370
371
  let a = this.sql, o = t.map((t) => x(t, e));
371
372
  if (this.supportsInsertReturning) {
372
373
  let t = a`INSERT INTO ${a(this.nsT(e))} ${a.insert(o)} RETURNING *`, s = r ? n.provideService(t, d, r) : t, c = await this.runtime.runPromise(s);
@@ -506,7 +507,7 @@ var E = (n) => e.layerConfig({
506
507
  }, a), m;
507
508
  }
508
509
  async executeInsertIgnore(e, t, r, i, a) {
509
- if (this.variant === "mariadb") {
510
+ if (t = E(e, t), this.variant === "mariadb") {
510
511
  let o = this.sql, s = o`INSERT IGNORE INTO ${o(this.nsT(e))} ${o.insert(x(t, e))} RETURNING *`, c = i ? n.provideService(s, d, i) : s, l = (await this.runtime.runPromise(c))[0];
511
512
  if (l) return this.routeEvent({
512
513
  table: e,
@@ -590,7 +591,7 @@ var E = (n) => e.layerConfig({
590
591
  let r = t.map((e) => e.id), a = i`SELECT * FROM ${i(this.nsT(e))} WHERE ${i("id")} IN ${i.in(r)}`;
591
592
  return n.flatMap(n.provideService(l, d, s), () => n.provideService(a, d, s));
592
593
  });
593
- }))), c = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(R)), l = r.pipe(n.retry(c), n.withSpan("store.updateMany", { attributes: {
594
+ }))), c = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(B)), l = r.pipe(n.retry(c), n.withSpan("store.updateMany", { attributes: {
594
595
  "db.system": this.variant,
595
596
  "db.operation": "update"
596
597
  } })), u = await this.runtime.runPromise(l);
@@ -625,7 +626,7 @@ var E = (n) => e.layerConfig({
625
626
  if (a.isNone(t)) return n.fail(/* @__PURE__ */ Error("MysqlStore.deleteMany: TransactionConnection missing."));
626
627
  let o = t.value, s = r`SELECT * FROM ${r(this.nsT(e))} WHERE ${i} FOR UPDATE`, c = r`DELETE FROM ${r(this.nsT(e))} WHERE ${i}`;
627
628
  return n.flatMap(n.provideService(s, d, o), (e) => e.length === 0 ? n.succeed(e) : n.as(n.provideService(c, d, o), e));
628
- }))), o = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(R)), c = t.pipe(n.retry(o), n.withSpan("store.deleteMany", { attributes: {
629
+ }))), o = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(B)), c = t.pipe(n.retry(o), n.withSpan("store.deleteMany", { attributes: {
629
630
  "db.system": this.variant,
630
631
  "db.operation": "delete"
631
632
  } })), l = await this.runtime.runPromise(c);
@@ -649,7 +650,7 @@ var E = (n) => e.layerConfig({
649
650
  if (this.cdcHandle) return;
650
651
  await this.assertBinlogConfig(), this.cdcReplicaId = e.replicaId;
651
652
  let t = await this.readCdcOffset(e.replicaId) ?? await this.resolveBinlogEnd();
652
- this.cdcHandle = await F({
653
+ this.cdcHandle = await L({
653
654
  connection: e.connection,
654
655
  serverId: e.serverId,
655
656
  variant: this.variant,
@@ -769,7 +770,7 @@ var E = (n) => e.layerConfig({
769
770
  let r = ++t;
770
771
  return this.sql.withTransaction(n.flatMap(n.serviceOption(d), (t) => {
771
772
  if (a.isNone(t)) return n.fail(/* @__PURE__ */ Error("MysqlStore.transactional: TransactionConnection missing."));
772
- let i = new K(this, t.value);
773
+ let i = new J(this, t.value);
773
774
  return n.tryPromise({
774
775
  try: () => e(i).then((e) => ({
775
776
  result: e,
@@ -779,7 +780,7 @@ var E = (n) => e.layerConfig({
779
780
  catch: (e) => e
780
781
  });
781
782
  }));
782
- }), i = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(R)), o = r.pipe(n.retry(i), n.withSpan("store.transactional", { attributes: {
783
+ }), i = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(B)), o = r.pipe(n.retry(i), n.withSpan("store.transactional", { attributes: {
783
784
  "db.system": this.variant,
784
785
  "db.operation": "transaction"
785
786
  } }));
@@ -825,7 +826,7 @@ var E = (n) => e.layerConfig({
825
826
  async ping() {
826
827
  await this.runtime.runPromise(this.sql`SELECT 1`);
827
828
  }
828
- }, K = class {
829
+ }, J = class {
829
830
  parent;
830
831
  txn;
831
832
  events = [];
@@ -897,9 +898,9 @@ var E = (n) => e.layerConfig({
897
898
  this.events.length = 0;
898
899
  }
899
900
  }
900
- }, q = (e) => e.__mysqlReplicationFriend ?? null, J = () => ({
901
+ }, Y = (e) => e.__mysqlReplicationFriend ?? null, X = () => ({
901
902
  async capturePrimaryPosition(e) {
902
- let t = q(e);
903
+ let t = Y(e);
903
904
  if (t === null) throw Error("mysqlReplicationAdapter: primary is not a MysqlStore (missing __mysqlReplicationFriend).");
904
905
  return t.runEffect(n.gen(function* () {
905
906
  let e = yield* u, r = (t.variant === "mariadb" ? yield* e`SELECT @@global.gtid_current_pos AS gtid` : yield* e`SELECT @@global.gtid_executed AS gtid`)[0]?.gtid;
@@ -907,7 +908,7 @@ var E = (n) => e.layerConfig({
907
908
  }));
908
909
  },
909
910
  async probeReplicaPosition(e) {
910
- let t = q(e);
911
+ let t = Y(e);
911
912
  if (t === null) throw Error("mysqlReplicationAdapter: replica is not a MysqlStore.");
912
913
  return t.runEffect(n.gen(function* () {
913
914
  let e = yield* u, r = (t.variant === "mariadb" ? yield* e`SELECT @@global.gtid_current_pos AS gtid` : yield* e`SELECT @@global.gtid_executed AS gtid`)[0]?.gtid;
@@ -917,24 +918,24 @@ var E = (n) => e.layerConfig({
917
918
  compare(e, t) {
918
919
  return "behind";
919
920
  }
920
- }), Y = {
921
+ }), Z = {
921
922
  id: "mysql",
922
- makeSqlLayer: (e) => O(e),
923
- makeStore: (e) => W({
923
+ makeSqlLayer: (e) => A(e),
924
+ makeStore: (e) => K({
924
925
  ...e,
925
926
  variant: "mysql"
926
927
  }),
927
928
  compileContains: (e, t, n) => n`${e} LIKE ${`%${t.replace(/[\\%_]/g, (e) => `\\${e}`)}%`} ESCAPE '\\'`,
928
- retryFilter: z
929
- }, X = {
929
+ retryFilter: V
930
+ }, Q = {
930
931
  id: "mariadb",
931
- makeSqlLayer: (e) => O(e),
932
- makeStore: (e) => W({
932
+ makeSqlLayer: (e) => A(e),
933
+ makeStore: (e) => K({
933
934
  ...e,
934
935
  variant: "mariadb"
935
936
  }),
936
937
  compileContains: (e, t, n) => n`${e} LIKE ${`%${t.replace(/[\\%_]/g, (e) => `\\${e}`)}%`} ESCAPE '\\'`,
937
- retryFilter: z
938
+ retryFilter: V
938
939
  };
939
940
  //#endregion
940
- export { f as CDC_OFFSETS_TABLE, e as MysqlClient, m as _voltroCdcOffsetsTable, D as connectionFromConfig, E as makeMysqlSqlLayer, O as makeMysqlSqlLayerFromConfig, W as makeMysqlStore, X as mariadbDialect, Y as mysqlDialect, J as mysqlReplicationAdapter, z as mysqlRetryFilter, F as startBinlogCdc };
941
+ export { f as CDC_OFFSETS_TABLE, e as MysqlClient, m as _voltroCdcOffsetsTable, k as connectionFromConfig, O as makeMysqlSqlLayer, A as makeMysqlSqlLayerFromConfig, K as makeMysqlStore, Q as mariadbDialect, Z as mysqlDialect, X as mysqlReplicationAdapter, V as mysqlRetryFilter, L as startBinlogCdc };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/sql-mysql",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "MySQL/MariaDB dialect adapter for Voltro's cross-dialect DataStore (mariadb binlog CDC; mysql inline reactivity).",
5
5
  "keywords": [
6
6
  "voltro",
@@ -34,8 +34,8 @@
34
34
  "dependencies": {
35
35
  "@effect/sql": "^0.51.1",
36
36
  "@effect/sql-mysql2": "^0.52.0",
37
- "@voltro/database": "0.10.0",
38
- "@voltro/logger": "0.10.0"
37
+ "@voltro/database": "0.11.0",
38
+ "@voltro/logger": "0.11.0"
39
39
  },
40
40
  "optionalDependencies": {
41
41
  "@vlasky/zongji": "^0.9.0"