@rotorsoft/act-sqlite 1.22.3 → 1.22.5
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/README.md +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/sqlite-store.d.ts +11 -55
- package/dist/@types/sqlite-store.d.ts.map +1 -1
- package/dist/index.cjs +180 -185
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +183 -186
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/sqlite-store.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createClient
|
|
4
|
+
} from "@libsql/client";
|
|
3
5
|
import {
|
|
4
6
|
ConcurrencyError,
|
|
5
7
|
is_literal_source,
|
|
@@ -79,13 +81,11 @@ function streamPatternToGlob(input) {
|
|
|
79
81
|
}
|
|
80
82
|
var SqliteStore = class {
|
|
81
83
|
client;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
* provider.
|
|
88
|
-
*/
|
|
84
|
+
// Memoized key resolver for the optional `pii_encryption` envelope.
|
|
85
|
+
// Initialized in the constructor when encryption is configured;
|
|
86
|
+
// `undefined` otherwise. The resolver caches the operator's key on
|
|
87
|
+
// first use — rotation means restarting the store with a fresh
|
|
88
|
+
// provider.
|
|
89
89
|
_resolve_pii_key;
|
|
90
90
|
constructor(config) {
|
|
91
91
|
this.client = createClient({
|
|
@@ -94,14 +94,10 @@ var SqliteStore = class {
|
|
|
94
94
|
});
|
|
95
95
|
this._resolve_pii_key = config.pii_encryption ? makeKeyResolver(config.pii_encryption) : void 0;
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* column entirely on a `NULL` write).
|
|
102
|
-
*
|
|
103
|
-
* @internal
|
|
104
|
-
*/
|
|
97
|
+
// Write-side pii encoder: encrypts when `pii_encryption` is configured,
|
|
98
|
+
// stringifies for the TEXT column either way. `null` passes through so
|
|
99
|
+
// `forget_pii` semantics survive intact (the row format skips the
|
|
100
|
+
// column entirely on a `NULL` write).
|
|
105
101
|
async _stringify_pii_for_write(pii) {
|
|
106
102
|
if (pii == null) return null;
|
|
107
103
|
if (this._resolve_pii_key) {
|
|
@@ -109,15 +105,11 @@ var SqliteStore = class {
|
|
|
109
105
|
}
|
|
110
106
|
return JSON.stringify(pii);
|
|
111
107
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
* rollouts (some pre-encryption rows, some post-) read cleanly.
|
|
118
|
-
*
|
|
119
|
-
* @internal
|
|
120
|
-
*/
|
|
108
|
+
// Read-side pii decoder: parses the TEXT column, and when
|
|
109
|
+
// `pii_encryption` is configured, transparently decrypts any value
|
|
110
|
+
// that came back as a JSON string. Plaintext writes deserialize to
|
|
111
|
+
// objects and pass through — which is what makes mixed-data
|
|
112
|
+
// rollouts (some pre-encryption rows, some post-) read cleanly.
|
|
121
113
|
async _parse_pii_from_read(raw) {
|
|
122
114
|
if (raw == null) return null;
|
|
123
115
|
const parsed = parse_json(raw);
|
|
@@ -127,8 +119,8 @@ var SqliteStore = class {
|
|
|
127
119
|
return parsed;
|
|
128
120
|
}
|
|
129
121
|
async seed() {
|
|
130
|
-
await this.
|
|
131
|
-
await this.
|
|
122
|
+
await this._exec("PRAGMA journal_mode=WAL");
|
|
123
|
+
await this._exec(`
|
|
132
124
|
CREATE TABLE IF NOT EXISTS events (
|
|
133
125
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
134
126
|
stream TEXT NOT NULL,
|
|
@@ -142,22 +134,22 @@ var SqliteStore = class {
|
|
|
142
134
|
)
|
|
143
135
|
`);
|
|
144
136
|
try {
|
|
145
|
-
await this.
|
|
137
|
+
await this._exec("ALTER TABLE events ADD COLUMN pii TEXT");
|
|
146
138
|
} catch {
|
|
147
139
|
}
|
|
148
|
-
await this.
|
|
140
|
+
await this._exec(
|
|
149
141
|
"CREATE INDEX IF NOT EXISTS idx_events_stream ON events(stream)"
|
|
150
142
|
);
|
|
151
|
-
await this.
|
|
143
|
+
await this._exec(
|
|
152
144
|
"CREATE INDEX IF NOT EXISTS idx_events_name ON events(name)"
|
|
153
145
|
);
|
|
154
|
-
await this.
|
|
146
|
+
await this._exec(
|
|
155
147
|
"CREATE INDEX IF NOT EXISTS idx_events_snapshot ON events(stream, id) WHERE name = '__snapshot__'"
|
|
156
148
|
);
|
|
157
|
-
await this.
|
|
149
|
+
await this._exec(
|
|
158
150
|
"CREATE INDEX IF NOT EXISTS idx_events_stream_id ON events(stream, id) WHERE name <> '__snapshot__'"
|
|
159
151
|
);
|
|
160
|
-
await this.
|
|
152
|
+
await this._exec(`
|
|
161
153
|
CREATE TABLE IF NOT EXISTS streams (
|
|
162
154
|
stream TEXT PRIMARY KEY,
|
|
163
155
|
source TEXT,
|
|
@@ -174,40 +166,36 @@ var SqliteStore = class {
|
|
|
174
166
|
)
|
|
175
167
|
`);
|
|
176
168
|
try {
|
|
177
|
-
await this.
|
|
169
|
+
await this._exec(
|
|
178
170
|
"ALTER TABLE streams ADD COLUMN priority INTEGER NOT NULL DEFAULT 0"
|
|
179
171
|
);
|
|
180
172
|
} catch {
|
|
181
173
|
}
|
|
182
174
|
try {
|
|
183
|
-
await this.
|
|
184
|
-
"ALTER TABLE streams ADD COLUMN deferred_at TEXT"
|
|
185
|
-
);
|
|
175
|
+
await this._exec("ALTER TABLE streams ADD COLUMN deferred_at TEXT");
|
|
186
176
|
} catch {
|
|
187
177
|
}
|
|
188
178
|
try {
|
|
189
|
-
await this.
|
|
179
|
+
await this._exec(
|
|
190
180
|
"ALTER TABLE streams ADD COLUMN lane TEXT NOT NULL DEFAULT 'default'"
|
|
191
181
|
);
|
|
192
182
|
} catch {
|
|
193
183
|
}
|
|
194
184
|
try {
|
|
195
|
-
await this.
|
|
196
|
-
"ALTER TABLE streams ADD COLUMN correlated_at INTEGER"
|
|
197
|
-
);
|
|
185
|
+
await this._exec("ALTER TABLE streams ADD COLUMN correlated_at INTEGER");
|
|
198
186
|
} catch {
|
|
199
187
|
}
|
|
200
|
-
await this.
|
|
188
|
+
await this._exec(
|
|
201
189
|
`UPDATE streams
|
|
202
190
|
SET correlated_at = (SELECT COALESCE(MAX(id), -1) FROM events)
|
|
203
191
|
WHERE correlated_at IS NULL`
|
|
204
192
|
);
|
|
205
|
-
const correlated_columns = await this.
|
|
193
|
+
const correlated_columns = await this._exec(
|
|
206
194
|
"PRAGMA table_info(correlated)"
|
|
207
195
|
);
|
|
208
196
|
const legacy = correlated_columns.rows.some((r) => r.name === "id");
|
|
209
197
|
if (legacy) {
|
|
210
|
-
await this.
|
|
198
|
+
await this._exec(`
|
|
211
199
|
CREATE TABLE IF NOT EXISTS correlated_keyed (
|
|
212
200
|
key TEXT PRIMARY KEY,
|
|
213
201
|
at INTEGER NOT NULL DEFAULT -1,
|
|
@@ -215,15 +203,13 @@ var SqliteStore = class {
|
|
|
215
203
|
leased_until INTEGER
|
|
216
204
|
)
|
|
217
205
|
`);
|
|
218
|
-
await this.
|
|
206
|
+
await this._exec(
|
|
219
207
|
"INSERT OR IGNORE INTO correlated_keyed (key, at) SELECT '', at FROM correlated WHERE id = 0"
|
|
220
208
|
);
|
|
221
|
-
await this.
|
|
222
|
-
await this.
|
|
223
|
-
"ALTER TABLE correlated_keyed RENAME TO correlated"
|
|
224
|
-
);
|
|
209
|
+
await this._exec("DROP TABLE correlated");
|
|
210
|
+
await this._exec("ALTER TABLE correlated_keyed RENAME TO correlated");
|
|
225
211
|
} else {
|
|
226
|
-
await this.
|
|
212
|
+
await this._exec(`
|
|
227
213
|
CREATE TABLE IF NOT EXISTS correlated (
|
|
228
214
|
key TEXT PRIMARY KEY,
|
|
229
215
|
at INTEGER NOT NULL DEFAULT -1,
|
|
@@ -232,104 +218,121 @@ var SqliteStore = class {
|
|
|
232
218
|
)
|
|
233
219
|
`);
|
|
234
220
|
}
|
|
235
|
-
await this.
|
|
236
|
-
|
|
237
|
-
);
|
|
238
|
-
await this.client.execute(
|
|
221
|
+
await this._exec("INSERT OR IGNORE INTO correlated (key) VALUES ('')");
|
|
222
|
+
await this._exec(
|
|
239
223
|
"CREATE INDEX IF NOT EXISTS idx_streams_claim ON streams(blocked, priority DESC, at)"
|
|
240
224
|
);
|
|
241
|
-
await this.
|
|
225
|
+
await this._exec(
|
|
242
226
|
"CREATE INDEX IF NOT EXISTS idx_streams_correlated_at ON streams(lane, priority DESC, at) WHERE blocked = 0 AND at < correlated_at"
|
|
243
227
|
);
|
|
244
|
-
await this.
|
|
228
|
+
await this._exec(
|
|
245
229
|
"CREATE INDEX IF NOT EXISTS idx_streams_correlated_at_any_lane ON streams(priority DESC, at) WHERE blocked = 0 AND at < correlated_at"
|
|
246
230
|
);
|
|
247
|
-
await this.
|
|
231
|
+
await this._exec(
|
|
248
232
|
"CREATE INDEX IF NOT EXISTS idx_streams_lane ON streams(lane)"
|
|
249
233
|
);
|
|
250
234
|
}
|
|
251
235
|
async drop() {
|
|
252
|
-
await this.
|
|
253
|
-
await this.
|
|
254
|
-
await this.
|
|
236
|
+
await this._exec("DROP TABLE IF EXISTS events");
|
|
237
|
+
await this._exec("DROP TABLE IF EXISTS streams");
|
|
238
|
+
await this._exec("DROP TABLE IF EXISTS correlated");
|
|
255
239
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
240
|
+
// One connection serves the whole store, and it can't run a statement
|
|
241
|
+
// while a write transaction is open on it: a second one fails at once
|
|
242
|
+
// (`TRANSACTION_ACTIVE` in memory, `SQLITE_BUSY` on a file) instead of
|
|
243
|
+
// waiting. So every statement and every transaction takes its turn here.
|
|
244
|
+
// A turn covers only the database work, never a caller's callback, so a
|
|
245
|
+
// callback that writes to the store can't wait on itself.
|
|
246
|
+
_turn = Promise.resolve();
|
|
247
|
+
_serial(work) {
|
|
248
|
+
const run = this._turn.then(work);
|
|
249
|
+
this._turn = run.catch(() => void 0);
|
|
250
|
+
return run;
|
|
251
|
+
}
|
|
252
|
+
// One statement, in its turn.
|
|
253
|
+
_exec(stmt) {
|
|
254
|
+
return this._serial(() => this.client.execute(stmt));
|
|
255
|
+
}
|
|
256
|
+
// One write transaction, in its turn: any failure rolls back and throws a {@link StoreError} naming the operation.
|
|
257
|
+
_tx(operation, work) {
|
|
258
|
+
return this._serial(async () => {
|
|
259
|
+
const tx = await this.client.transaction("write");
|
|
260
|
+
try {
|
|
261
|
+
const result = await work(tx);
|
|
262
|
+
await tx.commit();
|
|
263
|
+
return result;
|
|
264
|
+
} catch (e) {
|
|
265
|
+
await tx.rollback();
|
|
266
|
+
throw new StoreError(operation, { cause: e });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
267
269
|
}
|
|
268
270
|
async dispose() {
|
|
269
|
-
await
|
|
270
|
-
this.client.close();
|
|
271
|
+
await this._serial(async () => this.client.close());
|
|
271
272
|
}
|
|
272
273
|
// --- commit: transaction + optimistic concurrency ---
|
|
273
274
|
async commit(stream, msgs, meta, expectedVersion) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
275
|
+
return this._serial(async () => {
|
|
276
|
+
const tx = await this.client.transaction("write");
|
|
277
|
+
let current_version = -1;
|
|
278
|
+
try {
|
|
279
|
+
const version_row = await tx.execute({
|
|
280
|
+
sql: "SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?",
|
|
281
|
+
args: [stream]
|
|
282
|
+
});
|
|
283
|
+
current_version = Number(version_row.rows[0].v);
|
|
284
|
+
if (typeof expectedVersion === "number" && current_version !== expectedVersion) {
|
|
285
|
+
throw new ConcurrencyError(
|
|
286
|
+
stream,
|
|
287
|
+
current_version,
|
|
288
|
+
msgs,
|
|
289
|
+
expectedVersion
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
293
|
+
const committed = [];
|
|
294
|
+
let version = current_version + 1;
|
|
295
|
+
for (const { name, data, pii } of msgs) {
|
|
296
|
+
const pii_for_write = await this._stringify_pii_for_write(pii);
|
|
297
|
+
const result = await tx.execute({
|
|
298
|
+
sql: "INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
299
|
+
args: [
|
|
300
|
+
stream,
|
|
301
|
+
version,
|
|
302
|
+
name,
|
|
303
|
+
JSON.stringify(data),
|
|
304
|
+
JSON.stringify(meta),
|
|
305
|
+
now,
|
|
306
|
+
pii_for_write
|
|
307
|
+
]
|
|
308
|
+
});
|
|
309
|
+
committed.push({
|
|
310
|
+
id: Number(result.lastInsertRowid),
|
|
298
311
|
stream,
|
|
299
312
|
version,
|
|
313
|
+
created: new Date(now),
|
|
300
314
|
name,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
315
|
+
data,
|
|
316
|
+
meta,
|
|
317
|
+
...pii == null ? {} : { pii }
|
|
318
|
+
});
|
|
319
|
+
version++;
|
|
320
|
+
}
|
|
321
|
+
await tx.commit();
|
|
322
|
+
return committed;
|
|
323
|
+
} catch (e) {
|
|
324
|
+
await tx.rollback();
|
|
325
|
+
if (e instanceof ConcurrencyError) throw e;
|
|
326
|
+
if (is_unique_violation(e))
|
|
327
|
+
throw new ConcurrencyError(
|
|
328
|
+
stream,
|
|
329
|
+
current_version,
|
|
330
|
+
msgs,
|
|
331
|
+
expectedVersion ?? -1
|
|
332
|
+
);
|
|
333
|
+
throw new StoreError("commit", { cause: e });
|
|
318
334
|
}
|
|
319
|
-
|
|
320
|
-
return committed;
|
|
321
|
-
} catch (e) {
|
|
322
|
-
await tx.rollback();
|
|
323
|
-
if (e instanceof ConcurrencyError) throw e;
|
|
324
|
-
if (is_unique_violation(e))
|
|
325
|
-
throw new ConcurrencyError(
|
|
326
|
-
stream,
|
|
327
|
-
current_version,
|
|
328
|
-
msgs,
|
|
329
|
-
expectedVersion ?? -1
|
|
330
|
-
);
|
|
331
|
-
throw new StoreError("commit", { cause: e });
|
|
332
|
-
}
|
|
335
|
+
});
|
|
333
336
|
}
|
|
334
337
|
// --- query: read-only, no transaction needed ---
|
|
335
338
|
async query(callback, query) {
|
|
@@ -379,7 +382,7 @@ var SqliteStore = class {
|
|
|
379
382
|
sql += " LIMIT ?";
|
|
380
383
|
args.push(query.limit);
|
|
381
384
|
}
|
|
382
|
-
const result = await this.
|
|
385
|
+
const result = await this._exec({ sql, args });
|
|
383
386
|
let count = 0;
|
|
384
387
|
for (const row of result.rows) {
|
|
385
388
|
const pii_value = query?.with_pii === false ? null : await this._parse_pii_from_read(row.pii);
|
|
@@ -653,17 +656,15 @@ var SqliteStore = class {
|
|
|
653
656
|
at_iso
|
|
654
657
|
]);
|
|
655
658
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
* @returns Count of rows updated.
|
|
666
|
-
*/
|
|
659
|
+
// Runs `UPDATE streams <set>` on the streams named by `input`, in one
|
|
660
|
+
// write transaction (`_tx`) that rolls back on failure. An explicit list is
|
|
661
|
+
// de-duplicated, so a repeated name counts once (matching PG's set-based
|
|
662
|
+
// `stream = ANY(...)`), and updated one name per statement, so a long list
|
|
663
|
+
// never meets SQLite's bound-parameter limit. `set` and `where` bind
|
|
664
|
+
// `params` in order; libSQL `?` placeholders are positional and can't be
|
|
665
|
+
// reused, so a value used twice is passed twice.
|
|
666
|
+
//
|
|
667
|
+
// @returns Count of rows updated.
|
|
667
668
|
_update(operation, input, set, params = [], where) {
|
|
668
669
|
const selections = Array.isArray(input) ? [...new Set(input)].map((stream) => ({
|
|
669
670
|
clause: "stream = ?",
|
|
@@ -681,11 +682,9 @@ var SqliteStore = class {
|
|
|
681
682
|
return count;
|
|
682
683
|
});
|
|
683
684
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
* callers can compose it unconditionally.
|
|
688
|
-
*/
|
|
685
|
+
// Translate a {@link StreamFilter} to a SQLite `WHERE` clause fragment
|
|
686
|
+
// plus positional args. Returns `"1"` (always true) when empty so
|
|
687
|
+
// callers can compose it unconditionally.
|
|
689
688
|
_filter_clause(filter) {
|
|
690
689
|
const conditions = [];
|
|
691
690
|
const args = [];
|
|
@@ -754,8 +753,8 @@ var SqliteStore = class {
|
|
|
754
753
|
sql += " ORDER BY stream LIMIT ?";
|
|
755
754
|
args.push(limit);
|
|
756
755
|
const [streamsResult, maxResult] = await Promise.all([
|
|
757
|
-
this.
|
|
758
|
-
this.
|
|
756
|
+
this._exec({ sql, args }),
|
|
757
|
+
this._exec("SELECT COALESCE(MAX(id), -1) AS m FROM events")
|
|
759
758
|
]);
|
|
760
759
|
let count = 0;
|
|
761
760
|
for (const row of streamsResult.rows) {
|
|
@@ -862,10 +861,8 @@ var SqliteStore = class {
|
|
|
862
861
|
limit
|
|
863
862
|
);
|
|
864
863
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
* `(stream, version)` unique index. Parallel queries when tail set.
|
|
868
|
-
*/
|
|
864
|
+
// Cheap path — head (and optional tail) via ROW_NUMBER() over the
|
|
865
|
+
// `(stream, version)` unique index. Parallel queries when tail set.
|
|
869
866
|
async _query_stats_heads_only(from_clause, where_clause, args, want_tail, limit) {
|
|
870
867
|
const cols = `e.id, e.stream, e.version, e.name, e.data, e.created, e.meta`;
|
|
871
868
|
const limit_clause = limit !== void 0 ? " LIMIT ?" : "";
|
|
@@ -889,8 +886,8 @@ var SqliteStore = class {
|
|
|
889
886
|
)` : null;
|
|
890
887
|
const tail_args = limit !== void 0 ? [...args, ...args, limit] : [...args, ...args];
|
|
891
888
|
const [headRes, tailRes] = await Promise.all([
|
|
892
|
-
this.
|
|
893
|
-
tail_sql ? this.
|
|
889
|
+
this._exec({ sql: head_sql, args: head_args }),
|
|
890
|
+
tail_sql ? this._exec({ sql: tail_sql, args: tail_args }) : null
|
|
894
891
|
]);
|
|
895
892
|
const out = /* @__PURE__ */ new Map();
|
|
896
893
|
for (const row of headRes.rows) {
|
|
@@ -905,11 +902,9 @@ var SqliteStore = class {
|
|
|
905
902
|
}
|
|
906
903
|
return out;
|
|
907
904
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
* on the same scan.
|
|
912
|
-
*/
|
|
905
|
+
// Full-scan path — one CTE-based query with per-stream `COUNT(*)` and
|
|
906
|
+
// `json_group_object(name, n)`. Heads (and optional tails) ride free
|
|
907
|
+
// on the same scan.
|
|
913
908
|
async _query_stats_full_scan(from_clause, where_clause, args, want_tail, want_count, want_names, limit) {
|
|
914
909
|
const tail_cte = want_tail ? `, tails AS (
|
|
915
910
|
SELECT * FROM (
|
|
@@ -954,7 +949,7 @@ var SqliteStore = class {
|
|
|
954
949
|
ORDER BY h.stream
|
|
955
950
|
`;
|
|
956
951
|
const full_args = limit !== void 0 ? [...args, limit] : args;
|
|
957
|
-
const res = await this.
|
|
952
|
+
const res = await this._exec({ sql, args: full_args });
|
|
958
953
|
const out = /* @__PURE__ */ new Map();
|
|
959
954
|
for (const row of res.rows) {
|
|
960
955
|
const r = row;
|
|
@@ -1064,33 +1059,35 @@ var SqliteStore = class {
|
|
|
1064
1059
|
* from the source.
|
|
1065
1060
|
*/
|
|
1066
1061
|
async restore(driver) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1062
|
+
return this._serial(async () => {
|
|
1063
|
+
const tx = await this.client.transaction("write");
|
|
1064
|
+
try {
|
|
1065
|
+
await tx.execute("DELETE FROM events");
|
|
1066
|
+
await tx.execute("DELETE FROM streams");
|
|
1067
|
+
await tx.execute("UPDATE correlated SET at = -1");
|
|
1068
|
+
await tx.execute("DELETE FROM sqlite_sequence WHERE name = 'events'");
|
|
1069
|
+
await driver(async (event) => {
|
|
1070
|
+
const pii_for_write = await this._stringify_pii_for_write(event.pii);
|
|
1071
|
+
const ins = await tx.execute({
|
|
1072
|
+
sql: "INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
1073
|
+
args: [
|
|
1074
|
+
event.stream,
|
|
1075
|
+
event.version,
|
|
1076
|
+
event.name,
|
|
1077
|
+
JSON.stringify(event.data),
|
|
1078
|
+
JSON.stringify(event.meta),
|
|
1079
|
+
event.created.toISOString(),
|
|
1080
|
+
pii_for_write
|
|
1081
|
+
]
|
|
1082
|
+
});
|
|
1083
|
+
return Number(ins.lastInsertRowid);
|
|
1086
1084
|
});
|
|
1087
|
-
|
|
1088
|
-
})
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
}
|
|
1085
|
+
await tx.commit();
|
|
1086
|
+
} catch (error) {
|
|
1087
|
+
await tx.rollback();
|
|
1088
|
+
throw error;
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1094
1091
|
}
|
|
1095
1092
|
/**
|
|
1096
1093
|
* Wipe the sensitive-data payload for every event on the stream — the
|
|
@@ -1110,7 +1107,7 @@ var SqliteStore = class {
|
|
|
1110
1107
|
* @returns Count of events whose `pii` was set to `NULL`
|
|
1111
1108
|
*/
|
|
1112
1109
|
async forget_pii(stream) {
|
|
1113
|
-
const r = await this.
|
|
1110
|
+
const r = await this._exec({
|
|
1114
1111
|
sql: "UPDATE events SET pii = NULL WHERE stream = ? AND pii IS NOT NULL",
|
|
1115
1112
|
args: [stream]
|
|
1116
1113
|
});
|