@rotorsoft/act-pg 0.2.0 → 0.4.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/.tsbuildinfo +1 -1
- package/dist/{PostgresStore.d.ts → @types/PostgresStore.d.ts} +13 -3
- package/dist/@types/PostgresStore.d.ts.map +1 -0
- package/dist/@types/index.d.ts +3 -0
- package/dist/@types/index.d.ts.map +1 -0
- package/dist/@types/utils.d.ts.map +1 -0
- package/dist/index.cjs +369 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +331 -3
- package/dist/index.js.map +1 -1
- package/package.json +19 -7
- package/dist/PostgresStore.d.ts.map +0 -1
- package/dist/PostgresStore.js +0 -208
- package/dist/PostgresStore.js.map +0 -1
- package/dist/config.d.ts +0 -24
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -21
- package/dist/config.js.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/seed.d.ts +0 -2
- package/dist/seed.d.ts.map +0 -1
- package/dist/seed.js +0 -46
- package/dist/seed.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -19
- package/dist/utils.js.map +0 -1
- /package/dist/{utils.d.ts → @types/utils.d.ts} +0 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
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 { Pool, types } = import_pg.default;
|
|
52
|
+
types.setTypeParser(
|
|
53
|
+
types.builtins.JSONB,
|
|
54
|
+
(val) => JSON.parse(val, dateReviver)
|
|
55
|
+
);
|
|
56
|
+
var DEFAULT_CONFIG = {
|
|
57
|
+
host: "localhost",
|
|
58
|
+
port: 5432,
|
|
59
|
+
database: "postgres",
|
|
60
|
+
user: "postgres",
|
|
61
|
+
password: "postgres",
|
|
62
|
+
schema: "public",
|
|
63
|
+
table: "events",
|
|
64
|
+
leaseMillis: 3e4
|
|
65
|
+
};
|
|
66
|
+
var PostgresStore = class {
|
|
67
|
+
_pool;
|
|
68
|
+
config;
|
|
69
|
+
constructor(config = {}) {
|
|
70
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
71
|
+
this._pool = new Pool(this.config);
|
|
72
|
+
}
|
|
73
|
+
async dispose() {
|
|
74
|
+
await this._pool.end();
|
|
75
|
+
}
|
|
76
|
+
async seed() {
|
|
77
|
+
const client = await this._pool.connect();
|
|
78
|
+
try {
|
|
79
|
+
await client.query("BEGIN");
|
|
80
|
+
await client.query(
|
|
81
|
+
`CREATE SCHEMA IF NOT EXISTS "${this.config.schema}";`
|
|
82
|
+
);
|
|
83
|
+
await client.query(
|
|
84
|
+
`CREATE TABLE IF NOT EXISTS "${this.config.schema}"."${this.config.table}" (
|
|
85
|
+
id serial PRIMARY KEY,
|
|
86
|
+
name varchar(100) COLLATE pg_catalog."default" NOT NULL,
|
|
87
|
+
data jsonb,
|
|
88
|
+
stream varchar(100) COLLATE pg_catalog."default" NOT NULL,
|
|
89
|
+
version int NOT NULL,
|
|
90
|
+
created timestamptz NOT NULL DEFAULT now(),
|
|
91
|
+
meta jsonb
|
|
92
|
+
) TABLESPACE pg_default;`
|
|
93
|
+
);
|
|
94
|
+
await client.query(
|
|
95
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "${this.config.table}_stream_ix"
|
|
96
|
+
ON "${this.config.schema}"."${this.config.table}" (stream COLLATE pg_catalog."default", version);`
|
|
97
|
+
);
|
|
98
|
+
await client.query(
|
|
99
|
+
`CREATE INDEX IF NOT EXISTS "${this.config.table}_name_ix"
|
|
100
|
+
ON "${this.config.schema}"."${this.config.table}" (name COLLATE pg_catalog."default");`
|
|
101
|
+
);
|
|
102
|
+
await client.query(
|
|
103
|
+
`CREATE INDEX IF NOT EXISTS "${this.config.table}_created_id_ix"
|
|
104
|
+
ON "${this.config.schema}"."${this.config.table}" (created, id);`
|
|
105
|
+
);
|
|
106
|
+
await client.query(
|
|
107
|
+
`CREATE INDEX IF NOT EXISTS "${this.config.table}_correlation_ix"
|
|
108
|
+
ON "${this.config.schema}"."${this.config.table}" ((meta ->> 'correlation') COLLATE pg_catalog."default");`
|
|
109
|
+
);
|
|
110
|
+
await client.query(
|
|
111
|
+
`CREATE TABLE IF NOT EXISTS "${this.config.schema}"."${this.config.table}_streams" (
|
|
112
|
+
stream varchar(100) COLLATE pg_catalog."default" PRIMARY KEY,
|
|
113
|
+
at int NOT NULL DEFAULT -1,
|
|
114
|
+
retry smallint NOT NULL DEFAULT 0,
|
|
115
|
+
blocked boolean NOT NULL DEFAULT false,
|
|
116
|
+
leased_at int,
|
|
117
|
+
leased_by uuid,
|
|
118
|
+
leased_until timestamptz
|
|
119
|
+
) TABLESPACE pg_default;`
|
|
120
|
+
);
|
|
121
|
+
await client.query(
|
|
122
|
+
`CREATE INDEX IF NOT EXISTS "${this.config.table}_streams_fetch_ix"
|
|
123
|
+
ON "${this.config.schema}"."${this.config.table}_streams" (blocked, at);`
|
|
124
|
+
);
|
|
125
|
+
await client.query("COMMIT");
|
|
126
|
+
import_act.logger.info(
|
|
127
|
+
`Seeded schema "${this.config.schema}" with table "${this.config.table}"`
|
|
128
|
+
);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
await client.query("ROLLBACK");
|
|
131
|
+
import_act.logger.error("Failed to seed store:", error);
|
|
132
|
+
throw error;
|
|
133
|
+
} finally {
|
|
134
|
+
client.release();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async drop() {
|
|
138
|
+
await this._pool.query(
|
|
139
|
+
`
|
|
140
|
+
DO $$
|
|
141
|
+
BEGIN
|
|
142
|
+
IF EXISTS (SELECT 1 FROM information_schema.schemata
|
|
143
|
+
WHERE schema_name = '${this.config.schema}'
|
|
144
|
+
) THEN
|
|
145
|
+
EXECUTE 'DROP TABLE IF EXISTS "${this.config.schema}"."${this.config.table}"';
|
|
146
|
+
EXECUTE 'DROP TABLE IF EXISTS "${this.config.schema}"."${this.config.table}_streams"';
|
|
147
|
+
IF '${this.config.schema}' <> 'public' THEN
|
|
148
|
+
EXECUTE 'DROP SCHEMA "${this.config.schema}" CASCADE';
|
|
149
|
+
END IF;
|
|
150
|
+
END IF;
|
|
151
|
+
END
|
|
152
|
+
$$;
|
|
153
|
+
`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
async query(callback, query, withSnaps = false) {
|
|
157
|
+
const {
|
|
158
|
+
stream,
|
|
159
|
+
names,
|
|
160
|
+
before,
|
|
161
|
+
after,
|
|
162
|
+
limit,
|
|
163
|
+
created_before,
|
|
164
|
+
created_after,
|
|
165
|
+
backward,
|
|
166
|
+
correlation
|
|
167
|
+
} = query || {};
|
|
168
|
+
let sql = `SELECT * FROM "${this.config.schema}"."${this.config.table}" WHERE`;
|
|
169
|
+
const values = [];
|
|
170
|
+
if (withSnaps)
|
|
171
|
+
sql = sql.concat(
|
|
172
|
+
` id>=COALESCE((SELECT id
|
|
173
|
+
FROM "${this.config.schema}"."${this.config.table}"
|
|
174
|
+
WHERE stream='${stream}' AND name='${import_act.SNAP_EVENT}'
|
|
175
|
+
ORDER BY id DESC LIMIT 1), 0)
|
|
176
|
+
AND stream='${stream}'`
|
|
177
|
+
);
|
|
178
|
+
else if (query) {
|
|
179
|
+
if (typeof after !== "undefined") {
|
|
180
|
+
values.push(after);
|
|
181
|
+
sql = sql.concat(" id>$1");
|
|
182
|
+
} else sql = sql.concat(" id>-1");
|
|
183
|
+
if (stream) {
|
|
184
|
+
values.push(stream);
|
|
185
|
+
sql = sql.concat(` AND stream=$${values.length}`);
|
|
186
|
+
}
|
|
187
|
+
if (names && names.length) {
|
|
188
|
+
values.push(names);
|
|
189
|
+
sql = sql.concat(` AND name = ANY($${values.length})`);
|
|
190
|
+
}
|
|
191
|
+
if (before) {
|
|
192
|
+
values.push(before);
|
|
193
|
+
sql = sql.concat(` AND id<$${values.length}`);
|
|
194
|
+
}
|
|
195
|
+
if (created_after) {
|
|
196
|
+
values.push(created_after.toISOString());
|
|
197
|
+
sql = sql.concat(` AND created>$${values.length}`);
|
|
198
|
+
}
|
|
199
|
+
if (created_before) {
|
|
200
|
+
values.push(created_before.toISOString());
|
|
201
|
+
sql = sql.concat(` AND created<$${values.length}`);
|
|
202
|
+
}
|
|
203
|
+
if (correlation) {
|
|
204
|
+
values.push(correlation);
|
|
205
|
+
sql = sql.concat(` AND meta->>'correlation'=$${values.length}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
sql = sql.concat(` ORDER BY id ${backward ? "DESC" : "ASC"}`);
|
|
209
|
+
if (limit) {
|
|
210
|
+
values.push(limit);
|
|
211
|
+
sql = sql.concat(` LIMIT $${values.length}`);
|
|
212
|
+
}
|
|
213
|
+
const result = await this._pool.query(sql, values);
|
|
214
|
+
for (const row of result.rows) callback(row);
|
|
215
|
+
return result.rowCount ?? 0;
|
|
216
|
+
}
|
|
217
|
+
async commit(stream, msgs, meta, expectedVersion) {
|
|
218
|
+
const client = await this._pool.connect();
|
|
219
|
+
let version = -1;
|
|
220
|
+
try {
|
|
221
|
+
await client.query("BEGIN");
|
|
222
|
+
const last = await client.query(
|
|
223
|
+
`SELECT version
|
|
224
|
+
FROM "${this.config.schema}"."${this.config.table}"
|
|
225
|
+
WHERE stream=$1 ORDER BY version DESC LIMIT 1`,
|
|
226
|
+
[stream]
|
|
227
|
+
);
|
|
228
|
+
version = last.rowCount ? last.rows[0].version : -1;
|
|
229
|
+
if (expectedVersion && version !== expectedVersion)
|
|
230
|
+
throw new import_act.ConcurrencyError(
|
|
231
|
+
version,
|
|
232
|
+
msgs,
|
|
233
|
+
expectedVersion
|
|
234
|
+
);
|
|
235
|
+
const committed = await Promise.all(
|
|
236
|
+
msgs.map(async ({ name, data }) => {
|
|
237
|
+
version++;
|
|
238
|
+
const sql = `
|
|
239
|
+
INSERT INTO "${this.config.schema}"."${this.config.table}"(name, data, stream, version, meta)
|
|
240
|
+
VALUES($1, $2, $3, $4, $5) RETURNING *`;
|
|
241
|
+
const vals = [name, data, stream, version, meta];
|
|
242
|
+
const { rows } = await client.query(sql, vals);
|
|
243
|
+
return rows.at(0);
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
await client.query(
|
|
247
|
+
`
|
|
248
|
+
NOTIFY "${this.config.table}", '${JSON.stringify({
|
|
249
|
+
operation: "INSERT",
|
|
250
|
+
id: committed[0].name,
|
|
251
|
+
position: committed[0].id
|
|
252
|
+
})}';
|
|
253
|
+
COMMIT;
|
|
254
|
+
`
|
|
255
|
+
).catch((error) => {
|
|
256
|
+
import_act.logger.error(error);
|
|
257
|
+
throw new import_act.ConcurrencyError(
|
|
258
|
+
version,
|
|
259
|
+
msgs,
|
|
260
|
+
expectedVersion || -1
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
return committed;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
await client.query("ROLLBACK").catch(() => {
|
|
266
|
+
});
|
|
267
|
+
throw error;
|
|
268
|
+
} finally {
|
|
269
|
+
client.release();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async fetch(limit) {
|
|
273
|
+
const { rows } = await this._pool.query(
|
|
274
|
+
`
|
|
275
|
+
SELECT stream, at
|
|
276
|
+
FROM "${this.config.schema}"."${this.config.table}_streams"
|
|
277
|
+
WHERE blocked=false
|
|
278
|
+
ORDER BY at ASC
|
|
279
|
+
LIMIT $1::integer
|
|
280
|
+
`,
|
|
281
|
+
[limit]
|
|
282
|
+
);
|
|
283
|
+
const after = rows.length ? rows.reduce((min, r) => Math.min(min, r.at), Number.MAX_SAFE_INTEGER) : -1;
|
|
284
|
+
const events = [];
|
|
285
|
+
await this.query((e) => events.push(e), { after, limit });
|
|
286
|
+
return { streams: rows.map(({ stream }) => stream), events };
|
|
287
|
+
}
|
|
288
|
+
async lease(leases) {
|
|
289
|
+
const { by, at } = leases.at(0);
|
|
290
|
+
const streams = leases.map(({ stream }) => stream);
|
|
291
|
+
const client = await this._pool.connect();
|
|
292
|
+
try {
|
|
293
|
+
await client.query("BEGIN");
|
|
294
|
+
await client.query(
|
|
295
|
+
`
|
|
296
|
+
INSERT INTO "${this.config.schema}"."${this.config.table}_streams" (stream)
|
|
297
|
+
SELECT UNNEST($1::text[])
|
|
298
|
+
ON CONFLICT (stream) DO NOTHING
|
|
299
|
+
`,
|
|
300
|
+
[streams]
|
|
301
|
+
);
|
|
302
|
+
const { rows } = await client.query(
|
|
303
|
+
`
|
|
304
|
+
WITH free AS (
|
|
305
|
+
SELECT * FROM "${this.config.schema}"."${this.config.table}_streams"
|
|
306
|
+
WHERE stream = ANY($1::text[]) AND (leased_by IS NULL OR leased_until <= NOW())
|
|
307
|
+
FOR UPDATE
|
|
308
|
+
)
|
|
309
|
+
UPDATE "${this.config.schema}"."${this.config.table}_streams" U
|
|
310
|
+
SET
|
|
311
|
+
leased_by = $2::uuid,
|
|
312
|
+
leased_at = $3::integer,
|
|
313
|
+
leased_until = NOW() + ($4::integer || ' milliseconds')::interval
|
|
314
|
+
FROM free
|
|
315
|
+
WHERE U.stream = free.stream
|
|
316
|
+
RETURNING U.stream, U.leased_at, U.retry
|
|
317
|
+
`,
|
|
318
|
+
[streams, by, at, this.config.leaseMillis]
|
|
319
|
+
);
|
|
320
|
+
await client.query("COMMIT");
|
|
321
|
+
return rows.map(({ stream, leased_at, retry }) => ({
|
|
322
|
+
stream,
|
|
323
|
+
by,
|
|
324
|
+
at: leased_at,
|
|
325
|
+
retry,
|
|
326
|
+
block: false
|
|
327
|
+
}));
|
|
328
|
+
} catch (error) {
|
|
329
|
+
await client.query("ROLLBACK").catch(() => {
|
|
330
|
+
});
|
|
331
|
+
throw error;
|
|
332
|
+
} finally {
|
|
333
|
+
client.release();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async ack(leases) {
|
|
337
|
+
const client = await this._pool.connect();
|
|
338
|
+
try {
|
|
339
|
+
await client.query("BEGIN");
|
|
340
|
+
for (const { stream, by, at, retry, block } of leases) {
|
|
341
|
+
await client.query(
|
|
342
|
+
`UPDATE "${this.config.schema}"."${this.config.table}_streams"
|
|
343
|
+
SET
|
|
344
|
+
at = $3::integer,
|
|
345
|
+
retry = $4::integer,
|
|
346
|
+
blocked = $5::boolean,
|
|
347
|
+
leased_by = NULL,
|
|
348
|
+
leased_at = NULL,
|
|
349
|
+
leased_until = NULL
|
|
350
|
+
WHERE
|
|
351
|
+
stream = $1::text
|
|
352
|
+
AND leased_by = $2::uuid`,
|
|
353
|
+
[stream, by, at, retry, block]
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
await client.query("COMMIT");
|
|
357
|
+
} catch {
|
|
358
|
+
await client.query("ROLLBACK").catch(() => {
|
|
359
|
+
});
|
|
360
|
+
} finally {
|
|
361
|
+
client.release();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
366
|
+
0 && (module.exports = {
|
|
367
|
+
PostgresStore
|
|
368
|
+
});
|
|
369
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/PostgresStore.ts","../src/utils.ts"],"sourcesContent":["/** @module act-pg */\nexport * from \"./PostgresStore.js\";\n","import type {\n Committed,\n EventMeta,\n Lease,\n Message,\n Query,\n Schemas,\n Store,\n} from \"@rotorsoft/act\";\nimport { ConcurrencyError, SNAP_EVENT, logger } from \"@rotorsoft/act\";\nimport pg from \"pg\";\nimport { dateReviver } from \"./utils.js\";\n\nconst { Pool, types } = pg;\ntypes.setTypeParser(types.builtins.JSONB, (val) =>\n JSON.parse(val, dateReviver)\n);\n\ntype Config = Readonly<{\n host: string;\n port: number;\n database: string;\n user: string;\n password: string;\n schema: string;\n table: string;\n leaseMillis: number;\n}>;\n\nconst DEFAULT_CONFIG: Config = {\n host: \"localhost\",\n port: 5432,\n database: \"postgres\",\n user: \"postgres\",\n password: \"postgres\",\n schema: \"public\",\n table: \"events\",\n leaseMillis: 30_000,\n};\n\nexport class PostgresStore implements Store {\n private _pool;\n readonly config: Config;\n\n constructor(config: Partial<Config> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this._pool = new Pool(this.config);\n }\n\n async dispose() {\n await this._pool.end();\n }\n\n async seed() {\n const client = await this._pool.connect();\n\n try {\n await client.query(\"BEGIN\");\n\n // Create schema\n await client.query(\n `CREATE SCHEMA IF NOT EXISTS \"${this.config.schema}\";`\n );\n\n // Events table\n await client.query(\n `CREATE TABLE IF NOT EXISTS \"${this.config.schema}\".\"${this.config.table}\" (\n id serial PRIMARY KEY,\n name varchar(100) COLLATE pg_catalog.\"default\" NOT NULL,\n data jsonb,\n stream varchar(100) COLLATE pg_catalog.\"default\" NOT NULL,\n version int NOT NULL,\n created timestamptz NOT NULL DEFAULT now(),\n meta jsonb\n ) TABLESPACE pg_default;`\n );\n\n // Indexes on events\n await client.query(\n `CREATE UNIQUE INDEX IF NOT EXISTS \"${this.config.table}_stream_ix\" \n ON \"${this.config.schema}\".\"${this.config.table}\" (stream COLLATE pg_catalog.\"default\", version);`\n );\n await client.query(\n `CREATE INDEX IF NOT EXISTS \"${this.config.table}_name_ix\" \n ON \"${this.config.schema}\".\"${this.config.table}\" (name COLLATE pg_catalog.\"default\");`\n );\n await client.query(\n `CREATE INDEX IF NOT EXISTS \"${this.config.table}_created_id_ix\" \n ON \"${this.config.schema}\".\"${this.config.table}\" (created, id);`\n );\n await client.query(\n `CREATE INDEX IF NOT EXISTS \"${this.config.table}_correlation_ix\" \n ON \"${this.config.schema}\".\"${this.config.table}\" ((meta ->> 'correlation') COLLATE pg_catalog.\"default\");`\n );\n\n // Streams table\n await client.query(\n `CREATE TABLE IF NOT EXISTS \"${this.config.schema}\".\"${this.config.table}_streams\" (\n stream varchar(100) COLLATE pg_catalog.\"default\" PRIMARY KEY,\n at int NOT NULL DEFAULT -1,\n retry smallint NOT NULL DEFAULT 0,\n blocked boolean NOT NULL DEFAULT false,\n leased_at int,\n leased_by uuid,\n leased_until timestamptz\n ) TABLESPACE pg_default;`\n );\n\n // Index for fetching streams\n await client.query(\n `CREATE INDEX IF NOT EXISTS \"${this.config.table}_streams_fetch_ix\" \n ON \"${this.config.schema}\".\"${this.config.table}_streams\" (blocked, at);`\n );\n\n await client.query(\"COMMIT\");\n logger.info(\n `Seeded schema \"${this.config.schema}\" with table \"${this.config.table}\"`\n );\n } catch (error) {\n await client.query(\"ROLLBACK\");\n logger.error(\"Failed to seed store:\", error);\n throw error;\n } finally {\n client.release();\n }\n }\n\n async drop() {\n await this._pool.query(\n `\n DO $$\n BEGIN\n IF EXISTS (SELECT 1 FROM information_schema.schemata\n WHERE schema_name = '${this.config.schema}'\n ) THEN\n EXECUTE 'DROP TABLE IF EXISTS \"${this.config.schema}\".\"${this.config.table}\"';\n EXECUTE 'DROP TABLE IF EXISTS \"${this.config.schema}\".\"${this.config.table}_streams\"';\n IF '${this.config.schema}' <> 'public' THEN\n EXECUTE 'DROP SCHEMA \"${this.config.schema}\" CASCADE';\n END IF;\n END IF;\n END\n $$;\n `\n );\n }\n\n async query<E extends Schemas>(\n callback: (event: Committed<E, keyof E>) => void,\n query?: Query,\n withSnaps = false\n ) {\n const {\n stream,\n names,\n before,\n after,\n limit,\n created_before,\n created_after,\n backward,\n correlation,\n } = query || {};\n\n let sql = `SELECT * FROM \"${this.config.schema}\".\"${this.config.table}\" WHERE`;\n const values: any[] = [];\n\n if (withSnaps)\n sql = sql.concat(\n ` id>=COALESCE((SELECT id\n FROM \"${this.config.schema}\".\"${this.config.table}\"\n WHERE stream='${stream}' AND name='${SNAP_EVENT}'\n ORDER BY id DESC LIMIT 1), 0)\n AND stream='${stream}'`\n );\n else if (query) {\n if (typeof after !== \"undefined\") {\n values.push(after);\n sql = sql.concat(\" id>$1\");\n } else sql = sql.concat(\" id>-1\");\n if (stream) {\n values.push(stream);\n sql = sql.concat(` AND stream=$${values.length}`);\n }\n if (names && names.length) {\n values.push(names);\n sql = sql.concat(` AND name = ANY($${values.length})`);\n }\n if (before) {\n values.push(before);\n sql = sql.concat(` AND id<$${values.length}`);\n }\n if (created_after) {\n values.push(created_after.toISOString());\n sql = sql.concat(` AND created>$${values.length}`);\n }\n if (created_before) {\n values.push(created_before.toISOString());\n sql = sql.concat(` AND created<$${values.length}`);\n }\n if (correlation) {\n values.push(correlation);\n sql = sql.concat(` AND meta->>'correlation'=$${values.length}`);\n }\n }\n sql = sql.concat(` ORDER BY id ${backward ? \"DESC\" : \"ASC\"}`);\n if (limit) {\n values.push(limit);\n sql = sql.concat(` LIMIT $${values.length}`);\n }\n\n const result = await this._pool.query<Committed<E, keyof E>>(sql, values);\n for (const row of result.rows) callback(row);\n\n return result.rowCount ?? 0;\n }\n\n async commit<E extends Schemas>(\n stream: string,\n msgs: Message<E, keyof E>[],\n meta: EventMeta,\n expectedVersion?: number\n ) {\n const client = await this._pool.connect();\n let version = -1;\n try {\n await client.query(\"BEGIN\");\n\n const last = await client.query<Committed<E, keyof E>>(\n `SELECT version\n FROM \"${this.config.schema}\".\"${this.config.table}\"\n WHERE stream=$1 ORDER BY version DESC LIMIT 1`,\n [stream]\n );\n version = last.rowCount ? last.rows[0].version : -1;\n if (expectedVersion && version !== expectedVersion)\n throw new ConcurrencyError(\n version,\n msgs as unknown as Message<Schemas, string>[],\n expectedVersion\n );\n\n const committed = await Promise.all(\n msgs.map(async ({ name, data }) => {\n version++;\n const sql = `\n INSERT INTO \"${this.config.schema}\".\"${this.config.table}\"(name, data, stream, version, meta) \n VALUES($1, $2, $3, $4, $5) RETURNING *`;\n const vals = [name, data, stream, version, meta];\n const { rows } = await client.query<Committed<E, keyof E>>(sql, vals);\n return rows.at(0)!;\n })\n );\n\n await client\n .query(\n `\n NOTIFY \"${this.config.table}\", '${JSON.stringify({\n operation: \"INSERT\",\n id: committed[0].name,\n position: committed[0].id,\n })}';\n COMMIT;\n `\n )\n .catch((error) => {\n logger.error(error);\n throw new ConcurrencyError(\n version,\n msgs as unknown as Message<Schemas, string>[],\n expectedVersion || -1\n );\n });\n return committed;\n } catch (error) {\n await client.query(\"ROLLBACK\").catch(() => {});\n throw error;\n } finally {\n client.release();\n }\n }\n\n async fetch<E extends Schemas>(limit: number) {\n const { rows } = await this._pool.query<{ stream: string; at: number }>(\n `\n SELECT stream, at\n FROM \"${this.config.schema}\".\"${this.config.table}_streams\"\n WHERE blocked=false\n ORDER BY at ASC\n LIMIT $1::integer\n `,\n [limit]\n );\n\n const after = rows.length\n ? rows.reduce((min, r) => Math.min(min, r.at), Number.MAX_SAFE_INTEGER)\n : -1;\n\n const events: Committed<E, keyof E>[] = [];\n await this.query<E>((e) => events.push(e), { after, limit });\n return { streams: rows.map(({ stream }) => stream), events };\n }\n\n async lease(leases: Lease[]) {\n const { by, at } = leases.at(0)!;\n const streams = leases.map(({ stream }) => stream);\n const client = await this._pool.connect();\n\n try {\n await client.query(\"BEGIN\");\n // insert new streams\n await client.query(\n `\n INSERT INTO \"${this.config.schema}\".\"${this.config.table}_streams\" (stream)\n SELECT UNNEST($1::text[])\n ON CONFLICT (stream) DO NOTHING\n `,\n [streams]\n );\n // set leases\n const { rows } = await client.query<{\n stream: string;\n leased_at: number;\n retry: number;\n }>(\n `\n WITH free AS (\n SELECT * FROM \"${this.config.schema}\".\"${this.config.table}_streams\" \n WHERE stream = ANY($1::text[]) AND (leased_by IS NULL OR leased_until <= NOW())\n FOR UPDATE\n )\n UPDATE \"${this.config.schema}\".\"${this.config.table}_streams\" U\n SET\n leased_by = $2::uuid,\n leased_at = $3::integer,\n leased_until = NOW() + ($4::integer || ' milliseconds')::interval\n FROM free\n WHERE U.stream = free.stream\n RETURNING U.stream, U.leased_at, U.retry\n `,\n [streams, by, at, this.config.leaseMillis]\n );\n await client.query(\"COMMIT\");\n\n return rows.map(({ stream, leased_at, retry }) => ({\n stream,\n by,\n at: leased_at,\n retry,\n block: false,\n }));\n } catch (error) {\n await client.query(\"ROLLBACK\").catch(() => {});\n throw error;\n } finally {\n client.release();\n }\n }\n\n async ack(leases: Lease[]) {\n const client = await this._pool.connect();\n\n try {\n await client.query(\"BEGIN\");\n for (const { stream, by, at, retry, block } of leases) {\n await client.query(\n `UPDATE \"${this.config.schema}\".\"${this.config.table}_streams\"\n SET\n at = $3::integer,\n retry = $4::integer,\n blocked = $5::boolean,\n leased_by = NULL,\n leased_at = NULL,\n leased_until = NULL\n WHERE\n stream = $1::text\n AND leased_by = $2::uuid`,\n [stream, by, at, retry, block]\n );\n }\n await client.query(\"COMMIT\");\n } catch {\n // leased_until fallback\n await client.query(\"ROLLBACK\").catch(() => {});\n } finally {\n client.release();\n }\n }\n}\n","/**\n * Date reviver when parsing JSON strings with the following formats:\n * - YYYY-MM-DDTHH:MM:SS.sssZ\n * - YYYY-MM-DDTHH:MM:SS.sss+HH:MM\n * - YYYY-MM-DDTHH:MM:SS.sss-HH:MM\n */\nconst ISO_8601 =\n /^(\\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])?$/;\nexport const dateReviver = (key: string, value: string): string | Date => {\n if (typeof value === \"string\" && ISO_8601.test(value)) {\n return new Date(value);\n }\n return value;\n};\n"],"mappings":"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","names":["pg"]}
|