@rotorsoft/act-pg 0.18.5 → 0.19.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.cjs DELETED
@@ -1,682 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- PostgresStore: () => PostgresStore
34
- });
35
- module.exports = __toCommonJS(index_exports);
36
-
37
- // src/PostgresStore.ts
38
- var import_act = require("@rotorsoft/act");
39
- var import_pg = __toESM(require("pg"), 1);
40
-
41
- // src/utils.ts
42
- var ISO_8601 = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(\.\d+)?(Z|[+-][0-2][0-9]:[0-5][0-9])?$/;
43
- var dateReviver = (_key, value) => {
44
- if (typeof value === "string" && ISO_8601.test(value)) {
45
- return new Date(value);
46
- }
47
- return value;
48
- };
49
-
50
- // src/PostgresStore.ts
51
- var logger = (0, import_act.log)();
52
- var { Pool, types } = import_pg.default;
53
- types.setTypeParser(
54
- types.builtins.JSONB,
55
- (val) => JSON.parse(val, dateReviver)
56
- );
57
- var SAFE_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
58
- var PG_UNIQUE_VIOLATION = "23505";
59
- function assertSafeIdentifier(value, label) {
60
- if (!SAFE_IDENTIFIER.test(value))
61
- throw new Error(`Unsafe SQL identifier for ${label}: "${value}"`);
62
- }
63
- var DEFAULT_CONFIG = {
64
- host: "localhost",
65
- port: 5432,
66
- database: "postgres",
67
- user: "postgres",
68
- password: "postgres",
69
- schema: "public",
70
- table: "events"
71
- };
72
- var PostgresStore = class {
73
- _pool;
74
- config;
75
- _fqt;
76
- _fqs;
77
- /**
78
- * Create a new PostgresStore instance.
79
- * @param config Partial configuration (host, port, user, password, schema, table, etc.)
80
- */
81
- constructor(config = {}) {
82
- this.config = { ...DEFAULT_CONFIG, ...config };
83
- assertSafeIdentifier(this.config.schema, "schema");
84
- assertSafeIdentifier(this.config.table, "table");
85
- const { schema: _, table: __, ...poolConfig } = this.config;
86
- this._pool = new Pool(poolConfig);
87
- this._fqt = `"${this.config.schema}"."${this.config.table}"`;
88
- this._fqs = `"${this.config.schema}"."${this.config.table}_streams"`;
89
- }
90
- /**
91
- * Dispose of the store and close all database connections.
92
- * @returns Promise that resolves when all connections are closed
93
- */
94
- async dispose() {
95
- await this._pool.end();
96
- }
97
- /**
98
- * Seed the database with required tables, indexes, and schema for event storage.
99
- * @returns Promise that resolves when seeding is complete
100
- * @throws Error if seeding fails
101
- */
102
- async seed() {
103
- const client = await this._pool.connect();
104
- try {
105
- await client.query("BEGIN");
106
- await client.query(
107
- `CREATE SCHEMA IF NOT EXISTS "${this.config.schema}";`
108
- );
109
- await client.query(
110
- `CREATE TABLE IF NOT EXISTS ${this._fqt} (
111
- id serial PRIMARY KEY,
112
- name varchar(100) COLLATE pg_catalog."default" NOT NULL,
113
- data jsonb,
114
- stream varchar(100) COLLATE pg_catalog."default" NOT NULL,
115
- version int NOT NULL,
116
- created timestamptz NOT NULL DEFAULT now(),
117
- meta jsonb
118
- ) TABLESPACE pg_default;`
119
- );
120
- await client.query(
121
- `CREATE UNIQUE INDEX IF NOT EXISTS "${this.config.table}_stream_ix"
122
- ON ${this._fqt} (stream COLLATE pg_catalog."default", version);`
123
- );
124
- await client.query(
125
- `CREATE INDEX IF NOT EXISTS "${this.config.table}_name_ix"
126
- ON ${this._fqt} (name COLLATE pg_catalog."default");`
127
- );
128
- await client.query(
129
- `CREATE INDEX IF NOT EXISTS "${this.config.table}_created_id_ix"
130
- ON ${this._fqt} (created, id);`
131
- );
132
- await client.query(
133
- `CREATE INDEX IF NOT EXISTS "${this.config.table}_correlation_ix"
134
- ON ${this._fqt} ((meta ->> 'correlation') COLLATE pg_catalog."default");`
135
- );
136
- await client.query(
137
- `CREATE TABLE IF NOT EXISTS ${this._fqs} (
138
- stream varchar(100) COLLATE pg_catalog."default" PRIMARY KEY,
139
- source varchar(100) COLLATE pg_catalog."default",
140
- at int NOT NULL DEFAULT -1,
141
- retry smallint NOT NULL DEFAULT 0,
142
- blocked boolean NOT NULL DEFAULT false,
143
- error text,
144
- leased_by text,
145
- leased_until timestamptz
146
- ) TABLESPACE pg_default;`
147
- );
148
- await client.query(
149
- `CREATE INDEX IF NOT EXISTS "${this.config.table}_streams_fetch_ix"
150
- ON ${this._fqs} (blocked, at);`
151
- );
152
- await client.query("COMMIT");
153
- logger.info(
154
- `Seeded schema "${this.config.schema}" with table "${this.config.table}"`
155
- );
156
- } catch (error) {
157
- await client.query("ROLLBACK");
158
- logger.error(error);
159
- throw error;
160
- } finally {
161
- client.release();
162
- }
163
- }
164
- /**
165
- * Drop all tables and schema created by the store (for testing or cleanup).
166
- * @returns Promise that resolves when the schema is dropped
167
- */
168
- async drop() {
169
- await this._pool.query(
170
- `
171
- DO $$
172
- BEGIN
173
- IF EXISTS (SELECT 1 FROM information_schema.schemata
174
- WHERE schema_name = '${this.config.schema}'
175
- ) THEN
176
- EXECUTE 'DROP TABLE IF EXISTS ${this._fqt}';
177
- EXECUTE 'DROP TABLE IF EXISTS ${this._fqs}';
178
- IF '${this.config.schema}' <> 'public' THEN
179
- EXECUTE 'DROP SCHEMA "${this.config.schema}" CASCADE';
180
- END IF;
181
- END IF;
182
- END
183
- $$;
184
- `
185
- );
186
- }
187
- /**
188
- * Query events from the store, optionally filtered by stream, event name, time, etc.
189
- *
190
- * @param callback Function called for each event found
191
- * @param query (Optional) Query filter (stream, names, before, after, etc.)
192
- * @returns The number of events found
193
- *
194
- * @example
195
- * await store.query((event) => console.log(event), { stream: "A" });
196
- */
197
- async query(callback, query) {
198
- const {
199
- stream,
200
- names,
201
- before,
202
- after,
203
- limit,
204
- created_before,
205
- created_after,
206
- backward,
207
- correlation,
208
- with_snaps = false
209
- } = query || {};
210
- let sql = `SELECT * FROM ${this._fqt}`;
211
- const conditions = [];
212
- const values = [];
213
- if (query) {
214
- if (typeof after !== "undefined") {
215
- values.push(after);
216
- conditions.push(`id>$${values.length}`);
217
- } else {
218
- conditions.push("id>-1");
219
- }
220
- if (stream) {
221
- values.push(stream);
222
- conditions.push(
223
- query.stream_exact ? `stream = $${values.length}` : `stream ~ $${values.length}`
224
- );
225
- }
226
- if (names?.length) {
227
- values.push(names);
228
- conditions.push(`name = ANY($${values.length})`);
229
- }
230
- if (before) {
231
- values.push(before);
232
- conditions.push(`id<$${values.length}`);
233
- }
234
- if (created_after) {
235
- values.push(created_after.toISOString());
236
- conditions.push(`created>$${values.length}`);
237
- }
238
- if (created_before) {
239
- values.push(created_before.toISOString());
240
- conditions.push(`created<$${values.length}`);
241
- }
242
- if (correlation) {
243
- values.push(correlation);
244
- conditions.push(`meta->>'correlation'=$${values.length}`);
245
- }
246
- if (!with_snaps) {
247
- conditions.push(`name <> '${import_act.SNAP_EVENT}'`);
248
- }
249
- }
250
- if (conditions.length) {
251
- sql += " WHERE " + conditions.join(" AND ");
252
- }
253
- sql += ` ORDER BY id ${backward ? "DESC" : "ASC"}`;
254
- if (limit) {
255
- values.push(limit);
256
- sql += ` LIMIT $${values.length}`;
257
- }
258
- const result = await this._pool.query(sql, values);
259
- for (const row of result.rows) callback(row);
260
- return result.rowCount ?? 0;
261
- }
262
- /**
263
- * Commit new events to the store for a given stream, with concurrency control.
264
- *
265
- * @param stream The stream name
266
- * @param msgs Array of messages (event name and data)
267
- * @param meta Event metadata (correlation, causation, etc.)
268
- * @param expectedVersion (Optional) Expected stream version for concurrency control
269
- * @returns Array of committed events
270
- * @throws ConcurrencyError if the expected version does not match
271
- */
272
- async commit(stream, msgs, meta, expectedVersion) {
273
- if (msgs.length === 0) return [];
274
- const client = await this._pool.connect();
275
- let version = -1;
276
- try {
277
- await client.query("BEGIN");
278
- const last = await client.query(
279
- `SELECT version
280
- FROM ${this._fqt}
281
- WHERE stream=$1 ORDER BY version DESC LIMIT 1`,
282
- [stream]
283
- );
284
- version = last.rowCount ? last.rows[0].version : -1;
285
- if (typeof expectedVersion === "number" && version !== expectedVersion)
286
- throw new import_act.ConcurrencyError(
287
- stream,
288
- version,
289
- msgs,
290
- expectedVersion
291
- );
292
- const committed = [];
293
- for (const { name, data } of msgs) {
294
- version++;
295
- const sql = `
296
- INSERT INTO ${this._fqt}(name, data, stream, version, meta)
297
- VALUES($1, $2, $3, $4, $5) RETURNING *`;
298
- const vals = [name, data, stream, version, meta];
299
- try {
300
- const { rows } = await client.query(sql, vals);
301
- committed.push(rows.at(0));
302
- } catch (error) {
303
- if (error?.code === PG_UNIQUE_VIOLATION) {
304
- throw new import_act.ConcurrencyError(
305
- stream,
306
- version - 1,
307
- msgs,
308
- expectedVersion ?? -1
309
- );
310
- }
311
- throw error;
312
- }
313
- }
314
- await client.query(
315
- `
316
- NOTIFY "${this.config.table}", '${JSON.stringify({
317
- operation: "INSERT",
318
- id: committed[0].name,
319
- position: committed[0].id
320
- })}';
321
- COMMIT;
322
- `
323
- ).catch((error) => {
324
- logger.error(error);
325
- throw new import_act.ConcurrencyError(
326
- stream,
327
- version,
328
- msgs,
329
- expectedVersion || -1
330
- );
331
- });
332
- return committed;
333
- } catch (error) {
334
- await client.query("ROLLBACK").catch(() => {
335
- });
336
- throw error;
337
- } finally {
338
- client.release();
339
- }
340
- }
341
- /**
342
- * Atomically discovers and leases streams for reaction processing.
343
- *
344
- * Uses `FOR UPDATE SKIP LOCKED` to implement zero-contention competing consumers:
345
- * - Workers never block each other — locked rows are silently skipped
346
- * - Discovery and locking happen in a single atomic transaction
347
- * - No wasted polls — every returned stream is exclusively owned
348
- *
349
- * @param lagging - Max streams from lagging frontier (ascending watermark)
350
- * @param leading - Max streams from leading frontier (descending watermark)
351
- * @param by - Lease holder identifier (UUID)
352
- * @param millis - Lease duration in milliseconds
353
- * @returns Leased streams with metadata
354
- */
355
- async claim(lagging, leading, by, millis) {
356
- const client = await this._pool.connect();
357
- try {
358
- await client.query("BEGIN");
359
- const { rows } = await client.query(
360
- `
361
- WITH
362
- available AS (
363
- SELECT stream, source, at
364
- FROM ${this._fqs} s
365
- WHERE blocked = false
366
- AND (leased_by IS NULL OR leased_until <= NOW())
367
- AND (s.at < 0 OR EXISTS (
368
- SELECT 1 FROM ${this._fqt} e
369
- WHERE e.id > s.at
370
- AND e.name <> '${import_act.SNAP_EVENT}'
371
- AND (s.source IS NULL OR e.stream = COALESCE(s.source, s.stream))
372
- LIMIT 1
373
- ))
374
- FOR UPDATE SKIP LOCKED
375
- ),
376
- lag AS (
377
- SELECT stream, source, at, TRUE AS lagging
378
- FROM available
379
- ORDER BY at ASC
380
- LIMIT $1
381
- ),
382
- lead AS (
383
- SELECT stream, source, at, FALSE AS lagging
384
- FROM available
385
- ORDER BY at DESC
386
- LIMIT $2
387
- ),
388
- combined AS (
389
- SELECT DISTINCT ON (stream) stream, source, at, lagging
390
- FROM (SELECT * FROM lag UNION ALL SELECT * FROM lead) t
391
- ORDER BY stream, at
392
- )
393
- UPDATE ${this._fqs} s
394
- SET
395
- leased_by = $3,
396
- leased_until = NOW() + ($4::integer || ' milliseconds')::interval,
397
- retry = s.retry + 1
398
- FROM combined c
399
- WHERE s.stream = c.stream
400
- RETURNING s.stream, s.source, s.at, s.retry, c.lagging
401
- `,
402
- [lagging, leading, by, millis]
403
- );
404
- await client.query("COMMIT");
405
- return rows.map(({ stream, source, at, retry, lagging: lagging2 }) => ({
406
- stream,
407
- source: source ?? void 0,
408
- at,
409
- by,
410
- retry,
411
- lagging: lagging2
412
- }));
413
- } catch (error) {
414
- await client.query("ROLLBACK").catch(() => {
415
- });
416
- logger.error(error);
417
- return [];
418
- } finally {
419
- client.release();
420
- }
421
- }
422
- /**
423
- * Registers streams for event processing.
424
- * Upserts stream entries so they become visible to claim().
425
- * Also returns the current max watermark across all subscriptions.
426
- * @param streams - Streams to register with optional source.
427
- * @returns subscribed count and current max watermark.
428
- */
429
- async subscribe(streams) {
430
- const client = await this._pool.connect();
431
- try {
432
- await client.query("BEGIN");
433
- let subscribed = 0;
434
- if (streams.length) {
435
- const { rowCount } = await client.query(
436
- `
437
- INSERT INTO ${this._fqs} (stream, source)
438
- SELECT s->>'stream', s->>'source'
439
- FROM jsonb_array_elements($1::jsonb) AS s
440
- ON CONFLICT (stream) DO NOTHING
441
- `,
442
- [JSON.stringify(streams)]
443
- );
444
- subscribed = rowCount ?? 0;
445
- }
446
- const { rows } = await client.query(
447
- `SELECT COALESCE(MAX(at), -1) AS max FROM ${this._fqs}`
448
- );
449
- await client.query("COMMIT");
450
- return { subscribed, watermark: rows[0]?.max ?? -1 };
451
- } catch (error) {
452
- await client.query("ROLLBACK").catch(() => {
453
- });
454
- logger.error(error);
455
- return { subscribed: 0, watermark: -1 };
456
- } finally {
457
- client.release();
458
- }
459
- }
460
- /**
461
- * Acknowledge and release leases after processing, updating stream positions.
462
- *
463
- * @param leases - Leases to acknowledge, including last processed watermark and lease holder.
464
- * @returns Acked leases.
465
- */
466
- async ack(leases) {
467
- const client = await this._pool.connect();
468
- try {
469
- await client.query("BEGIN");
470
- const { rows } = await client.query(
471
- `
472
- WITH input AS (
473
- SELECT * FROM jsonb_to_recordset($1::jsonb)
474
- AS x(stream text, by text, at int, lagging boolean)
475
- )
476
- UPDATE ${this._fqs} AS s
477
- SET
478
- at = i.at,
479
- retry = -1,
480
- leased_by = NULL,
481
- leased_until = NULL
482
- FROM input i
483
- WHERE s.stream = i.stream AND s.leased_by = i.by
484
- RETURNING s.stream, s.source, s.at, i.by, s.retry, i.lagging
485
- `,
486
- [JSON.stringify(leases)]
487
- );
488
- await client.query("COMMIT");
489
- return rows.map((row) => ({
490
- stream: row.stream,
491
- source: row.source ?? void 0,
492
- at: row.at,
493
- by: row.by,
494
- retry: row.retry,
495
- lagging: row.lagging
496
- }));
497
- } catch (error) {
498
- await client.query("ROLLBACK").catch(() => {
499
- });
500
- logger.error(error);
501
- return [];
502
- } finally {
503
- client.release();
504
- }
505
- }
506
- /**
507
- * Block a stream for processing after failing to process and reaching max retries with blocking enabled.
508
- * @param leases - Leases to block, including lease holder and last error message.
509
- * @returns Blocked leases.
510
- */
511
- async block(leases) {
512
- const client = await this._pool.connect();
513
- try {
514
- await client.query("BEGIN");
515
- const { rows } = await client.query(
516
- `
517
- WITH input AS (
518
- SELECT * FROM jsonb_to_recordset($1::jsonb)
519
- AS x(stream text, by text, error text, lagging boolean)
520
- )
521
- UPDATE ${this._fqs} AS s
522
- SET blocked = true, error = i.error
523
- FROM input i
524
- WHERE s.stream = i.stream AND s.leased_by = i.by AND s.blocked = false
525
- RETURNING s.stream, s.source, s.at, i.by, s.retry, s.error, i.lagging
526
- `,
527
- [JSON.stringify(leases)]
528
- );
529
- await client.query("COMMIT");
530
- return rows.map((row) => ({
531
- stream: row.stream,
532
- source: row.source ?? void 0,
533
- at: row.at,
534
- by: row.by,
535
- retry: row.retry,
536
- lagging: row.lagging,
537
- error: row.error
538
- }));
539
- } catch (error) {
540
- await client.query("ROLLBACK").catch(() => {
541
- });
542
- logger.error(error);
543
- return [];
544
- } finally {
545
- client.release();
546
- }
547
- }
548
- /**
549
- * Reset watermarks for the given streams to -1, clearing retry, blocked,
550
- * error, and lease state so they can be replayed from the beginning.
551
- * @param streams - Stream names to reset.
552
- * @returns Count of streams that were actually reset.
553
- */
554
- async reset(streams) {
555
- if (!streams.length) return 0;
556
- const { rowCount } = await this._pool.query(
557
- `UPDATE ${this._fqs}
558
- SET at = -1, retry = 0, blocked = false, error = NULL,
559
- leased_by = NULL, leased_until = NULL
560
- WHERE stream = ANY($1)`,
561
- [streams]
562
- );
563
- return rowCount ?? 0;
564
- }
565
- /**
566
- * Streams subscription positions to a callback, ordered by stream name,
567
- * along with the highest event id in the store.
568
- *
569
- * Filters (`stream`, `source`, `blocked`, `after`, `limit`) are applied
570
- * server-side. `stream`/`source` are regex by default (`~`), or exact
571
- * with `*_exact: true` — same convention as {@link Store.query}.
572
- *
573
- * @returns `maxEventId` and the `count` of positions emitted.
574
- */
575
- async query_streams(callback, query) {
576
- const limit = query?.limit ?? 100;
577
- const conditions = [];
578
- const values = [];
579
- if (query?.stream !== void 0) {
580
- values.push(query.stream);
581
- conditions.push(
582
- query.stream_exact ? `stream = $${values.length}` : `stream ~ $${values.length}`
583
- );
584
- }
585
- if (query?.source !== void 0) {
586
- conditions.push(`source IS NOT NULL`);
587
- values.push(query.source);
588
- conditions.push(
589
- query.source_exact ? `source = $${values.length}` : `source ~ $${values.length}`
590
- );
591
- }
592
- if (query?.blocked !== void 0) {
593
- values.push(query.blocked);
594
- conditions.push(`blocked = $${values.length}`);
595
- }
596
- if (query?.after !== void 0) {
597
- values.push(query.after);
598
- conditions.push(`stream > $${values.length}`);
599
- }
600
- let sql = `SELECT stream, source, at, retry, blocked, error, leased_by, leased_until FROM ${this._fqs}`;
601
- if (conditions.length) sql += " WHERE " + conditions.join(" AND ");
602
- values.push(limit);
603
- sql += ` ORDER BY stream LIMIT $${values.length}`;
604
- const client = await this._pool.connect();
605
- try {
606
- const [streamsResult, maxResult] = await Promise.all([
607
- client.query(sql, values),
608
- client.query(
609
- `SELECT COALESCE(MAX(id), -1) AS m FROM ${this._fqt}`
610
- )
611
- ]);
612
- let count = 0;
613
- for (const row of streamsResult.rows) {
614
- callback({
615
- stream: row.stream,
616
- source: row.source ?? void 0,
617
- at: row.at,
618
- retry: row.retry,
619
- blocked: row.blocked,
620
- error: row.error ?? "",
621
- leased_by: row.leased_by ?? void 0,
622
- leased_until: row.leased_until ?? void 0
623
- });
624
- count++;
625
- }
626
- return { maxEventId: Number(maxResult.rows[0].m), count };
627
- } finally {
628
- client.release();
629
- }
630
- }
631
- /**
632
- * Atomically truncates streams and seeds each with a snapshot or tombstone.
633
- * @param targets - Streams to truncate with optional snapshot state and meta.
634
- * @returns Map keyed by stream name, each entry with `deleted` count and `committed` event.
635
- */
636
- async truncate(targets) {
637
- if (!targets.length) return /* @__PURE__ */ new Map();
638
- const streams = targets.map((t) => t.stream);
639
- const client = await this._pool.connect();
640
- try {
641
- await client.query("BEGIN");
642
- await client.query(`DELETE FROM ${this._fqs} WHERE stream = ANY($1)`, [
643
- streams
644
- ]);
645
- const result = /* @__PURE__ */ new Map();
646
- for (const { stream, snapshot, meta } of targets) {
647
- const { rowCount } = await client.query(
648
- `DELETE FROM ${this._fqt} WHERE stream = $1`,
649
- [stream]
650
- );
651
- const name = snapshot !== void 0 ? import_act.SNAP_EVENT : import_act.TOMBSTONE_EVENT;
652
- const { rows } = await client.query(
653
- `INSERT INTO ${this._fqt}(name, data, stream, version, created, meta)
654
- VALUES($1, $2, $3, 0, now(), $4) RETURNING *`,
655
- [
656
- name,
657
- snapshot ?? {},
658
- stream,
659
- meta ?? { correlation: "", causation: {} }
660
- ]
661
- );
662
- result.set(stream, {
663
- deleted: rowCount ?? 0,
664
- committed: rows[0]
665
- });
666
- }
667
- await client.query("COMMIT");
668
- return result;
669
- } catch (error) {
670
- await client.query("ROLLBACK").catch(() => {
671
- });
672
- throw error;
673
- } finally {
674
- client.release();
675
- }
676
- }
677
- };
678
- // Annotate the CommonJS export names for ESM import in node:
679
- 0 && (module.exports = {
680
- PostgresStore
681
- });
682
- //# sourceMappingURL=index.cjs.map