@keetanetwork/anchor 0.0.33 → 0.0.35

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.
Files changed (61) hide show
  1. package/lib/http-server/index.d.ts +7 -1
  2. package/lib/http-server/index.d.ts.map +1 -1
  3. package/lib/http-server/index.js +2 -0
  4. package/lib/http-server/index.js.map +1 -1
  5. package/lib/queue/common.d.ts +26 -0
  6. package/lib/queue/common.d.ts.map +1 -0
  7. package/lib/queue/common.js +47 -0
  8. package/lib/queue/common.js.map +1 -0
  9. package/lib/queue/drivers/queue_file.d.ts +17 -0
  10. package/lib/queue/drivers/queue_file.d.ts.map +1 -0
  11. package/lib/queue/drivers/queue_file.js +100 -0
  12. package/lib/queue/drivers/queue_file.js.map +1 -0
  13. package/lib/queue/drivers/queue_postgres.d.ts +28 -0
  14. package/lib/queue/drivers/queue_postgres.d.ts.map +1 -0
  15. package/lib/queue/drivers/queue_postgres.js +360 -0
  16. package/lib/queue/drivers/queue_postgres.js.map +1 -0
  17. package/lib/queue/drivers/queue_redis.d.ts +27 -0
  18. package/lib/queue/drivers/queue_redis.d.ts.map +1 -0
  19. package/lib/queue/drivers/queue_redis.js +359 -0
  20. package/lib/queue/drivers/queue_redis.js.map +1 -0
  21. package/lib/queue/drivers/queue_sqlite3.d.ts +28 -0
  22. package/lib/queue/drivers/queue_sqlite3.d.ts.map +1 -0
  23. package/lib/queue/drivers/queue_sqlite3.js +378 -0
  24. package/lib/queue/drivers/queue_sqlite3.js.map +1 -0
  25. package/lib/queue/index.d.ts +341 -0
  26. package/lib/queue/index.d.ts.map +1 -0
  27. package/lib/queue/index.js +946 -0
  28. package/lib/queue/index.js.map +1 -0
  29. package/lib/queue/internal.d.ts +20 -0
  30. package/lib/queue/internal.d.ts.map +1 -0
  31. package/lib/queue/internal.js +66 -0
  32. package/lib/queue/internal.js.map +1 -0
  33. package/lib/queue/pipeline.d.ts +152 -0
  34. package/lib/queue/pipeline.d.ts.map +1 -0
  35. package/lib/queue/pipeline.js +296 -0
  36. package/lib/queue/pipeline.js.map +1 -0
  37. package/lib/resolver.d.ts +1 -1
  38. package/lib/resolver.d.ts.map +1 -1
  39. package/lib/resolver.js.map +1 -1
  40. package/lib/utils/asleep.d.ts +2 -0
  41. package/lib/utils/asleep.d.ts.map +1 -0
  42. package/lib/utils/asleep.js +3 -0
  43. package/lib/utils/asleep.js.map +1 -0
  44. package/lib/utils/defer.d.ts +4 -0
  45. package/lib/utils/defer.d.ts.map +1 -0
  46. package/lib/utils/defer.js +3 -0
  47. package/lib/utils/defer.js.map +1 -0
  48. package/npm-shrinkwrap.json +2 -2
  49. package/package.json +1 -1
  50. package/services/fx/client.d.ts +1 -1
  51. package/services/fx/client.d.ts.map +1 -1
  52. package/services/fx/client.js +2 -2
  53. package/services/fx/client.js.map +1 -1
  54. package/services/fx/common.d.ts +19 -4
  55. package/services/fx/common.d.ts.map +1 -1
  56. package/services/fx/common.js +8 -5
  57. package/services/fx/common.js.map +1 -1
  58. package/services/fx/server.d.ts +105 -8
  59. package/services/fx/server.d.ts.map +1 -1
  60. package/services/fx/server.js +609 -43
  61. package/services/fx/server.js.map +1 -1
@@ -0,0 +1,360 @@
1
+ import { MethodLogger, ManageStatusUpdates, ConvertStringToRequestID } from '../internal.js';
2
+ import { Errors } from '../common.js';
3
+ import { asleep } from '../../utils/asleep.js';
4
+ export default class KeetaAnchorQueueStorageDriverPostgres {
5
+ logger;
6
+ poolInternal = null;
7
+ dbInitializationPromise = null;
8
+ name = 'KeetaAnchorQueueStorageDriverPostgres';
9
+ id;
10
+ path = [];
11
+ pathStr;
12
+ constructor(options) {
13
+ this.id = options?.id ?? crypto.randomUUID();
14
+ this.logger = options?.logger;
15
+ this.poolInternal = options.pool;
16
+ this.path = options.path ?? [];
17
+ this.pathStr = ['root', ...this.path].join('.');
18
+ Object.freeze(this.path);
19
+ this.methodLogger('new')?.debug('Initialized Postgres queue storage driver');
20
+ }
21
+ async initializeDBConnection(pool) {
22
+ const logger = this.methodLogger('initializeDBConnection');
23
+ if (this.dbInitializationPromise !== null) {
24
+ logger?.debug('DB schema initialization already in progress or completed, waiting for it to finish');
25
+ await this.dbInitializationPromise;
26
+ return (pool);
27
+ }
28
+ this.dbInitializationPromise = (async () => {
29
+ logger?.debug('Initializing DB schema for queue storage driver');
30
+ const client = await pool.connect();
31
+ try {
32
+ await client.query(`
33
+ CREATE TABLE IF NOT EXISTS queue_entries (
34
+ id TEXT NOT NULL,
35
+ path TEXT NOT NULL,
36
+ request TEXT NOT NULL,
37
+ output TEXT,
38
+ last_error TEXT,
39
+ status TEXT NOT NULL,
40
+ created BIGINT NOT NULL,
41
+ updated BIGINT NOT NULL,
42
+ worker BIGINT,
43
+ failures INTEGER NOT NULL DEFAULT 0,
44
+ PRIMARY KEY (id, path)
45
+ )`);
46
+ await client.query(`
47
+ CREATE TABLE IF NOT EXISTS queue_idempotent_keys (
48
+ entry_id TEXT NOT NULL,
49
+ idempotent_id TEXT NOT NULL,
50
+ path TEXT NOT NULL,
51
+ UNIQUE (idempotent_id, path),
52
+ PRIMARY KEY (entry_id, idempotent_id, path),
53
+ FOREIGN KEY (entry_id, path) REFERENCES queue_entries(id, path)
54
+ )`);
55
+ await client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_status ON queue_entries(status)');
56
+ await client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_updated ON queue_entries(updated)');
57
+ await client.query('CREATE INDEX IF NOT EXISTS idx_queue_idempotent_keys_idempotent_id ON queue_idempotent_keys(idempotent_id)');
58
+ }
59
+ finally {
60
+ client.release();
61
+ }
62
+ logger?.debug('Completed DB schema initialization for queue storage driver');
63
+ return (true);
64
+ })();
65
+ await this.dbInitializationPromise;
66
+ return (pool);
67
+ }
68
+ methodLogger(method) {
69
+ return (MethodLogger(this.logger, {
70
+ class: 'KeetaAnchorQueueStorageDriverPostgres',
71
+ file: 'src/lib/queue/drivers/queue_postgres.ts',
72
+ method: method,
73
+ instanceID: this.id
74
+ }));
75
+ }
76
+ async runWithRetry(fn) {
77
+ const logger = this.methodLogger('runWithRetry');
78
+ let lastError;
79
+ for (let retry = 0; retry < 16; retry++) {
80
+ if (this.poolInternal === null) {
81
+ this.methodLogger('runWithRetry')?.debug('Aborting DB operation retries because the instance was destroyed');
82
+ if (lastError !== undefined) {
83
+ // eslint-disable-next-line @typescript-eslint/only-throw-error
84
+ throw (lastError);
85
+ }
86
+ throw (new Error('Aborting because the instance was destroyed'));
87
+ }
88
+ try {
89
+ return (await fn());
90
+ }
91
+ catch (error) {
92
+ lastError = error;
93
+ if (error instanceof Error) {
94
+ const errorCode = 'code' in error ? error.code : null;
95
+ if (errorCode === '40001' || errorCode === '40P01') {
96
+ logger?.debug('Serialization failure or deadlock detected');
97
+ const minBackoff = 100;
98
+ const maxBackoff = 30_000;
99
+ const backoffIntervalSize = Math.min(maxBackoff - minBackoff, (retry + 50) ** 2);
100
+ const backoff = Math.round((Math.random() * backoffIntervalSize)) + minBackoff;
101
+ this.methodLogger('runWithRetry')?.debug(`Retrying DB operation in ${backoff}ms (retry #${retry}) from`, new Error().stack);
102
+ await asleep(backoff);
103
+ continue;
104
+ }
105
+ }
106
+ throw (error);
107
+ }
108
+ }
109
+ throw (lastError);
110
+ }
111
+ async newDBConnection() {
112
+ if (this.poolInternal === null) {
113
+ throw (new Error('Database connection is not available'));
114
+ }
115
+ return (await this.runWithRetry(async () => {
116
+ if (this.poolInternal === null) {
117
+ throw (new Error('Database connection is not available'));
118
+ }
119
+ this.methodLogger('newDBConnection')?.debug('Getting DB pool');
120
+ const pool = await this.poolInternal();
121
+ return (await this.initializeDBConnection(pool));
122
+ }));
123
+ }
124
+ async dbTransaction(className, fn) {
125
+ const pool = await this.newDBConnection();
126
+ const logger = this.methodLogger(className);
127
+ const result = await this.runWithRetry(async function () {
128
+ const client = await pool.connect();
129
+ try {
130
+ logger?.debug('Starting DB transaction');
131
+ await client.query('BEGIN');
132
+ logger?.debug('DB transaction started');
133
+ const retval = await fn(client, logger);
134
+ logger?.debug('Committing DB transaction');
135
+ await client.query('COMMIT');
136
+ logger?.debug('DB transaction committed');
137
+ return (retval);
138
+ }
139
+ catch (error) {
140
+ try {
141
+ logger?.debug('Rolling back DB transaction due to error:', error);
142
+ await client.query('ROLLBACK');
143
+ logger?.debug('DB transaction rolled back');
144
+ }
145
+ catch {
146
+ logger?.debug('Error rolling back DB transaction !!');
147
+ }
148
+ throw (error);
149
+ }
150
+ finally {
151
+ client.release();
152
+ }
153
+ });
154
+ return (result);
155
+ }
156
+ async add(request, info) {
157
+ return (await this.dbTransaction('add', async (client, logger) => {
158
+ let entryID = ConvertStringToRequestID(info?.id);
159
+ if (entryID) {
160
+ const existingEntry = await client.query('SELECT id FROM queue_entries WHERE id = $1 AND path = $2', [entryID, this.pathStr]);
161
+ if (existingEntry.rows.length > 0) {
162
+ logger?.debug(`Request with id ${String(entryID)} already exists, ignoring`);
163
+ return (entryID);
164
+ }
165
+ }
166
+ const idempotentIDs = info?.idempotentKeys;
167
+ if (idempotentIDs) {
168
+ const matchingIdempotentEntries = new Set();
169
+ for (const idempotentID of idempotentIDs) {
170
+ const idempotentEntryExists = await client.query('SELECT idempotent_id FROM queue_idempotent_keys WHERE idempotent_id = $1 AND path = $2', [idempotentID, this.pathStr]);
171
+ if (idempotentEntryExists.rows.length > 0) {
172
+ matchingIdempotentEntries.add(idempotentID);
173
+ }
174
+ }
175
+ if (matchingIdempotentEntries.size !== 0) {
176
+ throw (new Errors.IdempotentExistsError('One or more idempotent entries already exist in the queue', matchingIdempotentEntries));
177
+ }
178
+ }
179
+ entryID ??= ConvertStringToRequestID(crypto.randomUUID());
180
+ logger?.debug(`Enqueuing request with id ${String(entryID)}`);
181
+ const currentTime = Date.now();
182
+ const requestJSON = JSON.stringify(request);
183
+ /**
184
+ * The status to use for the new entry
185
+ */
186
+ const status = info?.status ?? 'pending';
187
+ try {
188
+ await client.query(`INSERT INTO queue_entries (id, path, request, output, last_error, status, created, updated, worker, failures)
189
+ VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6, NULL, 0)`, [entryID, this.pathStr, requestJSON, status, currentTime, currentTime]);
190
+ if (idempotentIDs && idempotentIDs.size > 0) {
191
+ for (const idempotentID of idempotentIDs) {
192
+ await client.query('INSERT INTO queue_idempotent_keys (entry_id, path, idempotent_id) VALUES ($1, $2, $3)', [entryID, this.pathStr, idempotentID]);
193
+ }
194
+ }
195
+ }
196
+ catch (error) {
197
+ if (error instanceof Error && error.message.includes('duplicate key') && idempotentIDs) {
198
+ throw (new Errors.IdempotentExistsError('One or more idempotent entries already exist in the queue', idempotentIDs));
199
+ }
200
+ throw (error);
201
+ }
202
+ return (entryID);
203
+ }));
204
+ }
205
+ async setStatus(id, status, ancillary) {
206
+ const { oldStatus } = ancillary ?? {};
207
+ return (await this.dbTransaction('setStatus', async (client, logger) => {
208
+ const existingEntry = await client.query('SELECT status, failures, last_error, output FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);
209
+ if (existingEntry.rows.length === 0) {
210
+ throw (new Error(`Request with ID ${String(id)} not found`));
211
+ }
212
+ const currentEntry = existingEntry.rows[0];
213
+ if (!currentEntry) {
214
+ throw (new Error(`Request with ID ${String(id)} not found`));
215
+ }
216
+ const newEntry = ManageStatusUpdates(id, currentEntry, status, ancillary, logger);
217
+ const currentTime = newEntry.updated.getTime();
218
+ const workerValue = newEntry.worker;
219
+ const newFailures = newEntry.failures ?? currentEntry.failures;
220
+ const newLastError = newEntry.lastError !== undefined ? newEntry.lastError : currentEntry.last_error;
221
+ const newOutput = newEntry.output !== undefined ? JSON.stringify(newEntry.output) : currentEntry.output;
222
+ let updateQuery;
223
+ let updateParams;
224
+ if (oldStatus) {
225
+ updateQuery = `UPDATE queue_entries
226
+ SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6
227
+ WHERE id = $7 AND path = $8 AND status = $9`;
228
+ updateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr, oldStatus];
229
+ }
230
+ else {
231
+ updateQuery = `UPDATE queue_entries
232
+ SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6
233
+ WHERE id = $7 AND path = $8`;
234
+ updateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr];
235
+ }
236
+ const result = await client.query(updateQuery, updateParams);
237
+ if (oldStatus && result.rowCount === 0) {
238
+ const currentEntry = await client.query('SELECT status FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);
239
+ const currentStatus = currentEntry.rows[0]?.status;
240
+ if (currentEntry.rows.length > 0) {
241
+ if (currentStatus === undefined) {
242
+ throw (new Error(`internal error: could not retrieve current status for request with ID ${String(id)}`));
243
+ }
244
+ throw (new Errors.IncorrectStateAssertedError(id, oldStatus, currentStatus));
245
+ }
246
+ else {
247
+ throw (new Error(`Request with ID ${String(id)} not found`));
248
+ }
249
+ }
250
+ }));
251
+ }
252
+ async get(id) {
253
+ return (await this.dbTransaction('get', async (client) => {
254
+ const row = await client.query(`SELECT id, request, output, last_error, status, created, updated, worker, failures
255
+ FROM queue_entries WHERE id = $1 AND path = $2`, [id, this.pathStr]);
256
+ if (row.rows.length === 0) {
257
+ return (null);
258
+ }
259
+ const entry = row.rows[0];
260
+ if (!entry) {
261
+ return (null);
262
+ }
263
+ const idempotentRows = await client.query('SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2', [id, this.pathStr]);
264
+ const idempotentKeys = idempotentRows.rows.length > 0
265
+ ? new Set(idempotentRows.rows.map(function (idempotentRow) {
266
+ return (ConvertStringToRequestID(idempotentRow.idempotent_id));
267
+ }))
268
+ : undefined;
269
+ return ({
270
+ id: ConvertStringToRequestID(entry.id),
271
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
272
+ request: JSON.parse(entry.request),
273
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
274
+ output: entry.output ? JSON.parse(entry.output) : null,
275
+ lastError: entry.last_error,
276
+ status: entry.status,
277
+ created: new Date(Number(entry.created)),
278
+ updated: new Date(Number(entry.updated)),
279
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
280
+ worker: entry.worker,
281
+ failures: entry.failures,
282
+ idempotentKeys: idempotentKeys
283
+ });
284
+ }));
285
+ }
286
+ async query(filter) {
287
+ return (await this.dbTransaction('query', async (client, logger) => {
288
+ logger?.debug(`Querying queue with id ${this.id} with filter:`, filter);
289
+ const conditions = [];
290
+ const params = [];
291
+ let paramIndex = 1;
292
+ conditions.push(`path = $${paramIndex++}`);
293
+ params.push(this.pathStr);
294
+ if (filter?.status) {
295
+ conditions.push(`status = $${paramIndex++}`);
296
+ params.push(filter.status);
297
+ }
298
+ if (filter?.updatedBefore) {
299
+ conditions.push(`updated < $${paramIndex++}`);
300
+ params.push(filter.updatedBefore.getTime());
301
+ }
302
+ let query = 'SELECT id, request, output, last_error, status, created, updated, worker, failures FROM queue_entries';
303
+ if (conditions.length > 0) {
304
+ query += ' WHERE ' + conditions.join(' AND ');
305
+ }
306
+ if (filter?.limit !== undefined) {
307
+ query += ` LIMIT $${paramIndex++}`;
308
+ params.push(filter.limit);
309
+ }
310
+ const rows = await client.query(query, params);
311
+ const entries = [];
312
+ for (const row of rows.rows) {
313
+ const idempotentRows = await client.query('SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2', [row.id, this.pathStr]);
314
+ const idempotentKeys = idempotentRows.rows.length > 0
315
+ ? new Set(idempotentRows.rows.map(function (idempotentRow) {
316
+ return (ConvertStringToRequestID(idempotentRow.idempotent_id));
317
+ }))
318
+ : undefined;
319
+ entries.push({
320
+ id: ConvertStringToRequestID(row.id),
321
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
322
+ request: JSON.parse(row.request),
323
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
324
+ output: row.output ? JSON.parse(row.output) : null,
325
+ lastError: row.last_error,
326
+ status: row.status,
327
+ created: new Date(Number(row.created)),
328
+ updated: new Date(Number(row.updated)),
329
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
330
+ worker: row.worker,
331
+ failures: row.failures,
332
+ idempotentKeys: idempotentKeys
333
+ });
334
+ }
335
+ logger?.debug(`Queried queue with id ${this.id} with filter:`, filter, '-- found', entries.length, 'entries');
336
+ return (entries);
337
+ }));
338
+ }
339
+ async partition(path) {
340
+ this.methodLogger('partition')?.debug(`Creating partitioned queue storage driver for path: ${path}`);
341
+ if (this.poolInternal === null) {
342
+ throw (new Error('Asked to partition but the instance has been destroyed'));
343
+ }
344
+ const retval = new KeetaAnchorQueueStorageDriverPostgres({
345
+ id: `${this.id}::${path}`,
346
+ logger: this.logger,
347
+ pool: this.poolInternal,
348
+ path: [...this.path, path]
349
+ });
350
+ return (retval);
351
+ }
352
+ async destroy() {
353
+ this.methodLogger('destroy')?.debug('Destroying instance');
354
+ this.poolInternal = null;
355
+ }
356
+ async [Symbol.asyncDispose]() {
357
+ return (await this.destroy());
358
+ }
359
+ }
360
+ //# sourceMappingURL=queue_postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue_postgres.js","sourceRoot":"","sources":["../../../../src/lib/queue/drivers/queue_postgres.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tKeetaAnchorQueueStorageDriver,\n\tKeetaAnchorQueueStorageDriverConstructor,\n\tKeetaAnchorQueueRequest,\n\tKeetaAnchorQueueRequestID,\n\tKeetaAnchorQueueEntry,\n\tKeetaAnchorQueueEntryExtra,\n\tKeetaAnchorQueueEntryAncillaryData,\n\tKeetaAnchorQueueStatus,\n\tKeetaAnchorQueueFilter,\n\tKeetaAnchorQueueWorkerID\n} from '../index.ts';\nimport {\n\tMethodLogger,\n\tManageStatusUpdates,\n\tConvertStringToRequestID\n} from '../internal.js';\nimport { Errors } from '../common.js';\n\nimport { asleep } from '../../utils/asleep.js';\n\nimport type { Logger } from '../../log/index.ts';\nimport type { JSONSerializable } from '../../utils/json.js';\n\nimport type * as pg from 'pg';\n\ntype QueueEntryRow = {\n\tid: string;\n\trequest: string;\n\toutput: string | null;\n\tlast_error: string | null;\n\tstatus: KeetaAnchorQueueStatus;\n\tcreated: Date;\n\tupdated: Date;\n\tworker: number | null;\n\tfailures: number;\n};\n\ntype IdempotentRow = {\n\tidempotent_id: string;\n};\n\nexport default class KeetaAnchorQueueStorageDriverPostgres<QueueRequest extends JSONSerializable = JSONSerializable, QueueResult extends JSONSerializable = JSONSerializable> implements KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult> {\n\tprivate readonly logger: Logger | undefined;\n\tprivate poolInternal: (() => Promise<pg.Pool>) | null = null;\n\tprivate dbInitializationPromise: Promise<boolean> | null = null;\n\n\treadonly name = 'KeetaAnchorQueueStorageDriverPostgres';\n\treadonly id: string;\n\treadonly path: string[] = [];\n\tprivate readonly pathStr: string;\n\n\tconstructor(options: NonNullable<ConstructorParameters<KeetaAnchorQueueStorageDriverConstructor<QueueRequest, QueueResult>>[0]> & { pool: () => Promise<pg.Pool>; }) {\n\t\tthis.id = options?.id ?? crypto.randomUUID();\n\t\tthis.logger = options?.logger\n\t\tthis.poolInternal = options.pool;\n\t\tthis.path = options.path ?? [];\n\t\tthis.pathStr = ['root', ...this.path].join('.');\n\t\tObject.freeze(this.path);\n\n\t\tthis.methodLogger('new')?.debug('Initialized Postgres queue storage driver');\n\t}\n\n\tprivate async initializeDBConnection(pool: pg.Pool): Promise<pg.Pool> {\n\t\tconst logger = this.methodLogger('initializeDBConnection');\n\n\t\tif (this.dbInitializationPromise !== null) {\n\t\t\tlogger?.debug('DB schema initialization already in progress or completed, waiting for it to finish');\n\n\t\t\tawait this.dbInitializationPromise;\n\t\t\treturn(pool);\n\t\t}\n\n\t\tthis.dbInitializationPromise = (async () => {\n\t\t\tlogger?.debug('Initializing DB schema for queue storage driver');\n\n\t\t\tconst client = await pool.connect();\n\t\t\ttry {\n\t\t\t\tawait client.query(`\n\t\t\t\t\tCREATE TABLE IF NOT EXISTS queue_entries (\n\t\t\t\t\t\tid TEXT NOT NULL,\n\t\t\t\t\t\tpath TEXT NOT NULL,\n\t\t\t\t\t\trequest TEXT NOT NULL,\n\t\t\t\t\t\toutput TEXT,\n\t\t\t\t\t\tlast_error TEXT,\n\t\t\t\t\t\tstatus TEXT NOT NULL,\n\t\t\t\t\t\tcreated BIGINT NOT NULL,\n\t\t\t\t\t\tupdated BIGINT NOT NULL,\n\t\t\t\t\t\tworker BIGINT,\n\t\t\t\t\t\tfailures INTEGER NOT NULL DEFAULT 0,\n\t\t\t\t\t\tPRIMARY KEY (id, path)\n\t\t\t\t\t)`);\n\n\t\t\t\tawait client.query(`\n\t\t\t\t\tCREATE TABLE IF NOT EXISTS queue_idempotent_keys (\n\t\t\t\t\t\tentry_id TEXT NOT NULL,\n\t\t\t\t\t\tidempotent_id TEXT NOT NULL,\n\t\t\t\t\t\tpath TEXT NOT NULL,\n\t\t\t\t\t\tUNIQUE (idempotent_id, path),\n\t\t\t\t\t\tPRIMARY KEY (entry_id, idempotent_id, path),\n\t\t\t\t\t\tFOREIGN KEY (entry_id, path) REFERENCES queue_entries(id, path)\n\t\t\t\t\t)`);\n\n\t\t\t\tawait client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_status ON queue_entries(status)');\n\t\t\t\tawait client.query('CREATE INDEX IF NOT EXISTS idx_queue_entries_updated ON queue_entries(updated)');\n\t\t\t\tawait client.query('CREATE INDEX IF NOT EXISTS idx_queue_idempotent_keys_idempotent_id ON queue_idempotent_keys(idempotent_id)');\n\t\t\t} finally {\n\t\t\t\tclient.release();\n\t\t\t}\n\n\t\t\tlogger?.debug('Completed DB schema initialization for queue storage driver');\n\n\t\t\treturn(true);\n\t\t})();\n\n\t\tawait this.dbInitializationPromise;\n\n\t\treturn(pool);\n\t}\n\n\tprivate methodLogger(method: string): Logger | undefined {\n\t\treturn(MethodLogger(this.logger, {\n\t\t\tclass: 'KeetaAnchorQueueStorageDriverPostgres',\n\t\t\tfile: 'src/lib/queue/drivers/queue_postgres.ts',\n\t\t\tmethod: method,\n\t\t\tinstanceID: this.id\n\t\t}));\n\t}\n\n\tprivate async runWithRetry<T>(fn: () => Promise<T>): Promise<T> {\n\t\tconst logger = this.methodLogger('runWithRetry');\n\n\t\tlet lastError: unknown;\n\t\tfor (let retry = 0; retry < 16; retry++) {\n\t\t\tif (this.poolInternal === null) {\n\t\t\t\tthis.methodLogger('runWithRetry')?.debug('Aborting DB operation retries because the instance was destroyed');\n\n\t\t\t\tif (lastError !== undefined) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\t\t\tthrow(lastError);\n\t\t\t\t}\n\t\t\t\tthrow(new Error('Aborting because the instance was destroyed'));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn(await fn());\n\t\t\t} catch (error: unknown) {\n\t\t\t\tlastError = error;\n\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tconst errorCode = 'code' in error ? error.code : null;\n\t\t\t\t\tif (errorCode === '40001' || errorCode === '40P01') {\n\t\t\t\t\t\tlogger?.debug('Serialization failure or deadlock detected');\n\n\t\t\t\t\t\tconst minBackoff = 100;\n\t\t\t\t\t\tconst maxBackoff = 30_000;\n\t\t\t\t\t\tconst backoffIntervalSize = Math.min(maxBackoff - minBackoff, (retry + 50) ** 2);\n\t\t\t\t\t\tconst backoff = Math.round((Math.random() * backoffIntervalSize)) + minBackoff;\n\n\t\t\t\t\t\tthis.methodLogger('runWithRetry')?.debug(`Retrying DB operation in ${backoff}ms (retry #${retry}) from`, new Error().stack);\n\t\t\t\t\t\tawait asleep(backoff);\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthrow(error);\n\t\t\t}\n\t\t}\n\t\tthrow(lastError);\n\t}\n\n\tprivate async newDBConnection(): Promise<pg.Pool> {\n\t\tif (this.poolInternal === null) {\n\t\t\tthrow(new Error('Database connection is not available'));\n\t\t}\n\n\t\treturn(await this.runWithRetry(async () => {\n\t\t\tif (this.poolInternal === null) {\n\t\t\t\tthrow(new Error('Database connection is not available'));\n\t\t\t}\n\n\t\t\tthis.methodLogger('newDBConnection')?.debug('Getting DB pool');\n\t\t\tconst pool = await this.poolInternal();\n\n\t\t\treturn(await this.initializeDBConnection(pool));\n\t\t}));\n\t}\n\n\tprivate async dbTransaction<T>(className: string, fn: (client: pg.PoolClient, logger: Logger | undefined) => Promise<T>): Promise<T> {\n\t\tconst pool = await this.newDBConnection();\n\t\tconst logger = this.methodLogger(className);\n\n\t\tconst result = await this.runWithRetry(async function() {\n\t\t\tconst client = await pool.connect();\n\n\t\t\ttry {\n\t\t\t\tlogger?.debug('Starting DB transaction');\n\t\t\t\tawait client.query('BEGIN');\n\t\t\t\tlogger?.debug('DB transaction started');\n\n\t\t\t\tconst retval = await fn(client, logger);\n\n\t\t\t\tlogger?.debug('Committing DB transaction');\n\t\t\t\tawait client.query('COMMIT');\n\t\t\t\tlogger?.debug('DB transaction committed');\n\n\t\t\t\treturn(retval);\n\t\t\t} catch (error: unknown) {\n\t\t\t\ttry {\n\t\t\t\t\tlogger?.debug('Rolling back DB transaction due to error:', error);\n\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\tlogger?.debug('DB transaction rolled back');\n\t\t\t\t} catch {\n\t\t\t\t\tlogger?.debug('Error rolling back DB transaction !!');\n\t\t\t\t}\n\t\t\t\tthrow(error);\n\t\t\t} finally {\n\t\t\t\tclient.release();\n\t\t\t}\n\t\t});\n\n\t\treturn(result);\n\t}\n\n\tasync add(request: KeetaAnchorQueueRequest<QueueRequest>, info?: KeetaAnchorQueueEntryExtra): Promise<KeetaAnchorQueueRequestID> {\n\t\treturn(await this.dbTransaction('add', async (client, logger): Promise<KeetaAnchorQueueRequestID> => {\n\t\t\tlet entryID = ConvertStringToRequestID(info?.id);\n\t\t\tif (entryID) {\n\t\t\t\tconst existingEntry = await client.query<{ id: string }>('SELECT id FROM queue_entries WHERE id = $1 AND path = $2', [entryID, this.pathStr]);\n\t\t\t\tif (existingEntry.rows.length > 0) {\n\t\t\t\t\tlogger?.debug(`Request with id ${String(entryID)} already exists, ignoring`);\n\t\t\t\t\treturn(entryID);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst idempotentIDs = info?.idempotentKeys;\n\t\t\tif (idempotentIDs) {\n\t\t\t\tconst matchingIdempotentEntries = new Set<KeetaAnchorQueueRequestID>();\n\t\t\t\tfor (const idempotentID of idempotentIDs) {\n\t\t\t\t\tconst idempotentEntryExists = await client.query<IdempotentRow>(\n\t\t\t\t\t\t'SELECT idempotent_id FROM queue_idempotent_keys WHERE idempotent_id = $1 AND path = $2',\n\t\t\t\t\t\t[idempotentID, this.pathStr]\n\t\t\t\t\t);\n\t\t\t\t\tif (idempotentEntryExists.rows.length > 0) {\n\t\t\t\t\t\tmatchingIdempotentEntries.add(idempotentID);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (matchingIdempotentEntries.size !== 0) {\n\t\t\t\t\tthrow(new Errors.IdempotentExistsError('One or more idempotent entries already exist in the queue', matchingIdempotentEntries));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tentryID ??= ConvertStringToRequestID(crypto.randomUUID());\n\n\t\t\tlogger?.debug(`Enqueuing request with id ${String(entryID)}`);\n\n\t\t\tconst currentTime = Date.now();\n\t\t\tconst requestJSON = JSON.stringify(request);\n\n\t\t\t/**\n\t\t\t * The status to use for the new entry\n\t\t\t */\n\t\t\tconst status = info?.status ?? 'pending';\n\n\t\t\ttry {\n\t\t\t\tawait client.query(\n\t\t\t\t\t`INSERT INTO queue_entries (id, path, request, output, last_error, status, created, updated, worker, failures)\n\t\t\t\t\t VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6, NULL, 0)`,\n\t\t\t\t\t[entryID, this.pathStr, requestJSON, status, currentTime, currentTime]\n\t\t\t\t);\n\n\t\t\t\tif (idempotentIDs && idempotentIDs.size > 0) {\n\t\t\t\t\tfor (const idempotentID of idempotentIDs) {\n\t\t\t\t\t\tawait client.query('INSERT INTO queue_idempotent_keys (entry_id, path, idempotent_id) VALUES ($1, $2, $3)', [entryID, this.pathStr, idempotentID]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error: unknown) {\n\t\t\t\tif (error instanceof Error && error.message.includes('duplicate key') && idempotentIDs) {\n\t\t\t\t\tthrow(new Errors.IdempotentExistsError('One or more idempotent entries already exist in the queue', idempotentIDs));\n\t\t\t\t}\n\t\t\t\tthrow(error);\n\t\t\t}\n\n\t\t\treturn(entryID);\n\t\t}));\n\t}\n\n\tasync setStatus(id: KeetaAnchorQueueRequestID, status: KeetaAnchorQueueStatus, ancillary?: KeetaAnchorQueueEntryAncillaryData<QueueResult>): Promise<void> {\n\t\tconst { oldStatus } = ancillary ?? {};\n\n\t\treturn(await this.dbTransaction('setStatus', async (client, logger): Promise<void> => {\n\t\t\tconst existingEntry = await client.query<{ status: KeetaAnchorQueueStatus; failures: number; last_error: string | null; output: string | null }>('SELECT status, failures, last_error, output FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);\n\t\t\tif (existingEntry.rows.length === 0) {\n\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t}\n\n\t\t\tconst currentEntry = existingEntry.rows[0];\n\t\t\tif (!currentEntry) {\n\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t}\n\n\t\t\tconst newEntry = ManageStatusUpdates<QueueResult>(id, currentEntry, status, ancillary, logger);\n\t\t\tconst currentTime = newEntry.updated.getTime();\n\t\t\tconst workerValue = newEntry.worker;\n\t\t\tconst newFailures = newEntry.failures ?? currentEntry.failures;\n\t\t\tconst newLastError = newEntry.lastError !== undefined ? newEntry.lastError : currentEntry.last_error;\n\t\t\tconst newOutput = newEntry.output !== undefined ? JSON.stringify(newEntry.output) : currentEntry.output;\n\n\t\t\tlet updateQuery: string;\n\t\t\tlet updateParams: (KeetaAnchorQueueRequestID | string | number | null)[];\n\n\t\t\tif (oldStatus) {\n\t\t\t\tupdateQuery = `UPDATE queue_entries\n\t\t\t\t SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6\n\t\t\t\t WHERE id = $7 AND path = $8 AND status = $9`;\n\t\t\t\tupdateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr, oldStatus];\n\t\t\t} else {\n\t\t\t\tupdateQuery = `UPDATE queue_entries\n\t\t\t\t SET status = $1, updated = $2, worker = $3, failures = $4, last_error = $5, output = $6\n\t\t\t\t WHERE id = $7 AND path = $8`;\n\t\t\t\tupdateParams = [status, currentTime, workerValue, newFailures, newLastError, newOutput, id, this.pathStr];\n\t\t\t}\n\n\t\t\tconst result = await client.query(updateQuery, updateParams);\n\n\t\t\tif (oldStatus && result.rowCount === 0) {\n\t\t\t\tconst currentEntry = await client.query<{ status: KeetaAnchorQueueStatus }>('SELECT status FROM queue_entries WHERE id = $1 AND path = $2', [id, this.pathStr]);\n\t\t\t\tconst currentStatus = currentEntry.rows[0]?.status;\n\t\t\t\tif (currentEntry.rows.length > 0) {\n\t\t\t\t\tif (currentStatus === undefined) {\n\t\t\t\t\t\tthrow(new Error(`internal error: could not retrieve current status for request with ID ${String(id)}`));\n\t\t\t\t\t}\n\t\t\t\t\tthrow(new Errors.IncorrectStateAssertedError(id, oldStatus, currentStatus));\n\t\t\t\t} else {\n\t\t\t\t\tthrow(new Error(`Request with ID ${String(id)} not found`));\n\t\t\t\t}\n\t\t\t}\n\t\t}));\n\t}\n\n\tasync get(id: KeetaAnchorQueueRequestID): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult> | null> {\n\t\treturn(await this.dbTransaction('get', async (client): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult> | null> => {\n\t\t\tconst row = await client.query<QueueEntryRow>(\n\t\t\t\t`SELECT id, request, output, last_error, status, created, updated, worker, failures\n\t\t\t\t FROM queue_entries WHERE id = $1 AND path = $2`,\n\t\t\t\t[id, this.pathStr]\n\t\t\t);\n\n\t\t\tif (row.rows.length === 0) {\n\t\t\t\treturn(null);\n\t\t\t}\n\n\t\t\tconst entry = row.rows[0];\n\t\t\tif (!entry) {\n\t\t\t\treturn(null);\n\t\t\t}\n\n\t\t\tconst idempotentRows = await client.query<IdempotentRow>(\n\t\t\t\t'SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2',\n\t\t\t\t[id, this.pathStr]\n\t\t\t);\n\n\t\t\tconst idempotentKeys = idempotentRows.rows.length > 0\n\t\t\t\t? new Set(idempotentRows.rows.map(function(idempotentRow: IdempotentRow) {\n\t\t\t\t\treturn(ConvertStringToRequestID(idempotentRow.idempotent_id));\n\t\t\t\t}))\n\t\t\t\t: undefined;\n\n\t\t\treturn({\n\t\t\t\tid: ConvertStringToRequestID(entry.id),\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\trequest: JSON.parse(entry.request) as QueueRequest,\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\toutput: entry.output ? JSON.parse(entry.output) as QueueResult : null,\n\t\t\t\tlastError: entry.last_error,\n\t\t\t\tstatus: entry.status,\n\t\t\t\tcreated: new Date(Number(entry.created)),\n\t\t\t\tupdated: new Date(Number(entry.updated)),\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\tworker: entry.worker as unknown as KeetaAnchorQueueWorkerID | null,\n\t\t\t\tfailures: entry.failures,\n\t\t\t\tidempotentKeys: idempotentKeys\n\t\t\t});\n\t\t}));\n\t}\n\n\tasync query(filter?: KeetaAnchorQueueFilter): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult>[]> {\n\t\treturn(await this.dbTransaction('query', async (client, logger): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult>[]> => {\n\t\t\tlogger?.debug(`Querying queue with id ${this.id} with filter:`, filter);\n\n\t\t\tconst conditions: string[] = [];\n\t\t\tconst params: (string | number)[] = [];\n\t\t\tlet paramIndex = 1;\n\n\t\t\tconditions.push(`path = $${paramIndex++}`);\n\t\t\tparams.push(this.pathStr);\n\n\t\t\tif (filter?.status) {\n\t\t\t\tconditions.push(`status = $${paramIndex++}`);\n\t\t\t\tparams.push(filter.status);\n\t\t\t}\n\n\t\t\tif (filter?.updatedBefore) {\n\t\t\t\tconditions.push(`updated < $${paramIndex++}`);\n\t\t\t\tparams.push(filter.updatedBefore.getTime());\n\t\t\t}\n\n\t\t\tlet query = 'SELECT id, request, output, last_error, status, created, updated, worker, failures FROM queue_entries';\n\n\t\t\tif (conditions.length > 0) {\n\t\t\t\tquery += ' WHERE ' + conditions.join(' AND ');\n\t\t\t}\n\n\t\t\tif (filter?.limit !== undefined) {\n\t\t\t\tquery += ` LIMIT $${paramIndex++}`;\n\t\t\t\tparams.push(filter.limit);\n\t\t\t}\n\n\t\t\tconst rows = await client.query<QueueEntryRow>(query, params);\n\n\t\t\tconst entries: KeetaAnchorQueueEntry<QueueRequest, QueueResult>[] = [];\n\n\t\t\tfor (const row of rows.rows) {\n\t\t\t\tconst idempotentRows = await client.query<IdempotentRow>(\n\t\t\t\t\t'SELECT idempotent_id FROM queue_idempotent_keys WHERE entry_id = $1 AND path = $2',\n\t\t\t\t\t[row.id, this.pathStr]\n\t\t\t\t);\n\n\t\t\t\tconst idempotentKeys = idempotentRows.rows.length > 0\n\t\t\t\t\t? new Set(idempotentRows.rows.map(function(idempotentRow: IdempotentRow) {\n\t\t\t\t\t\treturn(ConvertStringToRequestID(idempotentRow.idempotent_id));\n\t\t\t\t\t}))\n\t\t\t\t\t: undefined;\n\n\t\t\t\tentries.push({\n\t\t\t\t\tid: ConvertStringToRequestID(row.id),\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\trequest: JSON.parse(row.request) as QueueRequest,\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\toutput: row.output ? JSON.parse(row.output) as QueueResult : null,\n\t\t\t\t\tlastError: row.last_error,\n\t\t\t\t\tstatus: row.status,\n\t\t\t\t\tcreated: new Date(Number(row.created)),\n\t\t\t\t\tupdated: new Date(Number(row.updated)),\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n\t\t\t\t\tworker: row.worker as unknown as KeetaAnchorQueueWorkerID | null,\n\t\t\t\t\tfailures: row.failures,\n\t\t\t\t\tidempotentKeys: idempotentKeys\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlogger?.debug(`Queried queue with id ${this.id} with filter:`, filter, '-- found', entries.length, 'entries');\n\n\t\t\treturn(entries);\n\t\t}));\n\t}\n\n\tasync partition(path: string) : Promise<KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult>> {\n\t\tthis.methodLogger('partition')?.debug(`Creating partitioned queue storage driver for path: ${path}`);\n\n\t\tif (this.poolInternal === null) {\n\t\t\tthrow(new Error('Asked to partition but the instance has been destroyed'));\n\t\t}\n\n\t\tconst retval = new KeetaAnchorQueueStorageDriverPostgres<QueueRequest, QueueResult>({\n\t\t\tid: `${this.id}::${path}`,\n\t\t\tlogger: this.logger,\n\t\t\tpool: this.poolInternal,\n\t\t\tpath: [...this.path, path]\n\t\t});\n\n\t\treturn(retval);\n\t}\n\n\tasync destroy(): Promise<void> {\n\t\tthis.methodLogger('destroy')?.debug('Destroying instance');\n\n\t\tthis.poolInternal = null;\n\t}\n\n\tasync [Symbol.asyncDispose](): Promise<void> {\n\t\treturn(await this.destroy());\n\t}\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { KeetaAnchorQueueStorageDriver, KeetaAnchorQueueStorageDriverConstructor, KeetaAnchorQueueRequest, KeetaAnchorQueueRequestID, KeetaAnchorQueueEntry, KeetaAnchorQueueEntryExtra, KeetaAnchorQueueEntryAncillaryData, KeetaAnchorQueueStatus, KeetaAnchorQueueFilter } from '../index.ts';
2
+ import type { JSONSerializable } from '../../utils/json.js';
3
+ import type { RedisClientType } from 'redis';
4
+ export default class KeetaAnchorQueueStorageDriverRedis<QueueRequest extends JSONSerializable = JSONSerializable, QueueResult extends JSONSerializable = JSONSerializable> implements KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult> {
5
+ private readonly logger;
6
+ private redisInternal;
7
+ readonly name = "KeetaAnchorQueueStorageDriverRedis";
8
+ readonly id: string;
9
+ readonly path: string[];
10
+ private readonly pathStr;
11
+ constructor(options: NonNullable<ConstructorParameters<KeetaAnchorQueueStorageDriverConstructor<QueueRequest, QueueResult>>[0]> & {
12
+ redis: () => Promise<RedisClientType>;
13
+ });
14
+ private methodLogger;
15
+ private getRedis;
16
+ private queueKey;
17
+ private idempotentKey;
18
+ private indexKey;
19
+ add(request: KeetaAnchorQueueRequest<QueueRequest>, info?: KeetaAnchorQueueEntryExtra): Promise<KeetaAnchorQueueRequestID>;
20
+ setStatus(id: KeetaAnchorQueueRequestID, status: KeetaAnchorQueueStatus, ancillary?: KeetaAnchorQueueEntryAncillaryData<QueueResult>): Promise<void>;
21
+ get(id: KeetaAnchorQueueRequestID): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult> | null>;
22
+ query(filter?: KeetaAnchorQueueFilter): Promise<KeetaAnchorQueueEntry<QueueRequest, QueueResult>[]>;
23
+ partition(path: string): Promise<KeetaAnchorQueueStorageDriver<QueueRequest, QueueResult>>;
24
+ destroy(): Promise<void>;
25
+ [Symbol.asyncDispose](): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=queue_redis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue_redis.d.ts","sourceRoot":"","sources":["../../../../src/lib/queue/drivers/queue_redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,6BAA6B,EAC7B,wCAAwC,EACxC,uBAAuB,EACvB,yBAAyB,EACzB,qBAAqB,EACrB,0BAA0B,EAC1B,kCAAkC,EAClC,sBAAsB,EACtB,sBAAsB,EAEtB,MAAM,aAAa,CAAC;AASrB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAe7C,MAAM,CAAC,OAAO,OAAO,kCAAkC,CAAC,YAAY,SAAS,gBAAgB,GAAG,gBAAgB,EAAE,WAAW,SAAS,gBAAgB,GAAG,gBAAgB,CAAE,YAAW,6BAA6B,CAAC,YAAY,EAAE,WAAW,CAAC;IAC7O,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,aAAa,CAAiD;IAEtE,QAAQ,CAAC,IAAI,wCAAwC;IACrD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAM;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,WAAW,CAAC,qBAAqB,CAAC,wCAAwC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;QAAE,KAAK,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;KAAE;IAW5K,OAAO,CAAC,YAAY;YASN,QAAQ;IAOtB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,QAAQ;IAOV,GAAG,CAAC,OAAO,EAAE,uBAAuB,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC;IA+H1H,SAAS,CAAC,EAAE,EAAE,yBAAyB,EAAE,MAAM,EAAE,sBAAsB,EAAE,SAAS,CAAC,EAAE,kCAAkC,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoGpJ,GAAG,CAAC,EAAE,EAAE,yBAAyB,GAAG,OAAO,CAAC,qBAAqB,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC;IAkCpG,KAAK,CAAC,MAAM,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,CAAC;IAsEnG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,6BAA6B,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAiB1F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAG5C"}