@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.cjs
CHANGED
|
@@ -97,13 +97,11 @@ function streamPatternToGlob(input) {
|
|
|
97
97
|
}
|
|
98
98
|
var SqliteStore = class {
|
|
99
99
|
client;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* provider.
|
|
106
|
-
*/
|
|
100
|
+
// Memoized key resolver for the optional `pii_encryption` envelope.
|
|
101
|
+
// Initialized in the constructor when encryption is configured;
|
|
102
|
+
// `undefined` otherwise. The resolver caches the operator's key on
|
|
103
|
+
// first use — rotation means restarting the store with a fresh
|
|
104
|
+
// provider.
|
|
107
105
|
_resolve_pii_key;
|
|
108
106
|
constructor(config) {
|
|
109
107
|
this.client = (0, import_client.createClient)({
|
|
@@ -112,14 +110,10 @@ var SqliteStore = class {
|
|
|
112
110
|
});
|
|
113
111
|
this._resolve_pii_key = config.pii_encryption ? (0, import_act_crypto.makeKeyResolver)(config.pii_encryption) : void 0;
|
|
114
112
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* column entirely on a `NULL` write).
|
|
120
|
-
*
|
|
121
|
-
* @internal
|
|
122
|
-
*/
|
|
113
|
+
// Write-side pii encoder: encrypts when `pii_encryption` is configured,
|
|
114
|
+
// stringifies for the TEXT column either way. `null` passes through so
|
|
115
|
+
// `forget_pii` semantics survive intact (the row format skips the
|
|
116
|
+
// column entirely on a `NULL` write).
|
|
123
117
|
async _stringify_pii_for_write(pii) {
|
|
124
118
|
if (pii == null) return null;
|
|
125
119
|
if (this._resolve_pii_key) {
|
|
@@ -127,15 +121,11 @@ var SqliteStore = class {
|
|
|
127
121
|
}
|
|
128
122
|
return JSON.stringify(pii);
|
|
129
123
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
* rollouts (some pre-encryption rows, some post-) read cleanly.
|
|
136
|
-
*
|
|
137
|
-
* @internal
|
|
138
|
-
*/
|
|
124
|
+
// Read-side pii decoder: parses the TEXT column, and when
|
|
125
|
+
// `pii_encryption` is configured, transparently decrypts any value
|
|
126
|
+
// that came back as a JSON string. Plaintext writes deserialize to
|
|
127
|
+
// objects and pass through — which is what makes mixed-data
|
|
128
|
+
// rollouts (some pre-encryption rows, some post-) read cleanly.
|
|
139
129
|
async _parse_pii_from_read(raw) {
|
|
140
130
|
if (raw == null) return null;
|
|
141
131
|
const parsed = parse_json(raw);
|
|
@@ -145,8 +135,8 @@ var SqliteStore = class {
|
|
|
145
135
|
return parsed;
|
|
146
136
|
}
|
|
147
137
|
async seed() {
|
|
148
|
-
await this.
|
|
149
|
-
await this.
|
|
138
|
+
await this._exec("PRAGMA journal_mode=WAL");
|
|
139
|
+
await this._exec(`
|
|
150
140
|
CREATE TABLE IF NOT EXISTS events (
|
|
151
141
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
152
142
|
stream TEXT NOT NULL,
|
|
@@ -160,22 +150,22 @@ var SqliteStore = class {
|
|
|
160
150
|
)
|
|
161
151
|
`);
|
|
162
152
|
try {
|
|
163
|
-
await this.
|
|
153
|
+
await this._exec("ALTER TABLE events ADD COLUMN pii TEXT");
|
|
164
154
|
} catch {
|
|
165
155
|
}
|
|
166
|
-
await this.
|
|
156
|
+
await this._exec(
|
|
167
157
|
"CREATE INDEX IF NOT EXISTS idx_events_stream ON events(stream)"
|
|
168
158
|
);
|
|
169
|
-
await this.
|
|
159
|
+
await this._exec(
|
|
170
160
|
"CREATE INDEX IF NOT EXISTS idx_events_name ON events(name)"
|
|
171
161
|
);
|
|
172
|
-
await this.
|
|
162
|
+
await this._exec(
|
|
173
163
|
"CREATE INDEX IF NOT EXISTS idx_events_snapshot ON events(stream, id) WHERE name = '__snapshot__'"
|
|
174
164
|
);
|
|
175
|
-
await this.
|
|
165
|
+
await this._exec(
|
|
176
166
|
"CREATE INDEX IF NOT EXISTS idx_events_stream_id ON events(stream, id) WHERE name <> '__snapshot__'"
|
|
177
167
|
);
|
|
178
|
-
await this.
|
|
168
|
+
await this._exec(`
|
|
179
169
|
CREATE TABLE IF NOT EXISTS streams (
|
|
180
170
|
stream TEXT PRIMARY KEY,
|
|
181
171
|
source TEXT,
|
|
@@ -192,40 +182,36 @@ var SqliteStore = class {
|
|
|
192
182
|
)
|
|
193
183
|
`);
|
|
194
184
|
try {
|
|
195
|
-
await this.
|
|
185
|
+
await this._exec(
|
|
196
186
|
"ALTER TABLE streams ADD COLUMN priority INTEGER NOT NULL DEFAULT 0"
|
|
197
187
|
);
|
|
198
188
|
} catch {
|
|
199
189
|
}
|
|
200
190
|
try {
|
|
201
|
-
await this.
|
|
202
|
-
"ALTER TABLE streams ADD COLUMN deferred_at TEXT"
|
|
203
|
-
);
|
|
191
|
+
await this._exec("ALTER TABLE streams ADD COLUMN deferred_at TEXT");
|
|
204
192
|
} catch {
|
|
205
193
|
}
|
|
206
194
|
try {
|
|
207
|
-
await this.
|
|
195
|
+
await this._exec(
|
|
208
196
|
"ALTER TABLE streams ADD COLUMN lane TEXT NOT NULL DEFAULT 'default'"
|
|
209
197
|
);
|
|
210
198
|
} catch {
|
|
211
199
|
}
|
|
212
200
|
try {
|
|
213
|
-
await this.
|
|
214
|
-
"ALTER TABLE streams ADD COLUMN correlated_at INTEGER"
|
|
215
|
-
);
|
|
201
|
+
await this._exec("ALTER TABLE streams ADD COLUMN correlated_at INTEGER");
|
|
216
202
|
} catch {
|
|
217
203
|
}
|
|
218
|
-
await this.
|
|
204
|
+
await this._exec(
|
|
219
205
|
`UPDATE streams
|
|
220
206
|
SET correlated_at = (SELECT COALESCE(MAX(id), -1) FROM events)
|
|
221
207
|
WHERE correlated_at IS NULL`
|
|
222
208
|
);
|
|
223
|
-
const correlated_columns = await this.
|
|
209
|
+
const correlated_columns = await this._exec(
|
|
224
210
|
"PRAGMA table_info(correlated)"
|
|
225
211
|
);
|
|
226
212
|
const legacy = correlated_columns.rows.some((r) => r.name === "id");
|
|
227
213
|
if (legacy) {
|
|
228
|
-
await this.
|
|
214
|
+
await this._exec(`
|
|
229
215
|
CREATE TABLE IF NOT EXISTS correlated_keyed (
|
|
230
216
|
key TEXT PRIMARY KEY,
|
|
231
217
|
at INTEGER NOT NULL DEFAULT -1,
|
|
@@ -233,15 +219,13 @@ var SqliteStore = class {
|
|
|
233
219
|
leased_until INTEGER
|
|
234
220
|
)
|
|
235
221
|
`);
|
|
236
|
-
await this.
|
|
222
|
+
await this._exec(
|
|
237
223
|
"INSERT OR IGNORE INTO correlated_keyed (key, at) SELECT '', at FROM correlated WHERE id = 0"
|
|
238
224
|
);
|
|
239
|
-
await this.
|
|
240
|
-
await this.
|
|
241
|
-
"ALTER TABLE correlated_keyed RENAME TO correlated"
|
|
242
|
-
);
|
|
225
|
+
await this._exec("DROP TABLE correlated");
|
|
226
|
+
await this._exec("ALTER TABLE correlated_keyed RENAME TO correlated");
|
|
243
227
|
} else {
|
|
244
|
-
await this.
|
|
228
|
+
await this._exec(`
|
|
245
229
|
CREATE TABLE IF NOT EXISTS correlated (
|
|
246
230
|
key TEXT PRIMARY KEY,
|
|
247
231
|
at INTEGER NOT NULL DEFAULT -1,
|
|
@@ -250,104 +234,121 @@ var SqliteStore = class {
|
|
|
250
234
|
)
|
|
251
235
|
`);
|
|
252
236
|
}
|
|
253
|
-
await this.
|
|
254
|
-
|
|
255
|
-
);
|
|
256
|
-
await this.client.execute(
|
|
237
|
+
await this._exec("INSERT OR IGNORE INTO correlated (key) VALUES ('')");
|
|
238
|
+
await this._exec(
|
|
257
239
|
"CREATE INDEX IF NOT EXISTS idx_streams_claim ON streams(blocked, priority DESC, at)"
|
|
258
240
|
);
|
|
259
|
-
await this.
|
|
241
|
+
await this._exec(
|
|
260
242
|
"CREATE INDEX IF NOT EXISTS idx_streams_correlated_at ON streams(lane, priority DESC, at) WHERE blocked = 0 AND at < correlated_at"
|
|
261
243
|
);
|
|
262
|
-
await this.
|
|
244
|
+
await this._exec(
|
|
263
245
|
"CREATE INDEX IF NOT EXISTS idx_streams_correlated_at_any_lane ON streams(priority DESC, at) WHERE blocked = 0 AND at < correlated_at"
|
|
264
246
|
);
|
|
265
|
-
await this.
|
|
247
|
+
await this._exec(
|
|
266
248
|
"CREATE INDEX IF NOT EXISTS idx_streams_lane ON streams(lane)"
|
|
267
249
|
);
|
|
268
250
|
}
|
|
269
251
|
async drop() {
|
|
270
|
-
await this.
|
|
271
|
-
await this.
|
|
272
|
-
await this.
|
|
252
|
+
await this._exec("DROP TABLE IF EXISTS events");
|
|
253
|
+
await this._exec("DROP TABLE IF EXISTS streams");
|
|
254
|
+
await this._exec("DROP TABLE IF EXISTS correlated");
|
|
273
255
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
256
|
+
// One connection serves the whole store, and it can't run a statement
|
|
257
|
+
// while a write transaction is open on it: a second one fails at once
|
|
258
|
+
// (`TRANSACTION_ACTIVE` in memory, `SQLITE_BUSY` on a file) instead of
|
|
259
|
+
// waiting. So every statement and every transaction takes its turn here.
|
|
260
|
+
// A turn covers only the database work, never a caller's callback, so a
|
|
261
|
+
// callback that writes to the store can't wait on itself.
|
|
262
|
+
_turn = Promise.resolve();
|
|
263
|
+
_serial(work) {
|
|
264
|
+
const run = this._turn.then(work);
|
|
265
|
+
this._turn = run.catch(() => void 0);
|
|
266
|
+
return run;
|
|
267
|
+
}
|
|
268
|
+
// One statement, in its turn.
|
|
269
|
+
_exec(stmt) {
|
|
270
|
+
return this._serial(() => this.client.execute(stmt));
|
|
271
|
+
}
|
|
272
|
+
// One write transaction, in its turn: any failure rolls back and throws a {@link StoreError} naming the operation.
|
|
273
|
+
_tx(operation, work) {
|
|
274
|
+
return this._serial(async () => {
|
|
275
|
+
const tx = await this.client.transaction("write");
|
|
276
|
+
try {
|
|
277
|
+
const result = await work(tx);
|
|
278
|
+
await tx.commit();
|
|
279
|
+
return result;
|
|
280
|
+
} catch (e) {
|
|
281
|
+
await tx.rollback();
|
|
282
|
+
throw new import_act.StoreError(operation, { cause: e });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
285
|
}
|
|
286
286
|
async dispose() {
|
|
287
|
-
await
|
|
288
|
-
this.client.close();
|
|
287
|
+
await this._serial(async () => this.client.close());
|
|
289
288
|
}
|
|
290
289
|
// --- commit: transaction + optimistic concurrency ---
|
|
291
290
|
async commit(stream, msgs, meta, expectedVersion) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
291
|
+
return this._serial(async () => {
|
|
292
|
+
const tx = await this.client.transaction("write");
|
|
293
|
+
let current_version = -1;
|
|
294
|
+
try {
|
|
295
|
+
const version_row = await tx.execute({
|
|
296
|
+
sql: "SELECT COALESCE(MAX(version), -1) as v FROM events WHERE stream = ?",
|
|
297
|
+
args: [stream]
|
|
298
|
+
});
|
|
299
|
+
current_version = Number(version_row.rows[0].v);
|
|
300
|
+
if (typeof expectedVersion === "number" && current_version !== expectedVersion) {
|
|
301
|
+
throw new import_act.ConcurrencyError(
|
|
302
|
+
stream,
|
|
303
|
+
current_version,
|
|
304
|
+
msgs,
|
|
305
|
+
expectedVersion
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
309
|
+
const committed = [];
|
|
310
|
+
let version = current_version + 1;
|
|
311
|
+
for (const { name, data, pii } of msgs) {
|
|
312
|
+
const pii_for_write = await this._stringify_pii_for_write(pii);
|
|
313
|
+
const result = await tx.execute({
|
|
314
|
+
sql: "INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
315
|
+
args: [
|
|
316
|
+
stream,
|
|
317
|
+
version,
|
|
318
|
+
name,
|
|
319
|
+
JSON.stringify(data),
|
|
320
|
+
JSON.stringify(meta),
|
|
321
|
+
now,
|
|
322
|
+
pii_for_write
|
|
323
|
+
]
|
|
324
|
+
});
|
|
325
|
+
committed.push({
|
|
326
|
+
id: Number(result.lastInsertRowid),
|
|
316
327
|
stream,
|
|
317
328
|
version,
|
|
329
|
+
created: new Date(now),
|
|
318
330
|
name,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
331
|
+
data,
|
|
332
|
+
meta,
|
|
333
|
+
...pii == null ? {} : { pii }
|
|
334
|
+
});
|
|
335
|
+
version++;
|
|
336
|
+
}
|
|
337
|
+
await tx.commit();
|
|
338
|
+
return committed;
|
|
339
|
+
} catch (e) {
|
|
340
|
+
await tx.rollback();
|
|
341
|
+
if (e instanceof import_act.ConcurrencyError) throw e;
|
|
342
|
+
if (is_unique_violation(e))
|
|
343
|
+
throw new import_act.ConcurrencyError(
|
|
344
|
+
stream,
|
|
345
|
+
current_version,
|
|
346
|
+
msgs,
|
|
347
|
+
expectedVersion ?? -1
|
|
348
|
+
);
|
|
349
|
+
throw new import_act.StoreError("commit", { cause: e });
|
|
336
350
|
}
|
|
337
|
-
|
|
338
|
-
return committed;
|
|
339
|
-
} catch (e) {
|
|
340
|
-
await tx.rollback();
|
|
341
|
-
if (e instanceof import_act.ConcurrencyError) throw e;
|
|
342
|
-
if (is_unique_violation(e))
|
|
343
|
-
throw new import_act.ConcurrencyError(
|
|
344
|
-
stream,
|
|
345
|
-
current_version,
|
|
346
|
-
msgs,
|
|
347
|
-
expectedVersion ?? -1
|
|
348
|
-
);
|
|
349
|
-
throw new import_act.StoreError("commit", { cause: e });
|
|
350
|
-
}
|
|
351
|
+
});
|
|
351
352
|
}
|
|
352
353
|
// --- query: read-only, no transaction needed ---
|
|
353
354
|
async query(callback, query) {
|
|
@@ -397,7 +398,7 @@ var SqliteStore = class {
|
|
|
397
398
|
sql += " LIMIT ?";
|
|
398
399
|
args.push(query.limit);
|
|
399
400
|
}
|
|
400
|
-
const result = await this.
|
|
401
|
+
const result = await this._exec({ sql, args });
|
|
401
402
|
let count = 0;
|
|
402
403
|
for (const row of result.rows) {
|
|
403
404
|
const pii_value = query?.with_pii === false ? null : await this._parse_pii_from_read(row.pii);
|
|
@@ -671,17 +672,15 @@ var SqliteStore = class {
|
|
|
671
672
|
at_iso
|
|
672
673
|
]);
|
|
673
674
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
* @returns Count of rows updated.
|
|
684
|
-
*/
|
|
675
|
+
// Runs `UPDATE streams <set>` on the streams named by `input`, in one
|
|
676
|
+
// write transaction (`_tx`) that rolls back on failure. An explicit list is
|
|
677
|
+
// de-duplicated, so a repeated name counts once (matching PG's set-based
|
|
678
|
+
// `stream = ANY(...)`), and updated one name per statement, so a long list
|
|
679
|
+
// never meets SQLite's bound-parameter limit. `set` and `where` bind
|
|
680
|
+
// `params` in order; libSQL `?` placeholders are positional and can't be
|
|
681
|
+
// reused, so a value used twice is passed twice.
|
|
682
|
+
//
|
|
683
|
+
// @returns Count of rows updated.
|
|
685
684
|
_update(operation, input, set, params = [], where) {
|
|
686
685
|
const selections = Array.isArray(input) ? [...new Set(input)].map((stream) => ({
|
|
687
686
|
clause: "stream = ?",
|
|
@@ -699,11 +698,9 @@ var SqliteStore = class {
|
|
|
699
698
|
return count;
|
|
700
699
|
});
|
|
701
700
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
* callers can compose it unconditionally.
|
|
706
|
-
*/
|
|
701
|
+
// Translate a {@link StreamFilter} to a SQLite `WHERE` clause fragment
|
|
702
|
+
// plus positional args. Returns `"1"` (always true) when empty so
|
|
703
|
+
// callers can compose it unconditionally.
|
|
707
704
|
_filter_clause(filter) {
|
|
708
705
|
const conditions = [];
|
|
709
706
|
const args = [];
|
|
@@ -772,8 +769,8 @@ var SqliteStore = class {
|
|
|
772
769
|
sql += " ORDER BY stream LIMIT ?";
|
|
773
770
|
args.push(limit);
|
|
774
771
|
const [streamsResult, maxResult] = await Promise.all([
|
|
775
|
-
this.
|
|
776
|
-
this.
|
|
772
|
+
this._exec({ sql, args }),
|
|
773
|
+
this._exec("SELECT COALESCE(MAX(id), -1) AS m FROM events")
|
|
777
774
|
]);
|
|
778
775
|
let count = 0;
|
|
779
776
|
for (const row of streamsResult.rows) {
|
|
@@ -880,10 +877,8 @@ var SqliteStore = class {
|
|
|
880
877
|
limit
|
|
881
878
|
);
|
|
882
879
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
* `(stream, version)` unique index. Parallel queries when tail set.
|
|
886
|
-
*/
|
|
880
|
+
// Cheap path — head (and optional tail) via ROW_NUMBER() over the
|
|
881
|
+
// `(stream, version)` unique index. Parallel queries when tail set.
|
|
887
882
|
async _query_stats_heads_only(from_clause, where_clause, args, want_tail, limit) {
|
|
888
883
|
const cols = `e.id, e.stream, e.version, e.name, e.data, e.created, e.meta`;
|
|
889
884
|
const limit_clause = limit !== void 0 ? " LIMIT ?" : "";
|
|
@@ -907,8 +902,8 @@ var SqliteStore = class {
|
|
|
907
902
|
)` : null;
|
|
908
903
|
const tail_args = limit !== void 0 ? [...args, ...args, limit] : [...args, ...args];
|
|
909
904
|
const [headRes, tailRes] = await Promise.all([
|
|
910
|
-
this.
|
|
911
|
-
tail_sql ? this.
|
|
905
|
+
this._exec({ sql: head_sql, args: head_args }),
|
|
906
|
+
tail_sql ? this._exec({ sql: tail_sql, args: tail_args }) : null
|
|
912
907
|
]);
|
|
913
908
|
const out = /* @__PURE__ */ new Map();
|
|
914
909
|
for (const row of headRes.rows) {
|
|
@@ -923,11 +918,9 @@ var SqliteStore = class {
|
|
|
923
918
|
}
|
|
924
919
|
return out;
|
|
925
920
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
* on the same scan.
|
|
930
|
-
*/
|
|
921
|
+
// Full-scan path — one CTE-based query with per-stream `COUNT(*)` and
|
|
922
|
+
// `json_group_object(name, n)`. Heads (and optional tails) ride free
|
|
923
|
+
// on the same scan.
|
|
931
924
|
async _query_stats_full_scan(from_clause, where_clause, args, want_tail, want_count, want_names, limit) {
|
|
932
925
|
const tail_cte = want_tail ? `, tails AS (
|
|
933
926
|
SELECT * FROM (
|
|
@@ -972,7 +965,7 @@ var SqliteStore = class {
|
|
|
972
965
|
ORDER BY h.stream
|
|
973
966
|
`;
|
|
974
967
|
const full_args = limit !== void 0 ? [...args, limit] : args;
|
|
975
|
-
const res = await this.
|
|
968
|
+
const res = await this._exec({ sql, args: full_args });
|
|
976
969
|
const out = /* @__PURE__ */ new Map();
|
|
977
970
|
for (const row of res.rows) {
|
|
978
971
|
const r = row;
|
|
@@ -1082,33 +1075,35 @@ var SqliteStore = class {
|
|
|
1082
1075
|
* from the source.
|
|
1083
1076
|
*/
|
|
1084
1077
|
async restore(driver) {
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1078
|
+
return this._serial(async () => {
|
|
1079
|
+
const tx = await this.client.transaction("write");
|
|
1080
|
+
try {
|
|
1081
|
+
await tx.execute("DELETE FROM events");
|
|
1082
|
+
await tx.execute("DELETE FROM streams");
|
|
1083
|
+
await tx.execute("UPDATE correlated SET at = -1");
|
|
1084
|
+
await tx.execute("DELETE FROM sqlite_sequence WHERE name = 'events'");
|
|
1085
|
+
await driver(async (event) => {
|
|
1086
|
+
const pii_for_write = await this._stringify_pii_for_write(event.pii);
|
|
1087
|
+
const ins = await tx.execute({
|
|
1088
|
+
sql: "INSERT INTO events (stream, version, name, data, meta, created, pii) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
1089
|
+
args: [
|
|
1090
|
+
event.stream,
|
|
1091
|
+
event.version,
|
|
1092
|
+
event.name,
|
|
1093
|
+
JSON.stringify(event.data),
|
|
1094
|
+
JSON.stringify(event.meta),
|
|
1095
|
+
event.created.toISOString(),
|
|
1096
|
+
pii_for_write
|
|
1097
|
+
]
|
|
1098
|
+
});
|
|
1099
|
+
return Number(ins.lastInsertRowid);
|
|
1104
1100
|
});
|
|
1105
|
-
|
|
1106
|
-
})
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
}
|
|
1101
|
+
await tx.commit();
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
await tx.rollback();
|
|
1104
|
+
throw error;
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1112
1107
|
}
|
|
1113
1108
|
/**
|
|
1114
1109
|
* Wipe the sensitive-data payload for every event on the stream — the
|
|
@@ -1128,7 +1123,7 @@ var SqliteStore = class {
|
|
|
1128
1123
|
* @returns Count of events whose `pii` was set to `NULL`
|
|
1129
1124
|
*/
|
|
1130
1125
|
async forget_pii(stream) {
|
|
1131
|
-
const r = await this.
|
|
1126
|
+
const r = await this._exec({
|
|
1132
1127
|
sql: "UPDATE events SET pii = NULL WHERE stream = ? AND pii IS NOT NULL",
|
|
1133
1128
|
args: [stream]
|
|
1134
1129
|
});
|