@voltro/sql-sqlite 0.33.0 → 0.35.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/dist/index.d.ts CHANGED
@@ -70,6 +70,10 @@ export declare class SqliteStore implements DataStore {
70
70
  private readonly wrapTransaction;
71
71
  private readonly emitter;
72
72
  private inflightTxns;
73
+ /** PERF-23 — counts EVERY eager JSON-agg → walker degradation and logs a
74
+ * rate-limited line. Instance-scoped so a second store in one process cannot
75
+ * silence this one's first warning. */
76
+ private readonly reportEagerFallback;
73
77
  constructor(sql: SqlClient.SqlClient, runtime: ManagedRuntime.ManagedRuntime<SqlClient.SqlClient, never>, namespace?: QueryNamespace, sharedEmitter?: EventEmitter, isRetryable?: (e: unknown) => boolean, systemName?: string, wrapTransaction?: <A, E, R>(e: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>);
74
78
  /**
75
79
  * Bind a per-request tenant namespace, returning a view that qualifies
@@ -119,6 +123,19 @@ export declare class SqliteStore implements DataStore {
119
123
  }): Promise<ReadonlyArray<T>>;
120
124
  private runWithEager;
121
125
  getInternalRunWithEager(): (d: QueryDescriptor, txn: TxnContext | null) => Promise<ReadonlyArray<Row>>;
126
+ /**
127
+ * Bracket a NON-transactional write: the attribution capture every public
128
+ * write already did, plus the ONE metrics funnel for this dialect's writes
129
+ * (`voltro_db_queries_total{op}`).
130
+ *
131
+ * Named to match postgres' and mysql's `localWrite` on purpose. Those two
132
+ * additionally hold the table's transport echoes for the write's duration —
133
+ * a CDC-only concern this dialect does not have (sqlite emits inline, one
134
+ * process) — so this one is the attribution + metrics half only. One name for
135
+ * the write funnel across four stores is what makes the parity test able to
136
+ * ask the question at all.
137
+ */
138
+ private localWrite;
122
139
  insert(table: string, row: Row): Promise<Row>;
123
140
  insertMany(table: string, rows: ReadonlyArray<Row>): Promise<ReadonlyArray<Row>>;
124
141
  patchJson(table: string, primaryKey: string, path: string, value: unknown): Promise<Row | null>;
@@ -163,6 +180,18 @@ export declare class SqliteStore implements DataStore {
163
180
  * retry — the whole point of `BEGIN CONCURRENT` — would never fire.
164
181
  */
165
182
  transactional<T>(work: (tx: DataStore) => Promise<T>): Promise<T>;
183
+ /**
184
+ * The DIALECT half of the shared transaction bracket
185
+ * (`runStoreTransaction` in `@voltro/database`).
186
+ *
187
+ * sqlite/turso own four things: the client's `withTransaction`, the runtime,
188
+ * the injected `isRetryable` predicate, and `wrapTransaction` — the fiber flag
189
+ * that upgrades turso's `BEGIN` to `BEGIN CONCURRENT`. Retry, commit-defect
190
+ * promotion, attribution threading and exit settling are shared; the
191
+ * defect-promotion in particular lived HERE and only here for a release, which
192
+ * is why it is no longer a per-store decision. See `transactionOutcome.ts`.
193
+ */
194
+ private txnSpec;
166
195
  onChange(listener: (event: ChangeEvent) => void): () => void;
167
196
  /** Cross-instance reactivity seam — emit an externally-sourced event to
168
197
  * local subscribers without re-persisting. sqlite is single-process so
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { SqliteClient as e } from "@effect/sql-sqlite-node";
2
- import { Config as t, Context as n, Effect as r, Layer as i, ManagedRuntime as a, Option as o, Schedule as s } from "effect";
3
- import { EventEmitter as c } from "node:events";
4
- import { createLogger as l } from "@voltro/logger";
5
- import { SqlClient as u, TransactionConnection as d } from "@effect/sql/SqlClient";
6
- import { EagerCardinalityError as f, attachEagerLoads as p, attributionFields as m, compileEagerJson as h, compilePredicate as g, compileRawFragment as _, compileSelect as v, currentWriteAttribution as y, decodeRowsFromSchema as b, hasEagerLoads as x, isTableReactive as S, qualifyTable as C, raiseChangeListenerCeiling as w, recordsTable as T, requireTable as E, runWithWriteAttribution as D, runWriteRecorders as O, settleTransactionExit as k, stampGeneratedId as A, stampGeneratedIds as j, withCapturedAttribution as M } from "@voltro/database";
2
+ import { Config as t, Context as n, Effect as r, Layer as i, ManagedRuntime as a } from "effect";
3
+ import { EventEmitter as o } from "node:events";
4
+ import { createLogger as s } from "@voltro/logger";
5
+ import { SqlClient as c, TransactionConnection as l } from "@effect/sql/SqlClient";
6
+ import { EagerCardinalityError as u, attachEagerLoads as d, attributionFields as f, bulkInsertLimitsFor as p, chunkRowsForInsert as m, compileEagerJson as h, compilePredicate as g, compileRawFragment as _, compileSelect as v, decodeRowsFromSchema as y, externalChangeEvent as b, hasEagerLoads as x, isTableReactive as S, makeEagerFallbackReporter as C, observeDbOp as w, qualifyTable as T, raiseChangeListenerCeiling as E, recordsTable as D, requireTable as O, runStoreTransaction as k, runWriteRecorders as A, stampGeneratedId as j, stampGeneratedIds as M, withCapturedAttribution as N } from "@voltro/database";
7
7
  //#region src/sqlLayer.ts
8
- var N = (r) => e.layerConfig({
8
+ var P = (r) => e.layerConfig({
9
9
  filename: t.succeed(r.filename),
10
10
  ...r.readonly === void 0 ? {} : { readonly: t.succeed(r.readonly) }
11
- }).pipe(i.tap((t) => n.get(t, e.SqliteClient)`PRAGMA foreign_keys = ON`)), P = (e) => {
11
+ }).pipe(i.tap((t) => n.get(t, e.SqliteClient)`PRAGMA foreign_keys = ON`)), F = (e) => {
12
12
  if (e.url) {
13
13
  if (e.url === ":memory:") return { filename: ":memory:" };
14
14
  if (e.url.startsWith("file:")) return { filename: e.url.slice(5) };
@@ -16,12 +16,12 @@ var N = (r) => e.layerConfig({
16
16
  }
17
17
  if (e.database) return { filename: e.database };
18
18
  throw Error("@voltro/sql-sqlite: no filename supplied. Set DB_URL=file:./db.sqlite or DB_DATABASE=path/to/db.sqlite.");
19
- }, F = (e) => N(P(e)), I = /* @__PURE__ */ new Set([
19
+ }, I = (e) => P(F(e)), L = /* @__PURE__ */ new Set([
20
20
  "SQLITE_BUSY",
21
21
  "SQLITE_LOCKED",
22
22
  "5",
23
23
  "6"
24
- ]), L = (e) => {
24
+ ]), R = (e) => {
25
25
  let t = e;
26
26
  for (let e = 0; e < 5 && typeof t == "object" && t; e++) {
27
27
  let e = t.code;
@@ -29,20 +29,17 @@ var N = (r) => e.layerConfig({
29
29
  if (typeof e == "number") return String(e);
30
30
  t = t.cause;
31
31
  }
32
- }, R = (e) => {
33
- let t = L(e);
34
- return t !== void 0 && I.has(t);
35
- }, z = (e) => R(e) ? "retry" : "noRetry", B = l({ scope: "voltro:sqlite" }), V = async (e) => {
32
+ }, z = (e) => {
33
+ let t = R(e);
34
+ return t !== void 0 && L.has(t);
35
+ }, B = (e) => z(e) ? "retry" : "noRetry", V = s({ scope: "voltro:sqlite" }), H = async (e) => {
36
36
  let t = e.tracerLayer ? i.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, n = a.make(t);
37
- return new G(await n.runPromise(u), n, null, void 0, e.isRetryable ?? R, e.systemName ?? "sqlite", e.wrapTransaction);
38
- }, H = (e) => e, U = (e) => {
37
+ return new G(await n.runPromise(c), n, null, void 0, e.isRetryable ?? z, e.systemName ?? "sqlite", e.wrapTransaction);
38
+ }, U = (e) => e, W = (e) => {
39
39
  if (typeof e != "object" || !e) return e;
40
40
  let t = {};
41
41
  for (let [n, r] of Object.entries(e)) r instanceof Date ? t[n] = r.toISOString() : typeof r == "boolean" ? t[n] = +!!r : t[n] = r;
42
42
  return t;
43
- }, W = () => {
44
- let e = y();
45
- return e === void 0 ? (e) => e() : (t) => D(e, t);
46
43
  }, G = class e {
47
44
  sql;
48
45
  runtime;
@@ -52,46 +49,55 @@ var N = (r) => e.layerConfig({
52
49
  wrapTransaction;
53
50
  emitter;
54
51
  inflightTxns = 0;
55
- constructor(e, t, n = null, r, i = R, a = "sqlite", o = H) {
56
- this.sql = e, this.runtime = t, this.namespace = n, this.isRetryable = i, this.systemName = a, this.wrapTransaction = o, this.emitter = r ?? new c(), w(this.emitter);
52
+ reportEagerFallback;
53
+ constructor(e, t, n = null, r, i = z, a = "sqlite", s = U) {
54
+ this.sql = e, this.runtime = t, this.namespace = n, this.isRetryable = i, this.systemName = a, this.wrapTransaction = s, this.emitter = r ?? new o(), E(this.emitter), this.reportEagerFallback = C(V);
57
55
  }
58
56
  withNamespace(t) {
59
57
  return t === this.namespace ? this : new e(this.sql, this.runtime, t, this.emitter, this.isRetryable, this.systemName, this.wrapTransaction);
60
58
  }
61
59
  nsT(e) {
62
- return C(this.namespace, e);
60
+ return T(this.namespace, e);
63
61
  }
64
62
  async attachNamespace(e, t) {
65
63
  let n = this.sql, r = t ?? `${e}.db`;
66
64
  await this.runtime.runPromise(n`ATTACH DATABASE ${r} AS ${n(e)}`);
67
65
  }
68
66
  async executeQuery(e, t, n) {
69
- let i = v(e, this.sql, this.namespace), a = t ? r.provideService(i, d, t) : i;
70
- return b(await this.runtime.runPromise(a), e.table, "sqlite");
67
+ let i = v(e, this.sql, this.namespace), a = t ? r.provideService(i, l, t) : i;
68
+ return y(await w(this.systemName, "select", () => this.runtime.runPromise(a)), e.table, "sqlite");
71
69
  }
72
70
  async executeInsert(e, t, n, i, a) {
73
- t = A(e, t);
74
- let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(U(t))} RETURNING *`, c = n ? r.provideService(s, d, n) : s, l = (await this.runtime.runPromise(c))[0];
75
- if (!l) throw Error(`SqliteStore.insert: no row returned for table '${e}'`);
71
+ t = j(e, t);
72
+ let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(W(t))} RETURNING *`, c = n ? r.provideService(s, l, n) : s, u = (await this.runtime.runPromise(c))[0];
73
+ if (!u) throw Error(`SqliteStore.insert: no row returned for table '${e}'`);
76
74
  return await this.routeEvent({
77
75
  table: e,
78
76
  op: "insert",
79
77
  old: null,
80
- new: l
81
- }, i, n, a), l;
78
+ new: u
79
+ }, i, n, a), u;
82
80
  }
83
81
  async executeUpdate(e, t, n, i, a, o) {
84
- let s = this.sql, c = s`UPDATE ${s(this.nsT(e))} SET ${s.update(U(n))} WHERE ${s("id")} = ${t} RETURNING *`, l = i ? r.provideService(c, d, i) : c, u = (await this.runtime.runPromise(l))[0];
85
- return u ? (await this.routeEvent({
82
+ let s = this.sql, c = s`UPDATE ${s(this.nsT(e))} SET ${s.update(W(n))} WHERE ${s("id")} = ${t} RETURNING *`, u = i ? r.provideService(c, l, i) : c, d = (await this.runtime.runPromise(u))[0];
83
+ return d ? (await this.routeEvent({
86
84
  table: e,
87
85
  op: "update",
88
86
  old: null,
89
- new: u
90
- }, a, i, o), u) : null;
87
+ new: d
88
+ }, a, i, o), d) : null;
91
89
  }
92
90
  async executeInsertMany(e, t, n, i, a) {
93
- if (t = j(e, t), t.length === 0) return [];
94
- let o = this.sql, s = t.map((e) => U(e)), c = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(s)} RETURNING *`, l = n ? r.provideService(c, d, n) : c, u = await this.runtime.runPromise(l);
91
+ if (t = M(e, t), t.length === 0) return [];
92
+ let o = this.sql, s = m(t.map((e) => W(e)), p("sqlite")), c = (t) => o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t)} RETURNING *`, u;
93
+ if (s.length === 1) {
94
+ let e = c(s[0]), t = n ? r.provideService(e, l, n) : e;
95
+ u = await this.runtime.runPromise(t);
96
+ } else if (n) {
97
+ let e = [];
98
+ for (let t of s) e.push(...await this.runtime.runPromise(r.provideService(c(t), l, n)));
99
+ u = e;
100
+ } else u = await this.runtime.runPromise(o.withTransaction(r.map(r.forEach(s, c, { concurrency: 1 }), (e) => e.flat())));
95
101
  for (let t of u) await this.routeEvent({
96
102
  table: e,
97
103
  op: "insert",
@@ -101,7 +107,7 @@ var N = (r) => e.layerConfig({
101
107
  return u;
102
108
  }
103
109
  async executePatchJson(e, t, n, i, a, o, s) {
104
- let c = this.sql, l = n.split("."), u = l[0], f = l.slice(1), p = JSON.stringify(i ?? null), m = f.length === 0 ? c`${c(u)} = json_patch(COALESCE(${c(u)}, '{}'), json(${p}))` : c`${c(u)} = json_set(COALESCE(${c(u)}, '{}'), ${`$.${f.join(".")}`}, json(${p}))`, h = c`UPDATE ${c(this.nsT(e))} SET ${m} WHERE ${c("id")} = ${t} RETURNING *`, g = a ? r.provideService(h, d, a) : h, _ = (await this.runtime.runPromise(g))[0];
110
+ let c = this.sql, u = n.split("."), d = u[0], f = u.slice(1), p = JSON.stringify(i ?? null), m = f.length === 0 ? c`${c(d)} = json_patch(COALESCE(${c(d)}, '{}'), json(${p}))` : c`${c(d)} = json_set(COALESCE(${c(d)}, '{}'), ${`$.${f.join(".")}`}, json(${p}))`, h = c`UPDATE ${c(this.nsT(e))} SET ${m} WHERE ${c("id")} = ${t} RETURNING *`, g = a ? r.provideService(h, l, a) : h, _ = (await this.runtime.runPromise(g))[0];
105
111
  return _ ? (await this.routeEvent({
106
112
  table: e,
107
113
  op: "update",
@@ -110,27 +116,27 @@ var N = (r) => e.layerConfig({
110
116
  }, o, a, s), _) : null;
111
117
  }
112
118
  async executeDelete(e, t, n, i, a) {
113
- let o = this.sql, s = o`DELETE FROM ${o(this.nsT(e))} WHERE ${o("id")} = ${t} RETURNING *`, c = n ? r.provideService(s, d, n) : s, l = (await this.runtime.runPromise(c))[0];
114
- return l ? (await this.routeEvent({
119
+ let o = this.sql, s = o`DELETE FROM ${o(this.nsT(e))} WHERE ${o("id")} = ${t} RETURNING *`, c = n ? r.provideService(s, l, n) : s, u = (await this.runtime.runPromise(c))[0];
120
+ return u ? (await this.routeEvent({
115
121
  table: e,
116
122
  op: "delete",
117
- old: l,
123
+ old: u,
118
124
  new: null
119
125
  }, i, n, a), !0) : !1;
120
126
  }
121
127
  async appendInTxn(e, t, n) {
122
- let i = this.sql, a = i`INSERT INTO ${i(this.nsT(e))} ${i.insert(U(t))}`;
123
- await this.runtime.runPromise(n ? r.provideService(a, d, n) : a);
128
+ let i = this.sql, a = i`INSERT INTO ${i(this.nsT(e))} ${i.insert(W(t))}`;
129
+ await this.runtime.runPromise(n ? r.provideService(a, l, n) : a);
124
130
  }
125
131
  async maxInTxn(e, t, n, i) {
126
- let a = this.sql, o = Object.entries(n).map(([e, t]) => a`${a(e)} = ${t}`), s = a`SELECT MAX(${a(t)}) AS ${a("m")} FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = (await this.runtime.runPromise(i ? r.provideService(s, d, i) : s))[0]?.m;
132
+ let a = this.sql, o = Object.entries(n).map(([e, t]) => a`${a(e)} = ${t}`), s = a`SELECT MAX(${a(t)}) AS ${a("m")} FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = (await this.runtime.runPromise(i ? r.provideService(s, l, i) : s))[0]?.m;
127
133
  return c == null ? null : Number(c);
128
134
  }
129
135
  async routeEvent(e, t, n = null, r) {
130
136
  e = {
131
- ...m(r),
137
+ ...f(r),
132
138
  ...e
133
- }, T(e.table) && await O({
139
+ }, D(e.table) && await A({
134
140
  append: (e, t) => this.appendInTxn(e, t, n),
135
141
  maxOf: (e, t, r) => this.maxInTxn(e, t, r, n)
136
142
  }, {
@@ -147,7 +153,7 @@ var N = (r) => e.layerConfig({
147
153
  let r = await this.findByConflict(e, t, n.conflictColumns, i);
148
154
  return r ? await this.executeUpdate(e, r.id, n.update(r), i, a) ?? r : this.executeInsert(e, t, i, a);
149
155
  }
150
- let s = this.sql, c = n.conflictColumns.map((e) => s`${s(e)}`), l = Object.keys(t).filter((e) => t[e] !== void 0), u = n.update === void 0 ? l.filter((e) => e !== "id" && !n.conflictColumns.includes(e)) : n.update, f = u.length > 0 ? s.csv(u.map((e) => s`${s(e)} = excluded.${s(e)}`)) : s`${s(n.conflictColumns[0])} = excluded.${s(n.conflictColumns[0])}`, p = s`INSERT INTO ${s(this.nsT(e))} ${s.insert(U(t))} ON CONFLICT (${s.csv(c)}) DO UPDATE SET ${f} RETURNING *`, m = i ? r.provideService(p, d, i) : p, h = (await this.runtime.runPromise(m))[0];
156
+ let s = this.sql, c = n.conflictColumns.map((e) => s`${s(e)}`), u = Object.keys(t).filter((e) => t[e] !== void 0), d = n.update === void 0 ? u.filter((e) => e !== "id" && !n.conflictColumns.includes(e)) : n.update, f = d.length > 0 ? s.csv(d.map((e) => s`${s(e)} = excluded.${s(e)}`)) : s`${s(n.conflictColumns[0])} = excluded.${s(n.conflictColumns[0])}`, p = s`INSERT INTO ${s(this.nsT(e))} ${s.insert(W(t))} ON CONFLICT (${s.csv(c)}) DO UPDATE SET ${f} RETURNING *`, m = i ? r.provideService(p, l, i) : p, h = (await this.runtime.runPromise(m))[0];
151
157
  if (!h) throw Error(`SqliteStore.upsert: no row returned for table '${e}'`);
152
158
  let g = t.id !== void 0 && t.id === h.id ? "insert" : "update";
153
159
  return await this.routeEvent({
@@ -158,8 +164,8 @@ var N = (r) => e.layerConfig({
158
164
  }, a, i, o), h;
159
165
  }
160
166
  async executeInsertIgnore(e, t, n, i, a, o) {
161
- t = A(e, t);
162
- let s = this.sql, c = n.conflictColumns.map((e) => s`${s(e)}`), l = s`INSERT INTO ${s(this.nsT(e))} ${s.insert(U(t))} ON CONFLICT (${s.csv(c)}) DO NOTHING RETURNING *`, u = i ? r.provideService(l, d, i) : l, f = (await this.runtime.runPromise(u))[0];
167
+ t = j(e, t);
168
+ let s = this.sql, c = n.conflictColumns.map((e) => s`${s(e)}`), u = s`INSERT INTO ${s(this.nsT(e))} ${s.insert(W(t))} ON CONFLICT (${s.csv(c)}) DO NOTHING RETURNING *`, d = i ? r.provideService(u, l, i) : u, f = (await this.runtime.runPromise(d))[0];
163
169
  if (f) return await this.routeEvent({
164
170
  table: e,
165
171
  op: "insert",
@@ -172,7 +178,7 @@ var N = (r) => e.layerConfig({
172
178
  }
173
179
  async findByConflict(e, t, n, i) {
174
180
  if (n.length === 0) return;
175
- let a = this.sql, o = n.map((e) => a`${a(e)} = ${t[e]}`), s = a`SELECT * FROM ${a(this.nsT(e))} WHERE ${a.and(o)} LIMIT 1`, c = i ? r.provideService(s, d, i) : s;
181
+ let a = this.sql, o = n.map((e) => a`${a(e)} = ${t[e]}`), s = a`SELECT * FROM ${a(this.nsT(e))} WHERE ${a.and(o)} LIMIT 1`, c = i ? r.provideService(s, l, i) : s;
176
182
  return (await this.runtime.runPromise(c))[0];
177
183
  }
178
184
  query(e) {
@@ -180,40 +186,53 @@ var N = (r) => e.layerConfig({
180
186
  }
181
187
  raw(e, t) {
182
188
  let n = _(e, this.sql);
183
- return this.runtime.runPromise(n);
189
+ return w(this.systemName, "raw", () => this.runtime.runPromise(n));
184
190
  }
185
191
  async runWithEager(e, t) {
186
192
  if (!x(e)) return this.executeQuery(e, t);
187
193
  let n = this.namespace === null ? h(e, this.sql, "sqlite") : null;
188
194
  if (n !== null) try {
189
- let e = t ? r.provideService(n.fragment, d, t) : n.fragment, i = await this.runtime.runPromise(e);
195
+ let e = t ? r.provideService(n.fragment, l, t) : n.fragment, i = await w(this.systemName, "select", () => this.runtime.runPromise(e));
190
196
  return n.decode(i);
191
- } catch (e) {
192
- if (e instanceof f) throw e;
193
- B.warn("sqlite JSON-agg eager-load failed; falling back to walker", { err: e });
197
+ } catch (t) {
198
+ if (t instanceof u) throw t;
199
+ this.reportEagerFallback({
200
+ dialect: this.systemName,
201
+ table: e.table,
202
+ reason: "execute-failed",
203
+ error: t
204
+ });
194
205
  }
195
- return p(await this.executeQuery(e, t), e.eager, e.sourceTable ?? E(e.table), (e) => this.executeQuery(e, t));
206
+ else this.reportEagerFallback({
207
+ dialect: this.systemName,
208
+ table: e.table,
209
+ reason: "not-compilable"
210
+ });
211
+ return d(await this.executeQuery(e, t), e.eager, e.sourceTable ?? O(e.table), (e) => this.executeQuery(e, t));
196
212
  }
197
213
  getInternalRunWithEager() {
198
214
  return (e, t) => this.runWithEager(e, t);
199
215
  }
216
+ localWrite(e, t) {
217
+ return w(this.systemName, e, () => N(t));
218
+ }
200
219
  insert(e, t) {
201
- return M((n) => this.executeInsert(e, t, null, null, n));
220
+ return this.localWrite("insert", (n) => this.executeInsert(e, t, null, null, n));
202
221
  }
203
222
  insertMany(e, t) {
204
- return M((n) => this.executeInsertMany(e, t, null, null, n));
223
+ return this.localWrite("insert", (n) => this.executeInsertMany(e, t, null, null, n));
205
224
  }
206
225
  patchJson(e, t, n, r) {
207
- return M((i) => this.executePatchJson(e, t, n, r, null, null, i));
226
+ return this.localWrite("update", (i) => this.executePatchJson(e, t, n, r, null, null, i));
208
227
  }
209
228
  update(e, t, n) {
210
- return M((r) => this.executeUpdate(e, t, n, null, null, r));
229
+ return this.localWrite("update", (r) => this.executeUpdate(e, t, n, null, null, r));
211
230
  }
212
231
  delete(e, t) {
213
- return M((n) => this.executeDelete(e, t, null, null, n));
232
+ return this.localWrite("delete", (n) => this.executeDelete(e, t, null, null, n));
214
233
  }
215
234
  async updateMany(e, t, n) {
216
- let r = this.sql, i = g(n.where, r, this.namespace), a = r`UPDATE ${r(this.nsT(e))} SET ${r.update(U(t))} WHERE ${i} RETURNING *`, o = await this.runtime.runPromise(a);
235
+ let r = this.sql, i = g(n.where, r, this.namespace), a = r`UPDATE ${r(this.nsT(e))} SET ${r.update(W(t))} WHERE ${i} RETURNING *`, o = await w(this.systemName, "update", () => this.runtime.runPromise(a));
217
236
  for (let t of o) await this.routeEvent({
218
237
  table: e,
219
238
  op: "update",
@@ -223,7 +242,7 @@ var N = (r) => e.layerConfig({
223
242
  return o.length;
224
243
  }
225
244
  async deleteMany(e, t) {
226
- let n = this.sql, r = g(t.where, n, this.namespace), i = n`DELETE FROM ${n(this.nsT(e))} WHERE ${r} RETURNING *`, a = await this.runtime.runPromise(i);
245
+ let n = this.sql, r = g(t.where, n, this.namespace), i = n`DELETE FROM ${n(this.nsT(e))} WHERE ${r} RETURNING *`, a = await w(this.systemName, "delete", () => this.runtime.runPromise(i));
227
246
  for (let t of a) await this.routeEvent({
228
247
  table: e,
229
248
  op: "delete",
@@ -233,10 +252,10 @@ var N = (r) => e.layerConfig({
233
252
  return a.length;
234
253
  }
235
254
  upsert(e, t, n) {
236
- return M((r) => this.executeUpsert(e, t, n, null, null, r));
255
+ return this.localWrite("upsert", (r) => this.executeUpsert(e, t, n, null, null, r));
237
256
  }
238
257
  insertIgnore(e, t, n) {
239
- return M((r) => this.executeInsertIgnore(e, t, n, null, null, r));
258
+ return this.localWrite("upsert", (r) => this.executeInsertIgnore(e, t, n, null, null, r));
240
259
  }
241
260
  emitChange(e) {
242
261
  this.emitter.emit("change", e);
@@ -267,41 +286,40 @@ var N = (r) => e.layerConfig({
267
286
  }
268
287
  async transactional(e) {
269
288
  this.inflightTxns++;
270
- let t = y(), n = W(), i = 0, a = r.suspend(() => {
271
- let a = ++i;
272
- return this.sql.withTransaction(r.flatMap(r.serviceOption(d), (i) => {
273
- if (o.isNone(i)) return r.fail(/* @__PURE__ */ Error("SqliteStore.transactional: TransactionConnection unexpectedly missing."));
274
- let s = new K(this, i.value, t);
275
- return r.tryPromise({
276
- try: () => n(() => e(s)).then((e) => ({
277
- result: e,
278
- view: s,
279
- attempt: a
280
- })),
281
- catch: (e) => e
282
- });
283
- })).pipe(r.catchAllDefect((e) => this.isRetryable(e) ? r.fail(e) : r.die(e)));
284
- }), c = s.exponential("10 millis").pipe(s.compose(s.recurs(3)), s.whileInput(this.isRetryable)), l = a.pipe(r.retry(c), r.withSpan("store.transactional", { attributes: {
285
- "db.system": this.systemName,
286
- "db.operation": "transaction"
287
- } }));
288
289
  try {
289
- let e = k(await this.runtime.runPromiseExit(this.wrapTransaction(l)));
290
- return e.view.commitEvents(), e.result;
290
+ return await k({
291
+ ...this.txnSpec("SqliteStore.transactional"),
292
+ work: e,
293
+ makeView: (e, t) => new K(this, e, t)
294
+ });
291
295
  } finally {
292
296
  this.inflightTxns--;
293
297
  }
294
298
  }
299
+ txnSpec(e) {
300
+ return {
301
+ label: e,
302
+ dialect: this.systemName === "turso" ? "turso" : "sqlite",
303
+ withTransaction: (e) => this.sql.withTransaction(e),
304
+ runPromiseExit: (e) => this.runtime.runPromiseExit(e),
305
+ isRetryable: this.isRetryable,
306
+ wrapProgram: (e) => this.wrapTransaction(e),
307
+ span: {
308
+ name: "store.transactional",
309
+ attributes: {
310
+ "db.system": this.systemName,
311
+ "db.operation": "transaction"
312
+ }
313
+ }
314
+ };
315
+ }
295
316
  onChange(e) {
296
317
  return this.emitter.on("change", e), () => {
297
318
  this.emitter.off("change", e);
298
319
  };
299
320
  }
300
321
  injectExternalChange(e) {
301
- S(e.table) && this.emitter.emit("change", {
302
- ...e,
303
- origin: "injected"
304
- });
322
+ S(e.table) && this.emitter.emit("change", b(e));
305
323
  }
306
324
  run(e) {
307
325
  return this.runtime.runPromise(e);
@@ -310,7 +328,7 @@ var N = (r) => e.layerConfig({
310
328
  if (this.inflightTxns > 0) {
311
329
  let t = Date.now() + e;
312
330
  for (; this.inflightTxns > 0 && Date.now() < t;) await new Promise((e) => setTimeout(e, 25));
313
- this.inflightTxns > 0 && B.warn("close: grace period expired with in-flight transactions", {
331
+ this.inflightTxns > 0 && V.warn("close: grace period expired with in-flight transactions", {
314
332
  gracePeriodMs: e,
315
333
  inflight: this.inflightTxns
316
334
  });
@@ -395,10 +413,10 @@ var N = (r) => e.layerConfig({
395
413
  }
396
414
  }, q = {
397
415
  id: "sqlite",
398
- makeSqlLayer: (e) => F(e),
399
- makeStore: (e) => V(e),
416
+ makeSqlLayer: (e) => I(e),
417
+ makeStore: (e) => H(e),
400
418
  compileContains: (e, t, n) => n`${e} LIKE ${`%${t.replace(/[\\%_]/g, (e) => `\\${e}`)}%`} ESCAPE '\\'`,
401
- retryFilter: z
419
+ retryFilter: B
402
420
  };
403
421
  //#endregion
404
- export { e as SqliteClient, P as connectionFromConfig, N as makeSqliteSqlLayer, F as makeSqliteSqlLayerFromConfig, V as makeSqliteStore, q as sqliteDialect, z as sqliteRetryFilter };
422
+ export { e as SqliteClient, F as connectionFromConfig, P as makeSqliteSqlLayer, I as makeSqliteSqlLayerFromConfig, H as makeSqliteStore, q as sqliteDialect, B as sqliteRetryFilter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/sql-sqlite",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "description": "SQLite dialect adapter for Voltro's cross-dialect DataStore (single-process; in-process change events).",
5
5
  "keywords": [
6
6
  "voltro",
@@ -35,8 +35,8 @@
35
35
  "dependencies": {
36
36
  "@effect/sql": "^0.52.0",
37
37
  "@effect/sql-sqlite-node": "^0.53.0",
38
- "@voltro/database": "0.33.0",
39
- "@voltro/logger": "0.33.0"
38
+ "@voltro/database": "0.35.0",
39
+ "@voltro/logger": "0.35.0"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "effect": "^3.22.0"