@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.
@@ -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"}