@rotorsoft/act-pg 0.3.0 → 0.4.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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/PostgresStore.d.ts +13 -3
- package/dist/@types/PostgresStore.d.ts.map +1 -1
- package/dist/@types/index.d.ts +0 -1
- package/dist/@types/index.d.ts.map +1 -1
- package/dist/index.cjs +119 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +109 -95
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/@types/config.d.ts +0 -24
- package/dist/@types/config.d.ts.map +0 -1
- package/dist/@types/seed.d.ts +0 -2
- package/dist/@types/seed.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -5,9 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (
|
|
8
|
+
var __export = (target, all) => {
|
|
9
9
|
for (var name in all)
|
|
10
|
-
__defProp(
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
11
|
};
|
|
12
12
|
var __copyProps = (to, from, except, desc) => {
|
|
13
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -17,12 +17,12 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
}
|
|
18
18
|
return to;
|
|
19
19
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode,
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
21
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
22
|
// file that has been converted to a CommonJS file using a Babel-
|
|
23
23
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
24
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
@@ -30,88 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
PostgresStore: () => PostgresStore
|
|
34
|
-
config: () => config
|
|
33
|
+
PostgresStore: () => PostgresStore
|
|
35
34
|
});
|
|
36
35
|
module.exports = __toCommonJS(index_exports);
|
|
37
36
|
|
|
38
|
-
// src/config.ts
|
|
39
|
-
var import_act = require("@rotorsoft/act");
|
|
40
|
-
var import_v4 = require("zod/v4");
|
|
41
|
-
var { PG_HOST, PG_USER, PG_PASSWORD, PG_DATABASE, PG_PORT } = process.env;
|
|
42
|
-
var config = (0, import_act.extend)(
|
|
43
|
-
{
|
|
44
|
-
pg: {
|
|
45
|
-
host: PG_HOST || "localhost",
|
|
46
|
-
user: PG_USER || "postgres",
|
|
47
|
-
password: PG_PASSWORD || "postgres",
|
|
48
|
-
database: PG_DATABASE || "postgres",
|
|
49
|
-
port: Number.parseInt(PG_PORT || "5431")
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
import_v4.z.object({
|
|
53
|
-
pg: import_v4.z.object({
|
|
54
|
-
host: import_v4.z.string().min(1),
|
|
55
|
-
user: import_v4.z.string().min(1),
|
|
56
|
-
password: import_v4.z.string().min(1),
|
|
57
|
-
database: import_v4.z.string().min(1),
|
|
58
|
-
port: import_v4.z.number().int().min(1e3).max(65535)
|
|
59
|
-
})
|
|
60
|
-
}),
|
|
61
|
-
(0, import_act.config)()
|
|
62
|
-
);
|
|
63
|
-
|
|
64
37
|
// src/PostgresStore.ts
|
|
65
|
-
var
|
|
38
|
+
var import_act = require("@rotorsoft/act");
|
|
66
39
|
var import_pg = __toESM(require("pg"), 1);
|
|
67
40
|
|
|
68
|
-
// src/seed.ts
|
|
69
|
-
var seed_store = (table) => `
|
|
70
|
-
-- events
|
|
71
|
-
CREATE TABLE IF NOT EXISTS public."${table}"
|
|
72
|
-
(
|
|
73
|
-
id serial PRIMARY KEY,
|
|
74
|
-
name varchar(100) COLLATE pg_catalog."default" NOT NULL,
|
|
75
|
-
data jsonb,
|
|
76
|
-
stream varchar(100) COLLATE pg_catalog."default" NOT NULL,
|
|
77
|
-
version int NOT NULL,
|
|
78
|
-
created timestamptz NOT NULL DEFAULT now(),
|
|
79
|
-
meta jsonb
|
|
80
|
-
) TABLESPACE pg_default;
|
|
81
|
-
|
|
82
|
-
CREATE UNIQUE INDEX IF NOT EXISTS "${table}_stream_ix"
|
|
83
|
-
ON public."${table}" USING btree (stream COLLATE pg_catalog."default" ASC, version ASC)
|
|
84
|
-
TABLESPACE pg_default;
|
|
85
|
-
|
|
86
|
-
CREATE INDEX IF NOT EXISTS "${table}_name_ix"
|
|
87
|
-
ON public."${table}" USING btree (name COLLATE pg_catalog."default" ASC)
|
|
88
|
-
TABLESPACE pg_default;
|
|
89
|
-
|
|
90
|
-
CREATE INDEX IF NOT EXISTS "${table}_created_id_ix"
|
|
91
|
-
ON public."${table}" USING btree (created ASC, id ASC)
|
|
92
|
-
TABLESPACE pg_default;
|
|
93
|
-
|
|
94
|
-
CREATE INDEX IF NOT EXISTS "${table}_correlation_ix"
|
|
95
|
-
ON public."${table}" USING btree ((meta ->> 'correlation'::text) COLLATE pg_catalog."default" ASC NULLS LAST)
|
|
96
|
-
TABLESPACE pg_default;
|
|
97
|
-
|
|
98
|
-
-- streams
|
|
99
|
-
CREATE TABLE IF NOT EXISTS public."${table}_streams"
|
|
100
|
-
(
|
|
101
|
-
stream varchar(100) COLLATE pg_catalog."default" PRIMARY KEY,
|
|
102
|
-
at int not null default(-1),
|
|
103
|
-
retry smallint not null default(0),
|
|
104
|
-
blocked boolean not null default(false),
|
|
105
|
-
leased_at int,
|
|
106
|
-
leased_by uuid,
|
|
107
|
-
leased_until timestamptz
|
|
108
|
-
) TABLESPACE pg_default;
|
|
109
|
-
|
|
110
|
-
-- supports order by { blocked, at } when fetching
|
|
111
|
-
CREATE INDEX IF NOT EXISTS "${table}_streams_fetch_ix"
|
|
112
|
-
ON public."${table}_streams" USING btree (blocked, at) TABLESPACE pg_default;
|
|
113
|
-
`;
|
|
114
|
-
|
|
115
41
|
// src/utils.ts
|
|
116
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])?$/;
|
|
117
43
|
var dateReviver = (key, value) => {
|
|
@@ -127,22 +53,105 @@ types.setTypeParser(
|
|
|
127
53
|
types.builtins.JSONB,
|
|
128
54
|
(val) => JSON.parse(val, dateReviver)
|
|
129
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
|
+
};
|
|
130
66
|
var PostgresStore = class {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
67
|
+
_pool;
|
|
68
|
+
config;
|
|
69
|
+
constructor(config = {}) {
|
|
70
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
71
|
+
this._pool = new Pool(this.config);
|
|
134
72
|
}
|
|
135
|
-
_pool = new Pool(config.pg);
|
|
136
73
|
async dispose() {
|
|
137
74
|
await this._pool.end();
|
|
138
75
|
}
|
|
139
76
|
async seed() {
|
|
140
|
-
const
|
|
141
|
-
|
|
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
|
+
}
|
|
142
136
|
}
|
|
143
137
|
async drop() {
|
|
144
|
-
await this._pool.query(
|
|
145
|
-
|
|
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
|
+
);
|
|
146
155
|
}
|
|
147
156
|
async query(callback, query, withSnaps = false) {
|
|
148
157
|
const {
|
|
@@ -156,13 +165,13 @@ var PostgresStore = class {
|
|
|
156
165
|
backward,
|
|
157
166
|
correlation
|
|
158
167
|
} = query || {};
|
|
159
|
-
let sql = `SELECT * FROM "${this.table}" WHERE`;
|
|
168
|
+
let sql = `SELECT * FROM "${this.config.schema}"."${this.config.table}" WHERE`;
|
|
160
169
|
const values = [];
|
|
161
170
|
if (withSnaps)
|
|
162
171
|
sql = sql.concat(
|
|
163
172
|
` id>=COALESCE((SELECT id
|
|
164
|
-
FROM "${this.table}"
|
|
165
|
-
WHERE stream='${stream}' AND name='${
|
|
173
|
+
FROM "${this.config.schema}"."${this.config.table}"
|
|
174
|
+
WHERE stream='${stream}' AND name='${import_act.SNAP_EVENT}'
|
|
166
175
|
ORDER BY id DESC LIMIT 1), 0)
|
|
167
176
|
AND stream='${stream}'`
|
|
168
177
|
);
|
|
@@ -211,12 +220,14 @@ var PostgresStore = class {
|
|
|
211
220
|
try {
|
|
212
221
|
await client.query("BEGIN");
|
|
213
222
|
const last = await client.query(
|
|
214
|
-
`SELECT version
|
|
223
|
+
`SELECT version
|
|
224
|
+
FROM "${this.config.schema}"."${this.config.table}"
|
|
225
|
+
WHERE stream=$1 ORDER BY version DESC LIMIT 1`,
|
|
215
226
|
[stream]
|
|
216
227
|
);
|
|
217
228
|
version = last.rowCount ? last.rows[0].version : -1;
|
|
218
229
|
if (expectedVersion && version !== expectedVersion)
|
|
219
|
-
throw new
|
|
230
|
+
throw new import_act.ConcurrencyError(
|
|
220
231
|
version,
|
|
221
232
|
msgs,
|
|
222
233
|
expectedVersion
|
|
@@ -225,7 +236,7 @@ var PostgresStore = class {
|
|
|
225
236
|
msgs.map(async ({ name, data }) => {
|
|
226
237
|
version++;
|
|
227
238
|
const sql = `
|
|
228
|
-
INSERT INTO "${this.table}"(name, data, stream, version, meta)
|
|
239
|
+
INSERT INTO "${this.config.schema}"."${this.config.table}"(name, data, stream, version, meta)
|
|
229
240
|
VALUES($1, $2, $3, $4, $5) RETURNING *`;
|
|
230
241
|
const vals = [name, data, stream, version, meta];
|
|
231
242
|
const { rows } = await client.query(sql, vals);
|
|
@@ -234,7 +245,7 @@ var PostgresStore = class {
|
|
|
234
245
|
);
|
|
235
246
|
await client.query(
|
|
236
247
|
`
|
|
237
|
-
NOTIFY "${this.table}", '${JSON.stringify({
|
|
248
|
+
NOTIFY "${this.config.table}", '${JSON.stringify({
|
|
238
249
|
operation: "INSERT",
|
|
239
250
|
id: committed[0].name,
|
|
240
251
|
position: committed[0].id
|
|
@@ -242,8 +253,8 @@ var PostgresStore = class {
|
|
|
242
253
|
COMMIT;
|
|
243
254
|
`
|
|
244
255
|
).catch((error) => {
|
|
245
|
-
|
|
246
|
-
throw new
|
|
256
|
+
import_act.logger.error(error);
|
|
257
|
+
throw new import_act.ConcurrencyError(
|
|
247
258
|
version,
|
|
248
259
|
msgs,
|
|
249
260
|
expectedVersion || -1
|
|
@@ -262,7 +273,7 @@ var PostgresStore = class {
|
|
|
262
273
|
const { rows } = await this._pool.query(
|
|
263
274
|
`
|
|
264
275
|
SELECT stream, at
|
|
265
|
-
FROM "${this.table}_streams"
|
|
276
|
+
FROM "${this.config.schema}"."${this.config.table}_streams"
|
|
266
277
|
WHERE blocked=false
|
|
267
278
|
ORDER BY at ASC
|
|
268
279
|
LIMIT $1::integer
|
|
@@ -271,7 +282,10 @@ var PostgresStore = class {
|
|
|
271
282
|
);
|
|
272
283
|
const after = rows.length ? rows.reduce((min, r) => Math.min(min, r.at), Number.MAX_SAFE_INTEGER) : -1;
|
|
273
284
|
const events = [];
|
|
274
|
-
await this.query((e) => events.push(e), {
|
|
285
|
+
await this.query((e) => e.name !== import_act.SNAP_EVENT && events.push(e), {
|
|
286
|
+
after,
|
|
287
|
+
limit
|
|
288
|
+
});
|
|
275
289
|
return { streams: rows.map(({ stream }) => stream), events };
|
|
276
290
|
}
|
|
277
291
|
async lease(leases) {
|
|
@@ -282,7 +296,7 @@ var PostgresStore = class {
|
|
|
282
296
|
await client.query("BEGIN");
|
|
283
297
|
await client.query(
|
|
284
298
|
`
|
|
285
|
-
INSERT INTO "${this.table}_streams" (stream)
|
|
299
|
+
INSERT INTO "${this.config.schema}"."${this.config.table}_streams" (stream)
|
|
286
300
|
SELECT UNNEST($1::text[])
|
|
287
301
|
ON CONFLICT (stream) DO NOTHING
|
|
288
302
|
`,
|
|
@@ -291,11 +305,11 @@ var PostgresStore = class {
|
|
|
291
305
|
const { rows } = await client.query(
|
|
292
306
|
`
|
|
293
307
|
WITH free AS (
|
|
294
|
-
SELECT * FROM "${this.table}_streams"
|
|
308
|
+
SELECT * FROM "${this.config.schema}"."${this.config.table}_streams"
|
|
295
309
|
WHERE stream = ANY($1::text[]) AND (leased_by IS NULL OR leased_until <= NOW())
|
|
296
310
|
FOR UPDATE
|
|
297
311
|
)
|
|
298
|
-
UPDATE "${this.table}_streams" U
|
|
312
|
+
UPDATE "${this.config.schema}"."${this.config.table}_streams" U
|
|
299
313
|
SET
|
|
300
314
|
leased_by = $2::uuid,
|
|
301
315
|
leased_at = $3::integer,
|
|
@@ -304,7 +318,7 @@ var PostgresStore = class {
|
|
|
304
318
|
WHERE U.stream = free.stream
|
|
305
319
|
RETURNING U.stream, U.leased_at, U.retry
|
|
306
320
|
`,
|
|
307
|
-
[streams, by, at, this.leaseMillis]
|
|
321
|
+
[streams, by, at, this.config.leaseMillis]
|
|
308
322
|
);
|
|
309
323
|
await client.query("COMMIT");
|
|
310
324
|
return rows.map(({ stream, leased_at, retry }) => ({
|
|
@@ -328,7 +342,7 @@ var PostgresStore = class {
|
|
|
328
342
|
await client.query("BEGIN");
|
|
329
343
|
for (const { stream, by, at, retry, block } of leases) {
|
|
330
344
|
await client.query(
|
|
331
|
-
`UPDATE "${this.table}_streams"
|
|
345
|
+
`UPDATE "${this.config.schema}"."${this.config.table}_streams"
|
|
332
346
|
SET
|
|
333
347
|
at = $3::integer,
|
|
334
348
|
retry = $4::integer,
|
|
@@ -353,7 +367,6 @@ var PostgresStore = class {
|
|
|
353
367
|
};
|
|
354
368
|
// Annotate the CommonJS export names for ESM import in node:
|
|
355
369
|
0 && (module.exports = {
|
|
356
|
-
PostgresStore
|
|
357
|
-
config
|
|
370
|
+
PostgresStore
|
|
358
371
|
});
|
|
359
372
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/PostgresStore.ts","../src/seed.ts","../src/utils.ts"],"sourcesContent":["/** @module act-pg */\nexport * from \"./config.js\";\nexport * from \"./PostgresStore.js\";\n","import { extend, config as target } from \"@rotorsoft/act\";\nimport { z } from \"zod/v4\";\n\nconst { PG_HOST, PG_USER, PG_PASSWORD, PG_DATABASE, PG_PORT } = process.env;\n\nexport const config = extend(\n {\n pg: {\n host: PG_HOST || \"localhost\",\n user: PG_USER || \"postgres\",\n password: PG_PASSWORD || \"postgres\",\n database: PG_DATABASE || \"postgres\",\n port: Number.parseInt(PG_PORT || \"5431\"),\n },\n },\n z.object({\n pg: z.object({\n host: z.string().min(1),\n user: z.string().min(1),\n password: z.string().min(1),\n database: z.string().min(1),\n port: z.number().int().min(1000).max(65535),\n }),\n }),\n target()\n);\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 { config } from \"./config.js\";\nimport { seed_store } from \"./seed.js\";\nimport { dateReviver } from \"./utils.js\";\n\nconst { Pool, types } = pg;\ntypes.setTypeParser(types.builtins.JSONB, (val) =>\n JSON.parse(val, dateReviver)\n);\n\nexport class PostgresStore implements Store {\n private _pool = new Pool(config.pg);\n\n constructor(\n readonly table: string,\n readonly leaseMillis = 30_000\n ) {}\n async dispose() {\n await this._pool.end();\n }\n\n async seed() {\n const seed = seed_store(this.table);\n await this._pool.query(seed);\n }\n\n async drop() {\n await this._pool.query(`DROP TABLE IF EXISTS \"${this.table}\"`);\n await this._pool.query(`DROP TABLE IF EXISTS \"${this.table}_streams\"`);\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.table}\" WHERE`;\n const values: any[] = [];\n\n if (withSnaps)\n sql = sql.concat(\n ` id>=COALESCE((SELECT id\n FROM \"${this.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 FROM \"${this.table}\" 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.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.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.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.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.table}_streams\" \n WHERE stream = ANY($1::text[]) AND (leased_by IS NULL OR leased_until <= NOW())\n FOR UPDATE\n )\n UPDATE \"${this.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.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.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","export const seed_store = (table: string): string => `\n-- events\nCREATE TABLE IF NOT EXISTS public.\"${table}\"\n(\n\tid 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\nCREATE UNIQUE INDEX IF NOT EXISTS \"${table}_stream_ix\"\n ON public.\"${table}\" USING btree (stream COLLATE pg_catalog.\"default\" ASC, version ASC)\n TABLESPACE pg_default;\n\t\nCREATE INDEX IF NOT EXISTS \"${table}_name_ix\"\n ON public.\"${table}\" USING btree (name COLLATE pg_catalog.\"default\" ASC)\n TABLESPACE pg_default;\n \nCREATE INDEX IF NOT EXISTS \"${table}_created_id_ix\"\n ON public.\"${table}\" USING btree (created ASC, id ASC)\n TABLESPACE pg_default;\n\nCREATE INDEX IF NOT EXISTS \"${table}_correlation_ix\"\n ON public.\"${table}\" USING btree ((meta ->> 'correlation'::text) COLLATE pg_catalog.\"default\" ASC NULLS LAST)\n TABLESPACE pg_default;\n\n-- streams\nCREATE TABLE IF NOT EXISTS public.\"${table}_streams\"\n(\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-- supports order by { blocked, at } when fetching\nCREATE INDEX IF NOT EXISTS \"${table}_streams_fetch_ix\"\n ON public.\"${table}_streams\" USING btree (blocked, at) TABLESPACE pg_default;\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":["target","import_act","pg"]}
|
|
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) => e.name !== SNAP_EVENT && events.push(e), {\n after,\n limit,\n });\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACSA,iBAAqD;AACrD,gBAAe;;;ACJf,IAAM,WACJ;AACK,IAAM,cAAc,CAAC,KAAa,UAAiC;AACxE,MAAI,OAAO,UAAU,YAAY,SAAS,KAAK,KAAK,GAAG;AACrD,WAAO,IAAI,KAAK,KAAK;AAAA,EACvB;AACA,SAAO;AACT;;;ADAA,IAAM,EAAE,MAAM,MAAM,IAAI,UAAAA;AACxB,MAAM;AAAA,EAAc,MAAM,SAAS;AAAA,EAAO,CAAC,QACzC,KAAK,MAAM,KAAK,WAAW;AAC7B;AAaA,IAAM,iBAAyB;AAAA,EAC7B,MAAM;AAAA,EACN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,aAAa;AACf;AAEO,IAAM,gBAAN,MAAqC;AAAA,EAClC;AAAA,EACC;AAAA,EAET,YAAY,SAA0B,CAAC,GAAG;AACxC,SAAK,SAAS,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAC7C,SAAK,QAAQ,IAAI,KAAK,KAAK,MAAM;AAAA,EACnC;AAAA,EAEA,MAAM,UAAU;AACd,UAAM,KAAK,MAAM,IAAI;AAAA,EACvB;AAAA,EAEA,MAAM,OAAO;AACX,UAAM,SAAS,MAAM,KAAK,MAAM,QAAQ;AAExC,QAAI;AACF,YAAM,OAAO,MAAM,OAAO;AAG1B,YAAM,OAAO;AAAA,QACX,gCAAgC,KAAK,OAAO,MAAM;AAAA,MACpD;AAGA,YAAM,OAAO;AAAA,QACX,+BAA+B,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAS1E;AAGA,YAAM,OAAO;AAAA,QACX,sCAAsC,KAAK,OAAO,KAAK;AAAA,cACjD,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,MACjD;AACA,YAAM,OAAO;AAAA,QACX,+BAA+B,KAAK,OAAO,KAAK;AAAA,cAC1C,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,MACjD;AACA,YAAM,OAAO;AAAA,QACX,+BAA+B,KAAK,OAAO,KAAK;AAAA,cAC1C,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,MACjD;AACA,YAAM,OAAO;AAAA,QACX,+BAA+B,KAAK,OAAO,KAAK;AAAA,cAC1C,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,MACjD;AAGA,YAAM,OAAO;AAAA,QACX,+BAA+B,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAS1E;AAGA,YAAM,OAAO;AAAA,QACX,+BAA+B,KAAK,OAAO,KAAK;AAAA,cAC1C,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,MACjD;AAEA,YAAM,OAAO,MAAM,QAAQ;AAC3B,wBAAO;AAAA,QACL,kBAAkB,KAAK,OAAO,MAAM,iBAAiB,KAAK,OAAO,KAAK;AAAA,MACxE;AAAA,IACF,SAAS,OAAO;AACd,YAAM,OAAO,MAAM,UAAU;AAC7B,wBAAO,MAAM,yBAAyB,KAAK;AAC3C,YAAM;AAAA,IACR,UAAE;AACA,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAM,OAAO;AACX,UAAM,KAAK,MAAM;AAAA,MACf;AAAA;AAAA;AAAA;AAAA,iCAI2B,KAAK,OAAO,MAAM;AAAA;AAAA,2CAER,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,2CACzC,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,gBACpE,KAAK,OAAO,MAAM;AAAA,oCACE,KAAK,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMlD;AAAA,EACF;AAAA,EAEA,MAAM,MACJ,UACA,OACA,YAAY,OACZ;AACA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI,SAAS,CAAC;AAEd,QAAI,MAAM,kBAAkB,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AACrE,UAAM,SAAgB,CAAC;AAEvB,QAAI;AACF,YAAM,IAAI;AAAA,QACR;AAAA,oBACY,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,4BACjC,MAAM,eAAe,qBAAU;AAAA;AAAA,0BAEjC,MAAM;AAAA,MAC1B;AAAA,aACO,OAAO;AACd,UAAI,OAAO,UAAU,aAAa;AAChC,eAAO,KAAK,KAAK;AACjB,cAAM,IAAI,OAAO,QAAQ;AAAA,MAC3B,MAAO,OAAM,IAAI,OAAO,QAAQ;AAChC,UAAI,QAAQ;AACV,eAAO,KAAK,MAAM;AAClB,cAAM,IAAI,OAAO,gBAAgB,OAAO,MAAM,EAAE;AAAA,MAClD;AACA,UAAI,SAAS,MAAM,QAAQ;AACzB,eAAO,KAAK,KAAK;AACjB,cAAM,IAAI,OAAO,oBAAoB,OAAO,MAAM,GAAG;AAAA,MACvD;AACA,UAAI,QAAQ;AACV,eAAO,KAAK,MAAM;AAClB,cAAM,IAAI,OAAO,YAAY,OAAO,MAAM,EAAE;AAAA,MAC9C;AACA,UAAI,eAAe;AACjB,eAAO,KAAK,cAAc,YAAY,CAAC;AACvC,cAAM,IAAI,OAAO,iBAAiB,OAAO,MAAM,EAAE;AAAA,MACnD;AACA,UAAI,gBAAgB;AAClB,eAAO,KAAK,eAAe,YAAY,CAAC;AACxC,cAAM,IAAI,OAAO,iBAAiB,OAAO,MAAM,EAAE;AAAA,MACnD;AACA,UAAI,aAAa;AACf,eAAO,KAAK,WAAW;AACvB,cAAM,IAAI,OAAO,8BAA8B,OAAO,MAAM,EAAE;AAAA,MAChE;AAAA,IACF;AACA,UAAM,IAAI,OAAO,gBAAgB,WAAW,SAAS,KAAK,EAAE;AAC5D,QAAI,OAAO;AACT,aAAO,KAAK,KAAK;AACjB,YAAM,IAAI,OAAO,WAAW,OAAO,MAAM,EAAE;AAAA,IAC7C;AAEA,UAAM,SAAS,MAAM,KAAK,MAAM,MAA6B,KAAK,MAAM;AACxE,eAAW,OAAO,OAAO,KAAM,UAAS,GAAG;AAE3C,WAAO,OAAO,YAAY;AAAA,EAC5B;AAAA,EAEA,MAAM,OACJ,QACA,MACA,MACA,iBACA;AACA,UAAM,SAAS,MAAM,KAAK,MAAM,QAAQ;AACxC,QAAI,UAAU;AACd,QAAI;AACF,YAAM,OAAO,MAAM,OAAO;AAE1B,YAAM,OAAO,MAAM,OAAO;AAAA,QACxB;AAAA,gBACQ,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA,QAEjD,CAAC,MAAM;AAAA,MACT;AACA,gBAAU,KAAK,WAAW,KAAK,KAAK,CAAC,EAAE,UAAU;AACjD,UAAI,mBAAmB,YAAY;AACjC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAEF,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC9B,KAAK,IAAI,OAAO,EAAE,MAAM,KAAK,MAAM;AACjC;AACA,gBAAM,MAAM;AAAA,yBACG,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAExD,gBAAM,OAAO,CAAC,MAAM,MAAM,QAAQ,SAAS,IAAI;AAC/C,gBAAM,EAAE,KAAK,IAAI,MAAM,OAAO,MAA6B,KAAK,IAAI;AACpE,iBAAO,KAAK,GAAG,CAAC;AAAA,QAClB,CAAC;AAAA,MACH;AAEA,YAAM,OACH;AAAA,QACC;AAAA,sBACY,KAAK,OAAO,KAAK,OAAO,KAAK,UAAU;AAAA,UAC/C,WAAW;AAAA,UACX,IAAI,UAAU,CAAC,EAAE;AAAA,UACjB,UAAU,UAAU,CAAC,EAAE;AAAA,QACzB,CAAC,CAAC;AAAA;AAAA;AAAA,MAGN,EACC,MAAM,CAAC,UAAU;AAChB,0BAAO,MAAM,KAAK;AAClB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,mBAAmB;AAAA,QACrB;AAAA,MACF,CAAC;AACH,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,OAAO,MAAM,UAAU,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAC7C,YAAM;AAAA,IACR,UAAE;AACA,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAM,MAAyB,OAAe;AAC5C,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM;AAAA,MAChC;AAAA;AAAA,cAEQ,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,MAKjD,CAAC,KAAK;AAAA,IACR;AAEA,UAAM,QAAQ,KAAK,SACf,KAAK,OAAO,CAAC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,EAAE,GAAG,OAAO,gBAAgB,IACpE;AAEJ,UAAM,SAAkC,CAAC;AACzC,UAAM,KAAK,MAAS,CAAC,MAAM,EAAE,SAAS,yBAAc,OAAO,KAAK,CAAC,GAAG;AAAA,MAClE;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,EAAE,SAAS,KAAK,IAAI,CAAC,EAAE,OAAO,MAAM,MAAM,GAAG,OAAO;AAAA,EAC7D;AAAA,EAEA,MAAM,MAAM,QAAiB;AAC3B,UAAM,EAAE,IAAI,GAAG,IAAI,OAAO,GAAG,CAAC;AAC9B,UAAM,UAAU,OAAO,IAAI,CAAC,EAAE,OAAO,MAAM,MAAM;AACjD,UAAM,SAAS,MAAM,KAAK,MAAM,QAAQ;AAExC,QAAI;AACF,YAAM,OAAO,MAAM,OAAO;AAE1B,YAAM,OAAO;AAAA,QACX;AAAA,uBACe,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA,QAIxD,CAAC,OAAO;AAAA,MACV;AAEA,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO;AAAA,QAK5B;AAAA;AAAA,2BAEmB,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA,kBAIlD,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASnD,CAAC,SAAS,IAAI,IAAI,KAAK,OAAO,WAAW;AAAA,MAC3C;AACA,YAAM,OAAO,MAAM,QAAQ;AAE3B,aAAO,KAAK,IAAI,CAAC,EAAE,QAAQ,WAAW,MAAM,OAAO;AAAA,QACjD;AAAA,QACA;AAAA,QACA,IAAI;AAAA,QACJ;AAAA,QACA,OAAO;AAAA,MACT,EAAE;AAAA,IACJ,SAAS,OAAO;AACd,YAAM,OAAO,MAAM,UAAU,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAC7C,YAAM;AAAA,IACR,UAAE;AACA,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,MAAM,IAAI,QAAiB;AACzB,UAAM,SAAS,MAAM,KAAK,MAAM,QAAQ;AAExC,QAAI;AACF,YAAM,OAAO,MAAM,OAAO;AAC1B,iBAAW,EAAE,QAAQ,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ;AACrD,cAAM,OAAO;AAAA,UACX,WAAW,KAAK,OAAO,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAWpD,CAAC,QAAQ,IAAI,IAAI,OAAO,KAAK;AAAA,QAC/B;AAAA,MACF;AACA,YAAM,OAAO,MAAM,QAAQ;AAAA,IAC7B,QAAQ;AAEN,YAAM,OAAO,MAAM,UAAU,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC/C,UAAE;AACA,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;","names":["pg"]}
|