@voltro/sql-sqlite 0.84.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.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ChangeEvent } from '@voltro/database';
2
2
  import { ChangeStrategy } from '@voltro/database';
3
- import { ConfigError } from 'effect';
4
- import { ConfigError as ConfigError_2 } from 'effect/ConfigError';
3
+ import { Config } from 'effect';
5
4
  import { ConnectionConfig } from '@voltro/database';
6
5
  import { Context } from 'effect';
7
6
  import { DataStore } from '@voltro/database';
@@ -17,13 +16,13 @@ import { RawSqlFragment } from '@voltro/database/sql';
17
16
  import { RetryDecision } from '@voltro/database';
18
17
  import { Row } from '@voltro/database';
19
18
  import { RowKey } from '@voltro/database';
20
- import { SqlClient } from '@effect/sql';
21
- import { SqlClient as SqlClient_2 } from '@effect/sql/SqlClient';
19
+ import { SqlClient } from 'effect/sql';
20
+ import { SqlClient as SqlClient_2 } from 'effect/sql/SqlClient';
22
21
  import { SqlDialect } from '@voltro/database';
23
- import { SqlError } from '@effect/sql';
24
- import { SqlError as SqlError_2 } from '@effect/sql/SqlError';
22
+ import { SqlError } from 'effect/sql';
23
+ import { SqlError as SqlError_2 } from 'effect/sql/SqlError';
25
24
  import { SqliteClient } from '@effect/sql-sqlite-node';
26
- import { TransactionConnection } from '@effect/sql/SqlClient';
25
+ import { TransactionConnection } from 'effect/sql/SqlClient';
27
26
  import { UpsertOptions } from '@voltro/database';
28
27
  import { WriteAttribution } from '@voltro/database';
29
28
 
@@ -62,7 +61,7 @@ export declare const sqliteRetryFilter: (err: unknown) => RetryDecision;
62
61
  * to the error channel — matching the cross-dialect `sqlLayer` shape in
63
62
  * `@voltro/database`).
64
63
  */
65
- declare type SqliteSqlLayer = Layer.Layer<SqliteClient.SqliteClient | SqlClient_2, ConfigError_2 | SqlError_2>;
64
+ declare type SqliteSqlLayer = Layer.Layer<SqliteClient.SqliteClient | SqlClient_2, Config.ConfigError | SqlError_2>;
66
65
 
67
66
  export declare class SqliteStore implements DataStore {
68
67
  private readonly sql;
@@ -199,7 +198,7 @@ export declare class SqliteStore implements DataStore {
199
198
  * Retry uses the injected `isRetryable` predicate — sqlite's
200
199
  * `SQLITE_BUSY` / `SQLITE_LOCKED`, or (Turso dialect) the MVCC
201
200
  * `"Write-write conflict"` from `BEGIN CONCURRENT`. A conflict can
202
- * surface from the COMMIT, which `@effect/sql` runs as `Effect.orDie`
201
+ * surface from the COMMIT, which `effect/sql` runs as `Effect.orDie`
203
202
  * → it arrives as a DEFECT, not a typed failure, and `Effect.retry`
204
203
  * does not retry defects. So we promote a retryable defect back to a
205
204
  * typed failure before the schedule sees it (and re-die on a genuine
@@ -243,6 +242,9 @@ export declare class SqliteStore implements DataStore {
243
242
  * defect-promotion in particular lived HERE and only here for a release, which
244
243
  * is why it is no longer a per-store decision. See `transactionOutcome.ts`.
245
244
  */
245
+ /** The key under which this client's transactions provide their pinned
246
+ * connection (per client in Effect 4). Views reach it through the parent. */
247
+ get transactionService(): Context.Service<TransactionConnection, TransactionConnection.Service>;
246
248
  private txnSpec;
247
249
  onChange(listener: (event: ChangeEvent) => void): () => void;
248
250
  /** Cross-instance reactivity seam — emit an externally-sourced event to
@@ -257,7 +259,7 @@ export declare class SqliteStore implements DataStore {
257
259
  }
258
260
 
259
261
  export declare interface SqliteStoreOptions {
260
- readonly sqlLayer: Layer.Layer<SqlClient.SqlClient, ConfigError.ConfigError | SqlError.SqlError, never>;
262
+ readonly sqlLayer: Layer.Layer<SqlClient.SqlClient, Config.ConfigError | SqlError.SqlError, never>;
261
263
  readonly tracerLayer?: Layer.Layer<never, never, never>;
262
264
  /** Accepted for cross-dialect symmetry; ignored (always in-process). */
263
265
  readonly changeStrategy?: ChangeStrategy;
@@ -285,6 +287,6 @@ export declare interface SqliteStoreOptions {
285
287
  readonly fullText?: 'fts5' | 'like';
286
288
  }
287
289
 
288
- declare type TxnContext = Context.Tag.Service<typeof TransactionConnection>;
290
+ declare type TxnContext = TransactionConnection.Service;
289
291
 
290
292
  export { }
package/dist/index.js CHANGED
@@ -1,509 +1,13 @@
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 } from "effect";
3
- import { EagerCardinalityError as s, SQLITE_TRANSIENT_CODES as c, attachEagerLoads as l, attributionFields as u, bulkInsertLimitsFor as d, causeBranches as f, chunkRowsForInsert as p, coercePredicateForSqlite as m, compileEagerJson as h, compileJsonPatch as g, compilePredicate as _, compileRawFragment as v, compileRowProjection as y, compileSelect as b, decodeRowsFromSchema as x, encodeRowForSchema as S, externalChangeEvent as C, hasEagerLoads as ee, isTableReactive as w, jsonPatchAssignments as te, makeEagerFallbackReporter as T, makeTransactionAbortTracker as E, observeDbOp as D, qualifyTable as O, quoteIdent as k, raiseChangeListenerCeiling as A, recordsTable as j, requireTable as M, rowKeyColumns as N, runSavepoint as ne, runStoreTransaction as re, runWriteRecorders as ie, stampGeneratedId as P, stampGeneratedIds as F, upsertUpdatePatch as I, upsertUpdateValues as L, withCapturedAttribution as R } from "@voltro/database";
4
- import { EventEmitter as z } from "node:events";
5
- import { createLogger as B } from "@voltro/logger";
6
- import { SqlClient as V, TransactionConnection as H } from "@effect/sql/SqlClient";
7
- //#region src/sqlLayer.ts
8
- var U = (r) => e.layerConfig({
9
- filename: t.succeed(r.filename),
10
- ...r.readonly === void 0 ? {} : { readonly: t.succeed(r.readonly) }
11
- }).pipe(i.tap((t) => n.get(t, e.SqliteClient)`PRAGMA foreign_keys = ON`)), W = (e) => {
12
- if (e.url) {
13
- if (e.url === ":memory:") return { filename: ":memory:" };
14
- if (e.url.startsWith("file:")) return { filename: e.url.slice(5) };
15
- throw Error(`@voltro/sql-sqlite: unsupported url '${e.url}'. Accepted: 'file:./path.sqlite', 'file:/abs/path.sqlite', ':memory:'.`);
16
- }
17
- if (e.database) return { filename: e.database };
18
- throw Error("@voltro/sql-sqlite: no filename supplied. Set DB_URL=file:./db.sqlite or DB_DATABASE=path/to/db.sqlite.");
19
- }, G = (e) => U(W(e)), K = c, q = (e) => {
20
- let t = e;
21
- for (let e = 0; e < 5 && typeof t == "object" && t; e++) {
22
- let e = t.code;
23
- if (typeof e == "string") return e;
24
- if (typeof e == "number") return String(e);
25
- t = t.cause;
26
- }
27
- }, J = (e) => f(e).some((e) => {
28
- let t = q(e);
29
- return t !== void 0 && K.has(t);
30
- }), Y = (e) => J(e) ? "retry" : "noRetry", X = B({ scope: "voltro:sqlite" }), Z = async (e) => {
31
- let t = e.tracerLayer ? i.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, n = a.make(t);
32
- return new se(await n.runPromise(V), n, null, void 0, e.isRetryable ?? J, e.systemName ?? "sqlite", e.wrapTransaction, e.fullText ?? "fts5");
33
- }, ae = (e) => e, Q = (e) => m(e), oe = (e) => e.predicate === void 0 || e.predicate === null ? e : {
34
- ...e,
35
- predicate: Q(e.predicate)
36
- }, $ = (e, t) => {
37
- let n = S(e, t, [
38
- "json",
39
- "array",
40
- "decimal"
41
- ]), r = {};
42
- for (let [e, t] of Object.entries(n)) r[e] = t instanceof Date ? t.toISOString() : typeof t == "boolean" ? +!!t : t;
43
- return r;
44
- }, se = class e {
45
- sql;
46
- runtime;
47
- namespace;
48
- isRetryable;
49
- systemName;
50
- wrapTransaction;
51
- fullText;
52
- emitter;
53
- inflightTxns = 0;
54
- reportEagerFallback;
55
- constructor(e, t, n = null, r, i = J, a = "sqlite", o = ae, s = "fts5") {
56
- this.sql = e, this.runtime = t, this.namespace = n, this.isRetryable = i, this.systemName = a, this.wrapTransaction = o, this.fullText = s, this.emitter = r ?? new z(), A(this.emitter), this.reportEagerFallback = T(X);
57
- }
58
- withNamespace(t) {
59
- return t === this.namespace ? this : new e(this.sql, this.runtime, t, this.emitter, this.isRetryable, this.systemName, this.wrapTransaction, this.fullText);
60
- }
61
- withFullTextMode(e) {
62
- return this.fullText === "like" && e.fullTextSearch !== void 0 ? {
63
- ...e,
64
- fullTextSearch: {
65
- ...e.fullTextSearch,
66
- fallback: "like"
67
- }
68
- } : e;
69
- }
70
- nsT(e) {
71
- return O(this.namespace, e);
72
- }
73
- async attachNamespace(e, t) {
74
- let n = this.sql, r = t ?? `${e}.db`;
75
- await this.runtime.runPromise(n`ATTACH DATABASE ${r} AS ${n(e)}`);
76
- }
77
- async executeQuery(e, t, n) {
78
- let i = b(oe(this.withFullTextMode(e)), this.sql, this.namespace), a = t ? r.provideService(i, H, t) : i, o = await D(this.systemName, "select", () => this.runtime.runPromise(a));
79
- return x(o, e.table, "sqlite", e);
80
- }
81
- keyWhere(e, t) {
82
- let n = this.sql, r = $(Object.fromEntries(N(t)), e);
83
- return n.and(Object.entries(r).map(([e, t]) => n`${n(e)} = ${t}`));
84
- }
85
- async executeInsert(e, t, n, i, a) {
86
- t = P(e, t);
87
- let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert($(t, e))} RETURNING ${y(e, o)}`, c = n ? r.provideService(s, H, n) : s, l = await this.runtime.runPromise(c), u = x(l, e, "sqlite")[0];
88
- if (!u) throw Error(`SqliteStore.insert: no row returned for table '${e}'`);
89
- return await this.routeEvent({
90
- table: e,
91
- op: "insert",
92
- old: null,
93
- new: u
94
- }, i, n, a), u;
95
- }
96
- async executeUpdate(e, t, n, i, a, o) {
97
- let s = this.sql, c = s`UPDATE ${s(this.nsT(e))} SET ${s.update($(n, e))} WHERE ${this.keyWhere(e, t)} RETURNING ${y(e, s)}`, l = i ? r.provideService(c, H, i) : c, u = await this.runtime.runPromise(l), d = x(u, e, "sqlite")[0];
98
- return d ? (await this.routeEvent({
99
- table: e,
100
- op: "update",
101
- old: null,
102
- new: d
103
- }, a, i, o), d) : null;
104
- }
105
- async executeInsertMany(e, t, n, i, a) {
106
- if (t = F(e, t), t.length === 0) return [];
107
- let o = this.sql, s = t.map((t) => $(t, e)), c = p(s, d("sqlite")), l = (t) => o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t)} RETURNING ${y(e, o)}`, u;
108
- if (c.length === 1) {
109
- let e = l(c[0]), t = n ? r.provideService(e, H, n) : e;
110
- u = await this.runtime.runPromise(t);
111
- } else if (n) {
112
- let e = [];
113
- for (let t of c) e.push(...await this.runtime.runPromise(r.provideService(l(t), H, n)));
114
- u = e;
115
- } else u = await this.runtime.runPromise(o.withTransaction(r.map(r.forEach(c, l, { concurrency: 1 }), (e) => e.flat())));
116
- let f = x(u, e, "sqlite");
117
- for (let t of f) await this.routeEvent({
118
- table: e,
119
- op: "insert",
120
- old: null,
121
- new: t
122
- }, i, n, a);
123
- return f;
124
- }
125
- async executePatchJson(e, t, n, i, a, o, s, c) {
126
- let l = this.sql, u = $(te(t, n, c), e), d = l.csv([g(l, "sqlite", n, i, l`${l(this.nsT(e))}.${l(n.split(".")[0])}`), ...Object.entries(u).map(([e, t]) => l`${l(e)} = ${t}`)]), f = l`UPDATE ${l(this.nsT(e))} SET ${d} WHERE ${this.keyWhere(e, t)} RETURNING ${y(e, l)}`, p = a ? r.provideService(f, H, a) : f, m = await this.runtime.runPromise(p), h = x(m, e, "sqlite")[0];
127
- return h ? (await this.routeEvent({
128
- table: e,
129
- op: "update",
130
- old: null,
131
- new: h
132
- }, o, a, s), h) : null;
133
- }
134
- async executeDelete(e, t, n, i, a) {
135
- let o = this.sql, s = o`DELETE FROM ${o(this.nsT(e))} WHERE ${this.keyWhere(e, t)} RETURNING ${y(e, o)}`, c = n ? r.provideService(s, H, n) : s, l = await this.runtime.runPromise(c), u = x(l, e, "sqlite")[0];
136
- return u ? (await this.routeEvent({
137
- table: e,
138
- op: "delete",
139
- old: u,
140
- new: null
141
- }, i, n, a), !0) : !1;
142
- }
143
- async appendInTxn(e, t, n) {
144
- let i = this.sql, a = i`INSERT INTO ${i(this.nsT(e))} ${i.insert($(t, e))}`;
145
- await this.runtime.runPromise(n ? r.provideService(a, H, n) : a);
146
- }
147
- async maxInTxn(e, t, n, i) {
148
- 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, H, i) : s))[0]?.m;
149
- return c == null ? null : Number(c);
150
- }
151
- async routeEvent(e, t, n = null, r) {
152
- e = {
153
- ...u(r),
154
- ...e
155
- }, j(e.table) && await ie({
156
- append: (e, t) => this.appendInTxn(e, t, n),
157
- maxOf: (e, t, r) => this.maxInTxn(e, t, r, n)
158
- }, {
159
- table: e.table,
160
- op: e.op,
161
- next: e.new,
162
- prev: e.old,
163
- traceId: e.traceId,
164
- subjectId: e.subjectId
165
- }), w(e.table) && (t === null ? this.emitter.emit("change", e) : t.push(e));
166
- }
167
- async executeUpsert(e, t, n, i, a, o) {
168
- let s = L(n);
169
- if (t = P(e, t), typeof n.update == "function") {
170
- let r = await this.findByConflict(e, t, n.conflictColumns, i);
171
- if (r) {
172
- let s = Object.fromEntries(n.conflictColumns.map((e) => [e, r[e]]));
173
- return await this.executeUpdate(e, s, I(t, r, n), i, a, o) ?? r;
174
- }
175
- return this.executeInsert(e, t, i, a, o);
176
- }
177
- let c = this.sql, l = n.conflictColumns.map((e) => c`${c(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 = $(s, e), p = [...d.filter((e) => !Object.hasOwn(s, e)).map((e) => c`${c(e)} = excluded.${c(e)}`), ...Object.entries(f).map(([e, t]) => c`${c(e)} = ${t}`)], m = p.length > 0 ? c.csv(p) : c`${c(n.conflictColumns[0])} = excluded.${c(n.conflictColumns[0])}`, h = c`INSERT INTO ${c(this.nsT(e))} ${c.insert($(t, e))} ON CONFLICT (${c.csv(l)}) DO UPDATE SET ${m} RETURNING ${y(e, c)}`, g = i ? r.provideService(h, H, i) : h, _ = await this.runtime.runPromise(g), v = x(_, e, "sqlite")[0];
178
- if (!v) throw Error(`SqliteStore.upsert: no row returned for table '${e}'`);
179
- let b = t.id !== void 0 && t.id === v.id ? "insert" : "update";
180
- return await this.routeEvent({
181
- table: e,
182
- op: b,
183
- old: null,
184
- new: v
185
- }, a, i, o), v;
186
- }
187
- async executeInsertIgnore(e, t, n, i, a, o) {
188
- t = P(e, t);
189
- let s = this.sql, c = n.conflictColumns.map((e) => s`${s(e)}`), l = s`INSERT INTO ${s(this.nsT(e))} ${s.insert($(t, e))} ON CONFLICT (${s.csv(c)}) DO NOTHING RETURNING ${y(e, s)}`, u = i ? r.provideService(l, H, i) : l, d = await this.runtime.runPromise(u), f = x(d, e, "sqlite")[0];
190
- if (f) return await this.routeEvent({
191
- table: e,
192
- op: "insert",
193
- old: null,
194
- new: f
195
- }, a, i, o), {
196
- row: f,
197
- outcome: "inserted"
198
- };
199
- let p = await this.findByConflict(e, t, n.conflictColumns, i);
200
- if (!p) throw Error(`SqliteStore.insertIgnore: conflict fired but no existing row found in '${e}'`);
201
- return {
202
- row: p,
203
- outcome: "ignored"
204
- };
205
- }
206
- async findByConflict(e, t, n, i) {
207
- if (n.length === 0) return;
208
- let a = this.sql, o = $(Object.fromEntries(n.map((e) => [e, t[e]])), e), s = n.map((e) => a`${a(e)} = ${o[e]}`), c = a`SELECT ${y(e, a)} FROM ${a(this.nsT(e))} WHERE ${a.and(s)} LIMIT 1`, l = i ? r.provideService(c, H, i) : c, u = await this.runtime.runPromise(l);
209
- return x(u, e, "sqlite")[0];
210
- }
211
- query(e) {
212
- return this.runWithEager(e, null);
213
- }
214
- raw(e, t) {
215
- return this.rawOn(e, null);
216
- }
217
- rawOn(e, t) {
218
- let n = v(e, this.sql), i = t ? r.provideService(n, H, t) : n;
219
- return D(this.systemName, "raw", () => this.runtime.runPromise(i));
220
- }
221
- async runWithEager(e, t) {
222
- if (!ee(e)) return this.executeQuery(e, t);
223
- let n = this.namespace === null ? h(e, this.sql, "sqlite") : null;
224
- if (n !== null) try {
225
- let e = t ? r.provideService(n.fragment, H, t) : n.fragment, i = await D(this.systemName, "select", () => this.runtime.runPromise(e));
226
- return n.decode(i);
227
- } catch (t) {
228
- if (t instanceof s) throw t;
229
- this.reportEagerFallback({
230
- dialect: this.systemName,
231
- table: e.table,
232
- reason: "execute-failed",
233
- error: t
234
- });
235
- }
236
- else this.reportEagerFallback({
237
- dialect: this.systemName,
238
- table: e.table,
239
- reason: "not-compilable"
240
- });
241
- let i = await this.executeQuery(e, t);
242
- return l(i, e.eager, e.sourceTable ?? M(e.table), (e) => this.executeQuery(e, t));
243
- }
244
- getInternalRunWithEager() {
245
- return (e, t) => this.runWithEager(e, t);
246
- }
247
- localWrite(e, t, n) {
248
- let r = (e) => j(t) ? this.writeWithRecorders(n, e) : n(e, null, null);
249
- return D(this.systemName, e, () => R(r));
250
- }
251
- async writeWithRecorders(e, t) {
252
- let n = [], i = this.sql, a = await this.runtime.runPromise(i.withTransaction(r.flatMap(r.serviceOption(H), (i) => o.isNone(i) ? r.fail(/* @__PURE__ */ Error("SqliteStore.write: TransactionConnection missing.")) : r.tryPromise({
253
- try: () => e(t, i.value, n),
254
- catch: (e) => e
255
- }))));
256
- for (let e of n) this.emitChange(e);
257
- return a;
258
- }
259
- insert(e, t) {
260
- return this.localWrite("insert", e, (n, r, i) => this.executeInsert(e, t, r, i, n));
261
- }
262
- insertMany(e, t) {
263
- return this.localWrite("insert", e, (n, r, i) => this.executeInsertMany(e, t, r, i, n));
264
- }
265
- patchJson(e, t, n, r, i) {
266
- return this.localWrite("update", e, (a, o, s) => this.executePatchJson(e, t, n, r, o, s, a, i));
267
- }
268
- update(e, t, n) {
269
- return this.localWrite("update", e, (r, i, a) => this.executeUpdate(e, t, n, i, a, r));
270
- }
271
- delete(e, t) {
272
- return this.localWrite("delete", e, (n, r, i) => this.executeDelete(e, t, r, i, n));
273
- }
274
- updateMany(e, t, n) {
275
- return this.executeUpdateMany(e, t, n, null, null);
276
- }
277
- deleteMany(e, t) {
278
- return this.executeDeleteMany(e, t, null, null);
279
- }
280
- async executeUpdateMany(e, t, n, i, a, o) {
281
- let s = this.sql, c = _(Q(n.where), s, this.namespace, e), l = s`UPDATE ${s(this.nsT(e))} SET ${s.update($(t, e))} WHERE ${c} RETURNING ${y(e, s)}`, u = i ? r.provideService(l, H, i) : l, d = await D(this.systemName, "update", () => this.runtime.runPromise(u));
282
- for (let t of x(d, e, "sqlite")) await this.routeEvent({
283
- table: e,
284
- op: "update",
285
- old: null,
286
- new: t
287
- }, a, i, o);
288
- return d.length;
289
- }
290
- async executeDeleteMany(e, t, n, i, a) {
291
- let o = this.sql, s = _(Q(t.where), o, this.namespace, e), c = o`DELETE FROM ${o(this.nsT(e))} WHERE ${s} RETURNING ${y(e, o)}`, l = n ? r.provideService(c, H, n) : c, u = await D(this.systemName, "delete", () => this.runtime.runPromise(l));
292
- for (let t of x(u, e, "sqlite")) await this.routeEvent({
293
- table: e,
294
- op: "delete",
295
- old: t,
296
- new: null
297
- }, i, n, a);
298
- return u.length;
299
- }
300
- upsert(e, t, n) {
301
- return this.localWrite("upsert", e, (r, i, a) => this.executeUpsert(e, t, n, i, a, r));
302
- }
303
- insertIgnore(e, t, n) {
304
- return this.insertIgnoreWithOutcome(e, t, n).then((e) => e.row);
305
- }
306
- insertIgnoreWithOutcome(e, t, n) {
307
- return this.localWrite("upsert", e, (r, i, a) => this.executeInsertIgnore(e, t, n, i, a, r));
308
- }
309
- emitChange(e) {
310
- this.emitter.emit("change", e);
311
- }
312
- getInternalExecuteQuery() {
313
- return this.executeQuery.bind(this);
314
- }
315
- getInternalExecuteInsert() {
316
- return this.executeInsert.bind(this);
317
- }
318
- getInternalExecuteInsertMany() {
319
- return this.executeInsertMany.bind(this);
320
- }
321
- getInternalExecutePatchJson() {
322
- return this.executePatchJson.bind(this);
323
- }
324
- getInternalExecuteUpdate() {
325
- return this.executeUpdate.bind(this);
326
- }
327
- getInternalExecuteUpdateMany() {
328
- return this.executeUpdateMany.bind(this);
329
- }
330
- getInternalExecuteDeleteMany() {
331
- return this.executeDeleteMany.bind(this);
332
- }
333
- getInternalExecuteDelete() {
334
- return this.executeDelete.bind(this);
335
- }
336
- getInternalExecuteUpsert() {
337
- return this.executeUpsert.bind(this);
338
- }
339
- getInternalExecuteInsertIgnore() {
340
- return this.executeInsertIgnore.bind(this);
341
- }
342
- async emptyTables(e) {
343
- await this.emptyTablesOn(e, null);
344
- }
345
- async incomingForeignKeys(e) {
346
- if (e.length === 0) return [];
347
- let t = this.sql, n = new Set(e), r = await this.runtime.runPromise(t`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'`), i = [];
348
- for (let e of r) {
349
- if (n.has(e.name)) continue;
350
- let r = await this.runtime.runPromise(t`${t.unsafe(`PRAGMA foreign_key_list(${k(e.name, "sqlite")})`)}`).catch(() => []);
351
- for (let t of r) n.has(t.table) && i.push({
352
- from: e.name,
353
- column: t.from,
354
- to: t.table
355
- });
356
- }
357
- return i;
358
- }
359
- async emptyTablesOn(e, t) {
360
- if (e.length === 0) return;
361
- let n = this.sql, i = (e) => this.nsT(e), a = r.acquireUseRelease(n.unsafe("PRAGMA defer_foreign_keys = ON"), () => r.forEach(e, (e) => n`DELETE FROM ${n(i(e))}`, { discard: !0 }), () => r.orDie(r.ignore(n.unsafe("PRAGMA defer_foreign_keys = OFF")))), o = t === null ? n.withTransaction(a) : r.provideService(a, H, t);
362
- await this.runtime.runPromise(o);
363
- }
364
- transactionEndingFailure(e) {
365
- return this.isRetryable(e);
366
- }
367
- async transactional(e) {
368
- this.inflightTxns++;
369
- try {
370
- return await re({
371
- ...this.txnSpec("SqliteStore.transactional"),
372
- work: e,
373
- makeView: (e, t) => new ce(this, e, t)
374
- });
375
- } finally {
376
- this.inflightTxns--;
377
- }
378
- }
379
- txnSpec(e) {
380
- return {
381
- label: e,
382
- dialect: this.systemName === "turso" ? "turso" : "sqlite",
383
- withTransaction: (e) => this.sql.withTransaction(e),
384
- runPromiseExit: (e) => this.runtime.runPromiseExit(e),
385
- isRetryable: this.isRetryable,
386
- wrapProgram: (e) => this.wrapTransaction(e),
387
- span: {
388
- name: "store.transactional",
389
- attributes: {
390
- "db.system": this.systemName,
391
- "db.operation": "transaction"
392
- }
393
- }
394
- };
395
- }
396
- onChange(e) {
397
- return this.emitter.on("change", e), () => {
398
- this.emitter.off("change", e);
399
- };
400
- }
401
- injectExternalChange(e) {
402
- w(e.table) && this.emitter.emit("change", C(e));
403
- }
404
- run(e) {
405
- return this.runtime.runPromise(e);
406
- }
407
- async close(e = 5e3) {
408
- if (this.inflightTxns > 0) {
409
- let t = Date.now() + e;
410
- for (; this.inflightTxns > 0 && Date.now() < t;) await new Promise((e) => setTimeout(e, 25));
411
- this.inflightTxns > 0 && X.warn("close: grace period expired with in-flight transactions", {
412
- gracePeriodMs: e,
413
- inflight: this.inflightTxns
414
- });
415
- }
416
- await this.runtime.dispose();
417
- }
418
- async ping() {
419
- await this.runtime.runPromise(this.sql`SELECT 1`);
420
- }
421
- }, ce = class {
422
- parent;
423
- txn;
424
- attr;
425
- events = [];
426
- aborts = E((e) => this.parent.transactionEndingFailure(e));
427
- committed = !1;
428
- constructor(e, t, n = void 0) {
429
- this.parent = e, this.txn = t, this.attr = n;
430
- }
431
- savepoint(e) {
432
- return ne({
433
- tracker: this.aborts,
434
- name: `voltro_sp_${++this.savepointCounter}`,
435
- spelling: "standard",
436
- exec: (e) => this.parent.run(r.provideService(V.pipe(r.flatMap((t) => t.unsafe(e))), H, this.txn))
437
- }, e);
438
- }
439
- savepointCounter = 0;
440
- run(e) {
441
- return this.aborts.track(() => this.parent.run(r.provideService(e, H, this.txn)));
442
- }
443
- abortedBy() {
444
- return this.aborts.abortedBy();
445
- }
446
- raw(e, t) {
447
- return this.aborts.track(() => this.parent.rawOn(e, this.txn));
448
- }
449
- query(e) {
450
- return this.aborts.track(() => this.parent.getInternalRunWithEager()(e, this.txn));
451
- }
452
- emptyTables(e) {
453
- return this.aborts.track(() => this.parent.emptyTablesOn(e, this.txn));
454
- }
455
- insert(e, t) {
456
- return this.aborts.track(() => this.parent.getInternalExecuteInsert()(e, t, this.txn, this.events, this.attr));
457
- }
458
- insertMany(e, t) {
459
- return this.aborts.track(() => this.parent.getInternalExecuteInsertMany()(e, t, this.txn, this.events, this.attr));
460
- }
461
- patchJson(e, t, n, r, i) {
462
- return this.aborts.track(() => this.parent.getInternalExecutePatchJson()(e, t, n, r, this.txn, this.events, this.attr, i));
463
- }
464
- update(e, t, n) {
465
- return this.aborts.track(() => this.parent.getInternalExecuteUpdate()(e, t, n, this.txn, this.events, this.attr));
466
- }
467
- delete(e, t) {
468
- return this.aborts.track(() => this.parent.getInternalExecuteDelete()(e, t, this.txn, this.events, this.attr));
469
- }
470
- updateMany(e, t, n) {
471
- return this.aborts.track(() => this.parent.getInternalExecuteUpdateMany()(e, t, n, this.txn, this.events, this.attr));
472
- }
473
- deleteMany(e, t) {
474
- return this.aborts.track(() => this.parent.getInternalExecuteDeleteMany()(e, t, this.txn, this.events, this.attr));
475
- }
476
- upsert(e, t, n) {
477
- return this.aborts.track(() => this.parent.getInternalExecuteUpsert()(e, t, n, this.txn, this.events, this.attr));
478
- }
479
- insertIgnore(e, t, n) {
480
- return this.insertIgnoreWithOutcome(e, t, n).then((e) => e.row);
481
- }
482
- insertIgnoreWithOutcome(e, t, n) {
483
- return this.aborts.track(() => this.parent.getInternalExecuteInsertIgnore()(e, t, n, this.txn, this.events, this.attr));
484
- }
485
- transactional(e) {
486
- return Promise.reject(/* @__PURE__ */ Error("SqliteStore.transactional: nested transactions are not supported. Mutations should execute a single top-level transaction."));
487
- }
488
- onChange(e) {
489
- throw Error("SqliteTransactionalView.onChange: subscribing from inside a transaction is not supported.");
490
- }
491
- injectExternalChange(e) {
492
- throw Error("SqliteTransactionalView.injectExternalChange: injecting external changes from inside a transaction is not supported.");
493
- }
494
- commitEvents() {
495
- if (!this.committed) {
496
- this.committed = !0;
497
- for (let e of this.events) this.parent.emitChange(e);
498
- this.events.length = 0;
499
- }
500
- }
501
- }, le = {
1
+ import { SqliteClient as e, connectionFromConfig as t, makeSqliteSqlLayer as n, makeSqliteSqlLayerFromConfig as r } from "./sqlLayer.js";
2
+ import { sqliteRetryFilter as i } from "./retry.js";
3
+ import { makeSqliteStore as a } from "./store.js";
4
+ //#region src/index.ts
5
+ var o = {
502
6
  id: "sqlite",
503
- makeSqlLayer: (e) => G(e),
504
- makeStore: (e) => Z(e),
7
+ makeSqlLayer: (e) => r(e),
8
+ makeStore: (e) => a(e),
505
9
  compileContains: (e, t, n) => n`${e} LIKE ${`%${t.replace(/[\\%_]/g, (e) => `\\${e}`)}%`} ESCAPE '\\'`,
506
- retryFilter: Y
10
+ retryFilter: i
507
11
  };
508
12
  //#endregion
509
- export { e as SqliteClient, W as connectionFromConfig, U as makeSqliteSqlLayer, G as makeSqliteSqlLayerFromConfig, Z as makeSqliteStore, le as sqliteDialect, Y as sqliteRetryFilter };
13
+ export { e as SqliteClient, t as connectionFromConfig, n as makeSqliteSqlLayer, r as makeSqliteSqlLayerFromConfig, a as makeSqliteStore, o as sqliteDialect, i as sqliteRetryFilter };
package/dist/retry.js ADDED
@@ -0,0 +1,17 @@
1
+ import { SQLITE_TRANSIENT_CODES as e, causeBranches as t } from "@voltro/database";
2
+ //#region src/retry.ts
3
+ var n = e, r = (e) => {
4
+ let t = e;
5
+ for (let e = 0; e < 5 && typeof t == "object" && t; e++) {
6
+ let { code: e, errcode: n } = t;
7
+ if (typeof n == "number") return String(n);
8
+ if (typeof e == "string" && e !== "ERR_SQLITE_ERROR") return e;
9
+ if (typeof e == "number") return String(e);
10
+ t = t.cause;
11
+ }
12
+ }, i = (e) => t(e).some((e) => {
13
+ let t = r(e);
14
+ return t !== void 0 && n.has(t);
15
+ }), a = (e) => i(e) ? "retry" : "noRetry";
16
+ //#endregion
17
+ export { i as isRetryableSqliteFailure, a as sqliteRetryFilter };
@@ -0,0 +1,21 @@
1
+ import { SqliteClient as e } from "@effect/sql-sqlite-node";
2
+ import { Config as t, Context as n, Layer as r } from "effect";
3
+ import { SHARED_TRANSACTION_SERVICE as i } from "@voltro/database";
4
+ //#region src/sqlLayer.ts
5
+ var a = (a) => e.layerConfig({
6
+ transactionService: t.succeed(i),
7
+ filename: t.succeed(a.filename),
8
+ prepareCacheSize: t.succeed(0),
9
+ transactionMode: t.succeed("deferred"),
10
+ ...a.readonly === void 0 ? {} : { readonly: t.succeed(a.readonly) }
11
+ }).pipe(r.tap((t) => n.get(t, e.SqliteClient)`PRAGMA foreign_keys = ON`)), o = (e) => {
12
+ if (e.url) {
13
+ if (e.url === ":memory:") return { filename: ":memory:" };
14
+ if (e.url.startsWith("file:")) return { filename: e.url.slice(5) };
15
+ throw Error(`@voltro/sql-sqlite: unsupported url '${e.url}'. Accepted: 'file:./path.sqlite', 'file:/abs/path.sqlite', ':memory:'.`);
16
+ }
17
+ if (e.database) return { filename: e.database };
18
+ throw Error("@voltro/sql-sqlite: no filename supplied. Set DB_URL=file:./db.sqlite or DB_DATABASE=path/to/db.sqlite.");
19
+ }, s = (e) => a(o(e));
20
+ //#endregion
21
+ export { e as SqliteClient, o as connectionFromConfig, a as makeSqliteSqlLayer, s as makeSqliteSqlLayerFromConfig };