@rotorsoft/act-sqlite 1.18.0 → 1.19.1

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.
@@ -1,4 +1,4 @@
1
- import type { BlockedLease, Committed, EventMeta, Lease, Message, Query, QueryStatsOptions, QueryStreams, QueryStreamsResult, Schemas, Store, StreamFilter, StreamPosition, StreamStats, SubscribeInput } from "@rotorsoft/act";
1
+ import type { BlockedLease, Committed, EventMeta, Lease, Message, Query, QueryStatsOptions, QueryStreams, QueryStreamsResult, Schemas, Store, StreamFilter, StreamPosition, StreamStats, SubscribeInput, SubscribeResult } from "@rotorsoft/act";
2
2
  import { type Encryption } from "@rotorsoft/act-crypto";
3
3
  /**
4
4
  * SQLite store configuration
@@ -159,11 +159,11 @@ export declare class SqliteStore implements Store {
159
159
  dispose(): Promise<void>;
160
160
  commit<E extends Schemas>(stream: string, msgs: Message<E, keyof E>[], meta: EventMeta, expectedVersion?: number): Promise<Committed<E, keyof E>[]>;
161
161
  query<E extends Schemas>(callback: (event: Committed<E, keyof E>) => void, query?: Query): Promise<number>;
162
- subscribe(streams: SubscribeInput[], correlated_at?: number): Promise<{
163
- subscribed: number;
164
- watermark: number;
165
- correlated_at: number;
166
- }>;
162
+ subscribe(streams: SubscribeInput[], correlated_at?: number, correlator?: {
163
+ key: string;
164
+ by: string;
165
+ millis: number;
166
+ }): Promise<SubscribeResult>;
167
167
  claim(lagging: number, leading: number, by: string, millis: number, lane?: string): Promise<Lease[]>;
168
168
  ack(leases: Lease[]): Promise<Lease[]>;
169
169
  block(leases: BlockedLease[]): Promise<BlockedLease[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/sqlite-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../src/sqlite-store.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -46,7 +46,7 @@ var resolve_url = (url) => {
46
46
  );
47
47
  return is_in_memory_url(url) ? SHARED_MEMORY_URL : url;
48
48
  };
49
- var parse_json = (raw) => JSON.parse(raw, import_act.dateReviver);
49
+ var parse_json = (raw) => JSON.parse(raw);
50
50
  var SQLITE_CONSTRAINT_UNIQUE = 2067;
51
51
  var is_unique_violation = (e) => {
52
52
  const err = e;
@@ -131,11 +131,7 @@ var SqliteStore = class {
131
131
  if (raw == null) return null;
132
132
  const parsed = parse_json(raw);
133
133
  if (this._resolve_pii_key && typeof parsed === "string") {
134
- return await (0, import_act_crypto.decrypt)(
135
- parsed,
136
- this._resolve_pii_key,
137
- import_act.dateReviver
138
- );
134
+ return await (0, import_act_crypto.decrypt)(parsed, this._resolve_pii_key);
139
135
  }
140
136
  return parsed;
141
137
  }
@@ -215,14 +211,38 @@ var SqliteStore = class {
215
211
  SET correlated_at = (SELECT COALESCE(MAX(id), -1) FROM events)
216
212
  WHERE correlated_at IS NULL`
217
213
  );
218
- await this.client.execute(`
219
- CREATE TABLE IF NOT EXISTS correlated (
220
- id INTEGER PRIMARY KEY CHECK (id = 0),
221
- at INTEGER NOT NULL DEFAULT -1
222
- )
223
- `);
214
+ const correlated_columns = await this.client.execute(
215
+ "PRAGMA table_info(correlated)"
216
+ );
217
+ const legacy = correlated_columns.rows.some((r) => r.name === "id");
218
+ if (legacy) {
219
+ await this.client.execute(`
220
+ CREATE TABLE IF NOT EXISTS correlated_keyed (
221
+ key TEXT PRIMARY KEY,
222
+ at INTEGER NOT NULL DEFAULT -1,
223
+ leased_by TEXT,
224
+ leased_until INTEGER
225
+ )
226
+ `);
227
+ await this.client.execute(
228
+ "INSERT OR IGNORE INTO correlated_keyed (key, at) SELECT '', at FROM correlated WHERE id = 0"
229
+ );
230
+ await this.client.execute("DROP TABLE correlated");
231
+ await this.client.execute(
232
+ "ALTER TABLE correlated_keyed RENAME TO correlated"
233
+ );
234
+ } else {
235
+ await this.client.execute(`
236
+ CREATE TABLE IF NOT EXISTS correlated (
237
+ key TEXT PRIMARY KEY,
238
+ at INTEGER NOT NULL DEFAULT -1,
239
+ leased_by TEXT,
240
+ leased_until INTEGER
241
+ )
242
+ `);
243
+ }
224
244
  await this.client.execute(
225
- "INSERT OR IGNORE INTO correlated (id) VALUES (0)"
245
+ "INSERT OR IGNORE INTO correlated (key) VALUES ('')"
226
246
  );
227
247
  await this.client.execute(
228
248
  "CREATE INDEX IF NOT EXISTS idx_streams_claim ON streams(blocked, priority DESC, at)"
@@ -380,7 +400,7 @@ var SqliteStore = class {
380
400
  // plus a UPDATE pass to keep the *max* priority across reactions
381
401
  // targeting the same stream (ACT-102). Operator overrides go
382
402
  // through `prioritize()` instead.
383
- async subscribe(streams, correlated_at) {
403
+ async subscribe(streams, correlated_at, correlator) {
384
404
  for (const { source } of streams) {
385
405
  if (source !== void 0 && !(0, import_act.is_literal_source)(source))
386
406
  streamPatternToGlob(source);
@@ -420,19 +440,64 @@ var SqliteStore = class {
420
440
  const wm = await tx.execute(
421
441
  "SELECT COALESCE(MAX(at), -1) as w FROM streams"
422
442
  );
423
- if (correlated_at !== void 0)
443
+ let correlating;
444
+ if (correlator) {
445
+ const now = Date.now();
446
+ const taken = await tx.execute({
447
+ sql: `INSERT INTO correlated (key, at, leased_by, leased_until)
448
+ SELECT ?,
449
+ MAX(COALESCE(?, -1),
450
+ COALESCE((SELECT at FROM correlated WHERE key = ''), -1)),
451
+ ?, ?
452
+ ON CONFLICT (key) DO UPDATE
453
+ SET at = MAX(correlated.at, COALESCE(?, -1)),
454
+ leased_by = excluded.leased_by,
455
+ leased_until = excluded.leased_until
456
+ WHERE correlated.leased_until IS NULL
457
+ OR correlated.leased_until < ?
458
+ OR correlated.leased_by = excluded.leased_by`,
459
+ args: [
460
+ correlator.key,
461
+ correlated_at ?? null,
462
+ correlator.by,
463
+ // `millis <= 0` releases outright rather than setting an expiry a
464
+ // hair in the future, which would still refuse a successor asking
465
+ // in the same instant.
466
+ correlator.millis > 0 ? now + correlator.millis : null,
467
+ correlated_at ?? null,
468
+ now
469
+ ]
470
+ });
471
+ correlating = taken.rowsAffected > 0;
472
+ if (!correlating && correlated_at !== void 0)
473
+ await tx.execute({
474
+ sql: "UPDATE correlated SET at = MAX(at, ?) WHERE key = ?",
475
+ args: [correlated_at, correlator.key]
476
+ });
477
+ if (correlated_at !== void 0)
478
+ await tx.execute({
479
+ sql: "UPDATE correlated SET at = MAX(at, ?) WHERE key = ''",
480
+ args: [correlated_at]
481
+ });
482
+ } else if (correlated_at !== void 0)
424
483
  await tx.execute({
425
- sql: "UPDATE correlated SET at = MAX(at, ?) WHERE id = 0",
484
+ sql: "UPDATE correlated SET at = MAX(at, ?) WHERE key = ''",
426
485
  args: [correlated_at]
427
486
  });
428
- const cp = await tx.execute("SELECT at FROM correlated WHERE id = 0");
487
+ const cp = await tx.execute({
488
+ sql: "SELECT at FROM correlated WHERE key = ?",
489
+ args: [correlator?.key ?? ""]
490
+ });
429
491
  await tx.commit();
430
492
  return {
431
493
  subscribed,
432
494
  watermark: Number(wm.rows[0].w),
433
- // `seed()` always inserts the singleton row, so this read cannot
434
- // come back empty — no defensive fallback to leave untested.
435
- correlated_at: Number(cp.rows[0].at)
495
+ // `seed()` always inserts the shared row, and a correlator's row is
496
+ // created by the upsert above — or already existed, which is why the
497
+ // lease was refused. Either way this read cannot come back empty, so
498
+ // there is no defensive fallback to leave untested.
499
+ correlated_at: Number(cp.rows[0].at),
500
+ ...correlating === void 0 ? {} : { correlating }
436
501
  };
437
502
  } catch (e) {
438
503
  await tx.rollback();