@webda/postgres 4.0.0-beta.1 → 4.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -0
- package/lib/index.d.ts +4 -1
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/pgpool.d.ts +40 -0
- package/lib/pgpool.d.ts.map +1 -0
- package/lib/pgpool.js +101 -0
- package/lib/pgpool.js.map +1 -0
- package/lib/postgrespubsub.service.d.ts +131 -0
- package/lib/postgrespubsub.service.d.ts.map +1 -0
- package/lib/postgrespubsub.service.js +208 -0
- package/lib/postgrespubsub.service.js.map +1 -0
- package/lib/postgresqueue.service.d.ts +144 -0
- package/lib/postgresqueue.service.d.ts.map +1 -0
- package/lib/postgresqueue.service.js +210 -0
- package/lib/postgresqueue.service.js.map +1 -0
- package/lib/postgresstore.service.d.ts +132 -0
- package/lib/postgresstore.service.d.ts.map +1 -0
- package/lib/postgresstore.service.js +327 -0
- package/lib/postgresstore.service.js.map +1 -0
- package/lib/sqlstore.d.ts +122 -40
- package/lib/sqlstore.d.ts.map +1 -0
- package/lib/sqlstore.js +313 -103
- package/lib/sqlstore.js.map +1 -1
- package/package.json +33 -30
- package/test/config.json +2 -53
- package/webda.module.json +2386 -2414
- package/lib/postgresstore.d.ts +0 -114
- package/lib/postgresstore.js +0 -271
- package/lib/postgresstore.js.map +0 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { MessageReceipt, Queue, QueueParameters } from "@webda/core";
|
|
2
|
+
import pg, { ClientConfig, PoolConfig } from "pg";
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for {@link PostgresQueueService}.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PostgresQueueParameters extends QueueParameters {
|
|
7
|
+
/**
|
|
8
|
+
* Table name backing the queue. Auto-created on init if missing.
|
|
9
|
+
*
|
|
10
|
+
* @default "webda_queue"
|
|
11
|
+
* @pattern /^[a-zA-Z_][a-zA-Z0-9_]*$/
|
|
12
|
+
* @example "myapp_queue"
|
|
13
|
+
* @example "user_notifications"
|
|
14
|
+
* @example "cache_invalidation"
|
|
15
|
+
* @example "orders"
|
|
16
|
+
*/
|
|
17
|
+
table?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Visibility timeout in seconds — how long a locked-but-undeleted
|
|
20
|
+
* message stays invisible to other consumers before being eligible for
|
|
21
|
+
* redelivery. Workers that crash mid-process without acking will see
|
|
22
|
+
* their messages reappear after this window.
|
|
23
|
+
*
|
|
24
|
+
* @default 30
|
|
25
|
+
*/
|
|
26
|
+
visibilityTimeout?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Max number of messages pulled per `receiveMessage` call. The queue
|
|
29
|
+
* worker calls receiveMessage in a loop, so this is also the parallel
|
|
30
|
+
* batch size.
|
|
31
|
+
*
|
|
32
|
+
* @default 10
|
|
33
|
+
*/
|
|
34
|
+
batchSize?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to use a `pg.Pool` (recommended for shared workloads) or a
|
|
37
|
+
* single `pg.Client`.
|
|
38
|
+
*
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
usePool?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Connection settings forwarded to the chosen pg client/pool. Defaults
|
|
44
|
+
* to PG* environment variables.
|
|
45
|
+
*/
|
|
46
|
+
postgresqlServer?: ClientConfig | PoolConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to auto-create the queue table on init.
|
|
49
|
+
*
|
|
50
|
+
* @default true
|
|
51
|
+
*/
|
|
52
|
+
autoCreateTable?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* @override
|
|
55
|
+
* @param params - the input parameters
|
|
56
|
+
* @returns this
|
|
57
|
+
*/
|
|
58
|
+
load(params?: any): this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Postgres-backed FIFO queue using `SELECT … FOR UPDATE SKIP LOCKED` (PG
|
|
62
|
+
* 9.5+) for atomic multi-worker pulls. A schema-managed table holds
|
|
63
|
+
* pending and locked rows; receive locks a batch atomically, delete (or
|
|
64
|
+
* the visibility-timeout sweep) clears them. No extra infrastructure
|
|
65
|
+
* needed beyond a Postgres connection — reuses the same DB you're
|
|
66
|
+
* already running for the store.
|
|
67
|
+
*
|
|
68
|
+
* Wire format: payload column is `jsonb` so messages survive
|
|
69
|
+
* round-tripping with their structure intact and can be queried directly
|
|
70
|
+
* if you ever need to inspect the queue.
|
|
71
|
+
*
|
|
72
|
+
* @WebdaModda PostgresQueue
|
|
73
|
+
*/
|
|
74
|
+
export default class PostgresQueueService<T = any, K extends PostgresQueueParameters = PostgresQueueParameters> extends Queue<T, K> {
|
|
75
|
+
/**
|
|
76
|
+
* Backing pg client or pool. Pools are preferred under load — receive
|
|
77
|
+
* locks rotate across connections and benefit from concurrency.
|
|
78
|
+
*/
|
|
79
|
+
protected client?: pg.Client | pg.Pool;
|
|
80
|
+
/**
|
|
81
|
+
* True when {@link client} is a shared pool acquired from the registry.
|
|
82
|
+
* Drives the matching {@link releasePool} call in {@link stop}.
|
|
83
|
+
*/
|
|
84
|
+
protected ownsPool: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Resolved table identifier. Validated at init to keep the table name
|
|
87
|
+
* out of any literal SQL paths that aren't parameterizable.
|
|
88
|
+
* @returns the table name
|
|
89
|
+
*/
|
|
90
|
+
protected get table(): string;
|
|
91
|
+
/**
|
|
92
|
+
* @override
|
|
93
|
+
* @returns this service
|
|
94
|
+
*/
|
|
95
|
+
init(): Promise<this>;
|
|
96
|
+
/**
|
|
97
|
+
* Create the queue table and the index that supports the SKIP LOCKED
|
|
98
|
+
* receive query, if they're missing.
|
|
99
|
+
*/
|
|
100
|
+
protected ensureTable(): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* @override
|
|
103
|
+
* @param event - the event to enqueue
|
|
104
|
+
*/
|
|
105
|
+
sendMessage(event: T): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* @override
|
|
108
|
+
* @returns count of messages currently visible (pending or expired-lock)
|
|
109
|
+
*/
|
|
110
|
+
size(): Promise<number>;
|
|
111
|
+
/**
|
|
112
|
+
* @override
|
|
113
|
+
* @param proto - optional prototype to rehydrate the payload into
|
|
114
|
+
* @returns the locked batch
|
|
115
|
+
*/
|
|
116
|
+
receiveMessage<L>(proto?: {
|
|
117
|
+
new (): L;
|
|
118
|
+
}): Promise<MessageReceipt<L>[]>;
|
|
119
|
+
/**
|
|
120
|
+
* @override
|
|
121
|
+
* @param id - the receipt handle returned by {@link receiveMessage}
|
|
122
|
+
*/
|
|
123
|
+
deleteMessage(id: string): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Override the queue's per-receive parallelism: receiveMessage already
|
|
126
|
+
* pulls a batch, so the consumer-spawning loop only needs one parent
|
|
127
|
+
* worker per `batchSize`.
|
|
128
|
+
*
|
|
129
|
+
* @override
|
|
130
|
+
* @returns the result number
|
|
131
|
+
*/
|
|
132
|
+
getMaxConsumers(): number;
|
|
133
|
+
/**
|
|
134
|
+
* Convenience: drop and recreate the queue table. Used by tests; not
|
|
135
|
+
* for production. Mirrors the `__clean` hook on FileQueue.
|
|
136
|
+
*/
|
|
137
|
+
__clean(): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* @override
|
|
140
|
+
*/
|
|
141
|
+
stop(): Promise<void>;
|
|
142
|
+
}
|
|
143
|
+
export { PostgresQueueService };
|
|
144
|
+
//# sourceMappingURL=postgresqueue.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgresqueue.service.d.ts","sourceRoot":"","sources":["../src/postgresqueue.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGrE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAGlD;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,eAAe;IAC1D;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAC7C;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,IAAI,CAAC,MAAM,GAAE,GAAQ,GAAG,IAAI,CAQ3B;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAoB,CACvC,CAAC,GAAG,GAAG,EACP,CAAC,SAAS,uBAAuB,GAAG,uBAAuB,CAC3D,SAAQ,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC;IACvC;;;OAGG;IACH,SAAS,CAAC,QAAQ,UAAS;IAE3B;;;;OAIG;IACH,SAAS,KAAK,KAAK,IAAI,MAAM,CAE5B;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAgB1B;IAED;;;OAGG;IACH,UAAgB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAoB3C;IAED;;;OAGG;IACG,WAAW,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzC;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAM5B;IAED;;;;OAIG;IACG,cAAc,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CA2B3E;IAED;;;OAGG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7C;IAED;;;;;;;OAOG;IACH,eAAe,IAAI,MAAM,CAExB;IAED;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAO7B;IAED;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAe1B;CACF;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { Queue, QueueParameters } from "@webda/core";
|
|
2
|
+
import { JSONUtils } from "@webda/utils";
|
|
3
|
+
import { useLog } from "@webda/workout";
|
|
4
|
+
import pg from "pg";
|
|
5
|
+
import { acquirePool, releasePool } from "./pgpool.js";
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for {@link PostgresQueueService}.
|
|
8
|
+
*/
|
|
9
|
+
export class PostgresQueueParameters extends QueueParameters {
|
|
10
|
+
/**
|
|
11
|
+
* @override
|
|
12
|
+
* @param params - the input parameters
|
|
13
|
+
* @returns this
|
|
14
|
+
*/
|
|
15
|
+
load(params = {}) {
|
|
16
|
+
super.load(params);
|
|
17
|
+
this.table ?? (this.table = "webda_queue");
|
|
18
|
+
this.visibilityTimeout ?? (this.visibilityTimeout = 30);
|
|
19
|
+
this.batchSize ?? (this.batchSize = 10);
|
|
20
|
+
this.usePool ?? (this.usePool = true);
|
|
21
|
+
this.autoCreateTable ?? (this.autoCreateTable = true);
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Postgres-backed FIFO queue using `SELECT … FOR UPDATE SKIP LOCKED` (PG
|
|
27
|
+
* 9.5+) for atomic multi-worker pulls. A schema-managed table holds
|
|
28
|
+
* pending and locked rows; receive locks a batch atomically, delete (or
|
|
29
|
+
* the visibility-timeout sweep) clears them. No extra infrastructure
|
|
30
|
+
* needed beyond a Postgres connection — reuses the same DB you're
|
|
31
|
+
* already running for the store.
|
|
32
|
+
*
|
|
33
|
+
* Wire format: payload column is `jsonb` so messages survive
|
|
34
|
+
* round-tripping with their structure intact and can be queried directly
|
|
35
|
+
* if you ever need to inspect the queue.
|
|
36
|
+
*
|
|
37
|
+
* @WebdaModda PostgresQueue
|
|
38
|
+
*/
|
|
39
|
+
export default class PostgresQueueService extends Queue {
|
|
40
|
+
constructor() {
|
|
41
|
+
super(...arguments);
|
|
42
|
+
/**
|
|
43
|
+
* True when {@link client} is a shared pool acquired from the registry.
|
|
44
|
+
* Drives the matching {@link releasePool} call in {@link stop}.
|
|
45
|
+
*/
|
|
46
|
+
this.ownsPool = false;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolved table identifier. Validated at init to keep the table name
|
|
50
|
+
* out of any literal SQL paths that aren't parameterizable.
|
|
51
|
+
* @returns the table name
|
|
52
|
+
*/
|
|
53
|
+
get table() {
|
|
54
|
+
return this.parameters.table;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* @override
|
|
58
|
+
* @returns this service
|
|
59
|
+
*/
|
|
60
|
+
async init() {
|
|
61
|
+
await super.init();
|
|
62
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(this.table)) {
|
|
63
|
+
throw new Error(`Invalid table name "${this.table}" — must match /^[a-zA-Z_][a-zA-Z0-9_]*$/`);
|
|
64
|
+
}
|
|
65
|
+
if (this.parameters.usePool) {
|
|
66
|
+
this.client = acquirePool(this.parameters.postgresqlServer);
|
|
67
|
+
this.ownsPool = true;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.client = new pg.Client(this.parameters.postgresqlServer);
|
|
71
|
+
await this.client.connect();
|
|
72
|
+
}
|
|
73
|
+
if (this.parameters.autoCreateTable) {
|
|
74
|
+
await this.ensureTable();
|
|
75
|
+
}
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create the queue table and the index that supports the SKIP LOCKED
|
|
80
|
+
* receive query, if they're missing.
|
|
81
|
+
*/
|
|
82
|
+
async ensureTable() {
|
|
83
|
+
await this.client.query(`
|
|
84
|
+
CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
85
|
+
id BIGSERIAL PRIMARY KEY,
|
|
86
|
+
payload JSONB NOT NULL,
|
|
87
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
88
|
+
locked_until TIMESTAMPTZ
|
|
89
|
+
)
|
|
90
|
+
`);
|
|
91
|
+
// PG rejects STABLE functions like now() in index predicates ("functions
|
|
92
|
+
// in index predicate must be marked IMMUTABLE"), so the partial index
|
|
93
|
+
// covers only the unlocked half. Expired locks fall back to the
|
|
94
|
+
// sequential `locked_until < now()` filter at query time, which scans
|
|
95
|
+
// the (small) set of currently-locked rows. Pending rows — the hot path
|
|
96
|
+
// for healthy receive loops — get the index.
|
|
97
|
+
await this.client.query(`
|
|
98
|
+
CREATE INDEX IF NOT EXISTS ${this.table}_pending_idx
|
|
99
|
+
ON ${this.table} (id)
|
|
100
|
+
WHERE locked_until IS NULL
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @override
|
|
105
|
+
* @param event - the event to enqueue
|
|
106
|
+
*/
|
|
107
|
+
async sendMessage(event) {
|
|
108
|
+
if (!this.client)
|
|
109
|
+
throw new Error("PostgresQueue not connected");
|
|
110
|
+
this.metrics?.messages_sent?.inc();
|
|
111
|
+
await this.client.query(`INSERT INTO ${this.table} (payload) VALUES ($1::jsonb)`, [JSONUtils.stringify(event)]);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* @override
|
|
115
|
+
* @returns count of messages currently visible (pending or expired-lock)
|
|
116
|
+
*/
|
|
117
|
+
async size() {
|
|
118
|
+
if (!this.client)
|
|
119
|
+
return 0;
|
|
120
|
+
const res = await this.client.query(`SELECT COUNT(*)::text AS count FROM ${this.table} WHERE locked_until IS NULL OR locked_until < now()`);
|
|
121
|
+
return Number.parseInt(res.rows[0].count, 10);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* @override
|
|
125
|
+
* @param proto - optional prototype to rehydrate the payload into
|
|
126
|
+
* @returns the locked batch
|
|
127
|
+
*/
|
|
128
|
+
async receiveMessage(proto) {
|
|
129
|
+
if (!this.client)
|
|
130
|
+
throw new Error("PostgresQueue not connected");
|
|
131
|
+
// Atomically lock a batch: lock_until is set to now() + visibilityTimeout
|
|
132
|
+
// for rows whose previous lock has expired (or that are pending). Other
|
|
133
|
+
// workers running this same query in parallel get the SKIP LOCKED
|
|
134
|
+
// semantics from the inner SELECT, so each row goes to exactly one
|
|
135
|
+
// worker per visibility window.
|
|
136
|
+
const visibilityMs = this.parameters.visibilityTimeout * 1000;
|
|
137
|
+
const res = await this.client.query(`
|
|
138
|
+
UPDATE ${this.table}
|
|
139
|
+
SET locked_until = now() + ($1::bigint || ' milliseconds')::interval
|
|
140
|
+
WHERE id IN (
|
|
141
|
+
SELECT id FROM ${this.table}
|
|
142
|
+
WHERE locked_until IS NULL OR locked_until < now()
|
|
143
|
+
ORDER BY id
|
|
144
|
+
FOR UPDATE SKIP LOCKED
|
|
145
|
+
LIMIT $2
|
|
146
|
+
)
|
|
147
|
+
RETURNING id, payload
|
|
148
|
+
`, [visibilityMs, this.parameters.batchSize]);
|
|
149
|
+
return res.rows.map(row => ({
|
|
150
|
+
ReceiptHandle: row.id,
|
|
151
|
+
Message: proto ? Object.assign(new proto(), row.payload) : row.payload
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* @override
|
|
156
|
+
* @param id - the receipt handle returned by {@link receiveMessage}
|
|
157
|
+
*/
|
|
158
|
+
async deleteMessage(id) {
|
|
159
|
+
if (!this.client)
|
|
160
|
+
throw new Error("PostgresQueue not connected");
|
|
161
|
+
await this.client.query(`DELETE FROM ${this.table} WHERE id = $1::bigint`, [id]);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Override the queue's per-receive parallelism: receiveMessage already
|
|
165
|
+
* pulls a batch, so the consumer-spawning loop only needs one parent
|
|
166
|
+
* worker per `batchSize`.
|
|
167
|
+
*
|
|
168
|
+
* @override
|
|
169
|
+
* @returns the result number
|
|
170
|
+
*/
|
|
171
|
+
getMaxConsumers() {
|
|
172
|
+
return Math.max(1, Math.floor(this.parameters.maxConsumers / Math.max(this.parameters.batchSize, 1)));
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Convenience: drop and recreate the queue table. Used by tests; not
|
|
176
|
+
* for production. Mirrors the `__clean` hook on FileQueue.
|
|
177
|
+
*/
|
|
178
|
+
async __clean() {
|
|
179
|
+
if (!this.client)
|
|
180
|
+
return;
|
|
181
|
+
try {
|
|
182
|
+
await this.client.query(`TRUNCATE TABLE ${this.table} RESTART IDENTITY`);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
useLog("WARN", `PostgresQueue truncate failed: ${err.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* @override
|
|
190
|
+
*/
|
|
191
|
+
async stop() {
|
|
192
|
+
if (this.client) {
|
|
193
|
+
const client = this.client;
|
|
194
|
+
const owned = this.ownsPool;
|
|
195
|
+
this.client = undefined;
|
|
196
|
+
this.ownsPool = false;
|
|
197
|
+
if (owned) {
|
|
198
|
+
await releasePool(this.parameters.postgresqlServer);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
await client.end().catch(() => {
|
|
202
|
+
/* already ended */
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
await super.stop();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
export { PostgresQueueService };
|
|
210
|
+
//# sourceMappingURL=postgresqueue.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgresqueue.service.js","sourceRoot":"","sources":["../src/postgresqueue.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { ModelClass, Repository } from "@webda/core";
|
|
2
|
+
import pg, { ClientConfig, PoolConfig } from "pg";
|
|
3
|
+
import { PostgresRepository, SQLStore, SQLStoreParameters } from "./sqlstore.js";
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
export declare class PostgresParameters extends SQLStoreParameters {
|
|
8
|
+
/**
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
usePool?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* By default use environment variables
|
|
14
|
+
*/
|
|
15
|
+
postgresqlServer?: ClientConfig | PoolConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Auto create table if not exists
|
|
18
|
+
* @default true
|
|
19
|
+
*/
|
|
20
|
+
autoCreateTable?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* View name prefix
|
|
23
|
+
* @default ""
|
|
24
|
+
* @pattern /^[a-zA-Z_][a-zA-Z0-9_]*$/
|
|
25
|
+
* @example "app_"
|
|
26
|
+
* @example "v_"
|
|
27
|
+
* @example "view_"
|
|
28
|
+
*/
|
|
29
|
+
viewPrefix?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Regexp patterns of model identifiers to include when generating views
|
|
32
|
+
*
|
|
33
|
+
* @default [".*"]
|
|
34
|
+
*/
|
|
35
|
+
views?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Per-model table name overrides.
|
|
38
|
+
* Maps a model identifier (e.g. "Webda/User") to a custom table name.
|
|
39
|
+
* When not specified, defaults are: primary model → `table`, others → identifier lowercased with "/" → "_".
|
|
40
|
+
*/
|
|
41
|
+
tables?: {
|
|
42
|
+
[modelIdentifier: string]: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* @override
|
|
46
|
+
* @param params - raw parameters
|
|
47
|
+
* @returns this
|
|
48
|
+
*/
|
|
49
|
+
load(params: any): this;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Store data within PostgreSQL with JSONB
|
|
53
|
+
*
|
|
54
|
+
* The table should be created before with
|
|
55
|
+
*
|
|
56
|
+
* ```sql
|
|
57
|
+
* CREATE TABLE IF NOT EXISTS ${tableName}
|
|
58
|
+
* (
|
|
59
|
+
* uuid uuid NOT NULL,
|
|
60
|
+
* data jsonb,
|
|
61
|
+
* CONSTRAINT ${tableName}_pkey PRIMARY KEY (uuid)
|
|
62
|
+
* );
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @WebdaModda
|
|
66
|
+
*/
|
|
67
|
+
export declare class PostgresStore<K extends PostgresParameters = PostgresParameters> extends SQLStore<K> {
|
|
68
|
+
client: pg.Client | pg.Pool;
|
|
69
|
+
/**
|
|
70
|
+
* True when {@link client} is a shared pool acquired from the registry.
|
|
71
|
+
* Tracked so {@link stop} only releases pools it took a refcount on.
|
|
72
|
+
*/
|
|
73
|
+
protected ownsPool: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* @override
|
|
76
|
+
*/
|
|
77
|
+
init(): Promise<this>;
|
|
78
|
+
/**
|
|
79
|
+
* @override
|
|
80
|
+
*/
|
|
81
|
+
stop(): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Resolve the table name for a given model class.
|
|
84
|
+
*
|
|
85
|
+
* Resolution order:
|
|
86
|
+
* 1. `parameters.tables[meta.Identifier]` — explicit per-model override
|
|
87
|
+
* 2. Single-model back-compat: when only one model is configured, `parameters.table` maps to it
|
|
88
|
+
* 3. Default — model identifier lowercased with "/" replaced by "_"
|
|
89
|
+
*
|
|
90
|
+
* @param model - the model class to resolve the table for
|
|
91
|
+
* @returns the table name
|
|
92
|
+
*/
|
|
93
|
+
resolveTable(model: ModelClass): string;
|
|
94
|
+
/**
|
|
95
|
+
* Ensure all managed model tables exist (one per model in the hierarchy).
|
|
96
|
+
* When `autoCreateTable` is false, this is a no-op.
|
|
97
|
+
*/
|
|
98
|
+
checkTable(): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Return the postgresql client
|
|
101
|
+
* @returns the pg client or pool
|
|
102
|
+
*/
|
|
103
|
+
getClient(): import("pg").Client | import("pg").Pool;
|
|
104
|
+
/**
|
|
105
|
+
* Build and return a PostgresRepository for the given model, using the
|
|
106
|
+
* per-model table name resolved by `resolveTable`.
|
|
107
|
+
*
|
|
108
|
+
* The result is cached per model class via `@InstanceCache`.
|
|
109
|
+
* @param model - the model class
|
|
110
|
+
* @returns a repository backed by this store's pg connection
|
|
111
|
+
*/
|
|
112
|
+
getRepository<T extends ModelClass>(model: T): Repository<T>;
|
|
113
|
+
/**
|
|
114
|
+
* Return the underlying PostgresRepository for each model this store manages,
|
|
115
|
+
* unwrapping the EventRepository produced by {@link getRepository}.
|
|
116
|
+
* @returns the per-model PostgresRepository instances
|
|
117
|
+
*/
|
|
118
|
+
getRepositories(): PostgresRepository<any>[];
|
|
119
|
+
/**
|
|
120
|
+
* Create views for each model that is stored in a PostgresStore.
|
|
121
|
+
*
|
|
122
|
+
* Creates one SQL VIEW per model (or model subclass) that maps the JSONB
|
|
123
|
+
* `data` column to typed columns based on the model's stored JSON schema.
|
|
124
|
+
*/
|
|
125
|
+
createViews(): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Delete all rows from all managed tables (used in tests).
|
|
128
|
+
*/
|
|
129
|
+
__clean(): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
export default PostgresStore;
|
|
132
|
+
//# sourceMappingURL=postgresstore.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgresstore.service.d.ts","sourceRoot":"","sources":["../src/postgresstore.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAElD,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAcjF;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,kBAAkB;IACxD;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,gBAAgB,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAC7C;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAEjB;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAE,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAE/C;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAQtB;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,aAAa,CAAC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,CAAE,SAAQ,QAAQ,CAAC,CAAC,CAAC;IAC/F,MAAM,EAAE,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC;IAC5B;;;OAGG;IACH,SAAS,CAAC,QAAQ,UAAS;IAE3B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAiB1B;IAED;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAW1B;IAED;;;;;;;;;;OAUG;IACH,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAetC;IAED;;;OAGG;IACG,UAAU,kBAef;IAED;;;OAGG;IACH,SAAS,4CAER;IAED;;;;;;;OAOG;IAEH,aAAa,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAO3D;IAED;;;;OAIG;IACH,eAAe,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAgB3C;IAED;;;;;OAKG;IACG,WAAW,kBA2DhB;IAED;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAe7B;CACF;eAEc,aAAa"}
|