@enbox/dwn-sql-store 0.0.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.
Files changed (75) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +178 -0
  3. package/dist/cjs/main.js +3784 -0
  4. package/dist/cjs/package.json +1 -0
  5. package/dist/esm/src/data-store-sql.js +103 -0
  6. package/dist/esm/src/data-store-sql.js.map +1 -0
  7. package/dist/esm/src/dialect/dialect.js +2 -0
  8. package/dist/esm/src/dialect/dialect.js.map +1 -0
  9. package/dist/esm/src/dialect/mysql-dialect.js +40 -0
  10. package/dist/esm/src/dialect/mysql-dialect.js.map +1 -0
  11. package/dist/esm/src/dialect/postgres-dialect.js +26 -0
  12. package/dist/esm/src/dialect/postgres-dialect.js.map +1 -0
  13. package/dist/esm/src/dialect/sqlite-dialect.js +33 -0
  14. package/dist/esm/src/dialect/sqlite-dialect.js.map +1 -0
  15. package/dist/esm/src/event-log-sql.js +169 -0
  16. package/dist/esm/src/event-log-sql.js.map +1 -0
  17. package/dist/esm/src/main.js +9 -0
  18. package/dist/esm/src/main.js.map +1 -0
  19. package/dist/esm/src/message-store-sql.js +317 -0
  20. package/dist/esm/src/message-store-sql.js.map +1 -0
  21. package/dist/esm/src/resumable-task-store-sql.js +141 -0
  22. package/dist/esm/src/resumable-task-store-sql.js.map +1 -0
  23. package/dist/esm/src/types.js +2 -0
  24. package/dist/esm/src/types.js.map +1 -0
  25. package/dist/esm/src/utils/filter.js +125 -0
  26. package/dist/esm/src/utils/filter.js.map +1 -0
  27. package/dist/esm/src/utils/sanitize.js +92 -0
  28. package/dist/esm/src/utils/sanitize.js.map +1 -0
  29. package/dist/esm/src/utils/tags.js +38 -0
  30. package/dist/esm/src/utils/tags.js.map +1 -0
  31. package/dist/esm/src/utils/transaction.js +25 -0
  32. package/dist/esm/src/utils/transaction.js.map +1 -0
  33. package/dist/types/src/data-store-sql.d.ts +14 -0
  34. package/dist/types/src/data-store-sql.d.ts.map +1 -0
  35. package/dist/types/src/dialect/dialect.d.ts +40 -0
  36. package/dist/types/src/dialect/dialect.d.ts.map +1 -0
  37. package/dist/types/src/dialect/mysql-dialect.d.ts +18 -0
  38. package/dist/types/src/dialect/mysql-dialect.d.ts.map +1 -0
  39. package/dist/types/src/dialect/postgres-dialect.d.ts +12 -0
  40. package/dist/types/src/dialect/postgres-dialect.d.ts.map +1 -0
  41. package/dist/types/src/dialect/sqlite-dialect.d.ts +12 -0
  42. package/dist/types/src/dialect/sqlite-dialect.d.ts.map +1 -0
  43. package/dist/types/src/event-log-sql.d.ts +24 -0
  44. package/dist/types/src/event-log-sql.d.ts.map +1 -0
  45. package/dist/types/src/main.d.ts +9 -0
  46. package/dist/types/src/main.d.ts.map +1 -0
  47. package/dist/types/src/message-store-sql.d.ts +46 -0
  48. package/dist/types/src/message-store-sql.d.ts.map +1 -0
  49. package/dist/types/src/resumable-task-store-sql.d.ts +16 -0
  50. package/dist/types/src/resumable-task-store-sql.d.ts.map +1 -0
  51. package/dist/types/src/types.d.ts +100 -0
  52. package/dist/types/src/types.d.ts.map +1 -0
  53. package/dist/types/src/utils/filter.d.ts +13 -0
  54. package/dist/types/src/utils/filter.d.ts.map +1 -0
  55. package/dist/types/src/utils/sanitize.d.ts +28 -0
  56. package/dist/types/src/utils/sanitize.d.ts.map +1 -0
  57. package/dist/types/src/utils/tags.d.ts +20 -0
  58. package/dist/types/src/utils/tags.d.ts.map +1 -0
  59. package/dist/types/src/utils/transaction.d.ts +7 -0
  60. package/dist/types/src/utils/transaction.d.ts.map +1 -0
  61. package/package.json +91 -0
  62. package/src/data-store-sql.ts +148 -0
  63. package/src/dialect/dialect.ts +77 -0
  64. package/src/dialect/mysql-dialect.ts +86 -0
  65. package/src/dialect/postgres-dialect.ts +66 -0
  66. package/src/dialect/sqlite-dialect.ts +72 -0
  67. package/src/event-log-sql.ts +227 -0
  68. package/src/main.ts +8 -0
  69. package/src/message-store-sql.ts +440 -0
  70. package/src/resumable-task-store-sql.ts +174 -0
  71. package/src/types.ts +109 -0
  72. package/src/utils/filter.ts +136 -0
  73. package/src/utils/sanitize.ts +117 -0
  74. package/src/utils/tags.ts +46 -0
  75. package/src/utils/transaction.ts +28 -0
@@ -0,0 +1,317 @@
1
+ import { DwnInterfaceName, DwnMethodName, executeUnlessAborted, SortDirection, } from '@enbox/dwn-sdk-js';
2
+ import { Kysely } from 'kysely';
3
+ import * as block from 'multiformats/block';
4
+ import * as cbor from '@ipld/dag-cbor';
5
+ import { executeWithRetryIfDatabaseIsLocked } from './utils/transaction.js';
6
+ import { extractTagsAndSanitizeIndexes } from './utils/sanitize.js';
7
+ import { filterSelectQuery } from './utils/filter.js';
8
+ import { sha256 } from 'multiformats/hashes/sha2';
9
+ import { TagTables } from './utils/tags.js';
10
+ export class MessageStoreSql {
11
+ #dialect;
12
+ #tags;
13
+ #db = null;
14
+ constructor(dialect) {
15
+ this.#dialect = dialect;
16
+ this.#tags = new TagTables(dialect, 'messageStoreMessages');
17
+ }
18
+ async open() {
19
+ if (this.#db) {
20
+ return;
21
+ }
22
+ this.#db = new Kysely({ dialect: this.#dialect });
23
+ // create messages table if it does not exist
24
+ const messagesTableName = 'messageStoreMessages';
25
+ const messagesTableExists = await this.#dialect.hasTable(this.#db, messagesTableName);
26
+ if (!messagesTableExists) {
27
+ let createMessagesTable = this.#db.schema
28
+ .createTable(messagesTableName)
29
+ .ifNotExists()
30
+ .addColumn('tenant', 'varchar(255)', (col) => col.notNull())
31
+ .addColumn('messageCid', 'varchar(60)', (col) => col.notNull())
32
+ .addColumn('interface', 'varchar(20)')
33
+ .addColumn('method', 'varchar(20)')
34
+ .addColumn('recordId', 'varchar(60)')
35
+ .addColumn('entryId', 'varchar(60)')
36
+ .addColumn('parentId', 'varchar(60)')
37
+ .addColumn('protocol', 'varchar(200)')
38
+ .addColumn('protocolPath', 'varchar(200)')
39
+ .addColumn('contextId', 'varchar(500)')
40
+ .addColumn('schema', 'varchar(200)')
41
+ .addColumn('author', 'varchar(255)')
42
+ .addColumn('recipient', 'varchar(255)')
43
+ .addColumn('messageTimestamp', 'varchar(30)')
44
+ .addColumn('dateCreated', 'varchar(30)')
45
+ .addColumn('datePublished', 'varchar(30)')
46
+ .addColumn('isLatestBaseState', 'boolean')
47
+ .addColumn('published', 'boolean')
48
+ .addColumn('prune', 'boolean')
49
+ .addColumn('dataFormat', 'varchar(30)')
50
+ .addColumn('dataCid', 'varchar(60)')
51
+ .addColumn('dataSize', 'integer')
52
+ .addColumn('encodedData', 'text') // we optionally store encoded data if it is below a threshold
53
+ .addColumn('attester', 'text')
54
+ .addColumn('permissionGrantId', 'varchar(60)')
55
+ .addColumn('latest', 'text'); // TODO: obsolete, remove once `dwn-sdk-js` tests are updated
56
+ // Add columns that have dialect-specific constraints
57
+ createMessagesTable = this.#dialect.addAutoIncrementingColumn(createMessagesTable, 'id', (col) => col.primaryKey());
58
+ createMessagesTable = this.#dialect.addBlobColumn(createMessagesTable, 'encodedMessageBytes', (col) => col.notNull());
59
+ await createMessagesTable.execute();
60
+ // add indexes to the table
61
+ await this.createIndexes(this.#db, messagesTableName, [
62
+ ['tenant'],
63
+ ['tenant', 'recordId'],
64
+ ['tenant', 'parentId'],
65
+ ['tenant', 'protocol', 'published', 'messageTimestamp'],
66
+ ['tenant', 'interface'],
67
+ ['tenant', 'contextId', 'messageTimestamp'],
68
+ ['tenant', 'permissionGrantId'], // for deleting grant-authorized messages though pending https://github.com/TBD54566975/dwn-sdk-js/issues/716
69
+ // other potential indexes
70
+ // ['tenant', 'author'],
71
+ // ['tenant', 'recipient'],
72
+ // ['tenant', 'schema', 'dataFormat'],
73
+ // ['tenant', 'dateCreated'],
74
+ // ['tenant', 'datePublished'],
75
+ // ['tenant', 'messageCid'],
76
+ // ['tenant', 'protocolPath'],
77
+ ]);
78
+ }
79
+ // create tags table
80
+ const tagsTableName = 'messageStoreRecordsTags';
81
+ const tagsTableExists = await this.#dialect.hasTable(this.#db, tagsTableName);
82
+ if (!tagsTableExists) {
83
+ let createRecordsTagsTable = this.#db.schema
84
+ .createTable(tagsTableName)
85
+ .ifNotExists()
86
+ .addColumn('tag', 'varchar(30)', (col) => col.notNull())
87
+ .addColumn('valueString', 'varchar(200)')
88
+ .addColumn('valueNumber', 'decimal');
89
+ // Add columns that have dialect-specific constraints
90
+ const foreignMessageInsertId = 'messageInsertId';
91
+ createRecordsTagsTable = this.#dialect.addAutoIncrementingColumn(createRecordsTagsTable, 'id', (col) => col.primaryKey());
92
+ createRecordsTagsTable = this.#dialect.addReferencedColumn(createRecordsTagsTable, tagsTableName, foreignMessageInsertId, 'integer', 'messageStoreMessages', 'id', 'cascade');
93
+ await createRecordsTagsTable.execute();
94
+ // add indexes to the table
95
+ await this.createIndexes(this.#db, tagsTableName, [
96
+ [foreignMessageInsertId],
97
+ ['tag', 'valueString'],
98
+ ['tag', 'valueNumber']
99
+ ]);
100
+ }
101
+ }
102
+ /**
103
+ * Creates indexes on the given table.
104
+ * @param tableName The name of the table to create the indexes on.
105
+ * @param indexes Each inner array represents a single index and contains the column names to be indexed as a composite index.
106
+ * If the inner array contains only one element, it will be treated as a single column index.
107
+ */
108
+ async createIndexes(database, tableName, indexes) {
109
+ for (const columnNames of indexes) {
110
+ const indexName = 'index_' + columnNames.join('_'); // e.g. index_tenant_protocol
111
+ await database.schema
112
+ .createIndex(indexName)
113
+ // .ifNotExists() // intentionally kept commented out code to show that it is not supported by all dialects (ie. MySQL)
114
+ .on(tableName)
115
+ .columns(columnNames)
116
+ .execute();
117
+ }
118
+ }
119
+ async close() {
120
+ await this.#db?.destroy();
121
+ this.#db = null;
122
+ }
123
+ async put(tenant, message, indexes, options) {
124
+ if (!this.#db) {
125
+ throw new Error('Connection to database not open. Call `open` before using `put`.');
126
+ }
127
+ options?.signal?.throwIfAborted();
128
+ // gets the encoded data and removes it from the message
129
+ // we remove it from the message as it would cause the `encodedMessageBytes` to be greater than the
130
+ // maximum bytes allowed by SQL
131
+ const getEncodedData = (message) => {
132
+ let encodedData = null;
133
+ if (message.descriptor.interface === DwnInterfaceName.Records && message.descriptor.method === DwnMethodName.Write) {
134
+ const data = message.encodedData;
135
+ if (data) {
136
+ delete message.encodedData;
137
+ encodedData = data;
138
+ }
139
+ }
140
+ return { message, encodedData };
141
+ };
142
+ const { message: messageToProcess, encodedData } = getEncodedData(message);
143
+ const encodedMessageBlock = await executeUnlessAborted(block.encode({ value: messageToProcess, codec: cbor, hasher: sha256 }), options?.signal);
144
+ const messageCid = encodedMessageBlock.cid.toString();
145
+ const encodedMessageBytes = Buffer.from(encodedMessageBlock.bytes);
146
+ // we execute the insert in a transaction as we are making multiple inserts into multiple tables.
147
+ // if any of these inserts would throw, the whole transaction would be rolled back.
148
+ // otherwise it is committed.
149
+ const putMessageOperation = this.constructPutMessageOperation({ tenant, messageCid, encodedMessageBytes, encodedData, indexes });
150
+ await executeWithRetryIfDatabaseIsLocked(this.#db, putMessageOperation);
151
+ }
152
+ /**
153
+ * Constructs the transactional operation to insert the given message into the database.
154
+ */
155
+ constructPutMessageOperation(queryOptions) {
156
+ const { tenant, messageCid, encodedMessageBytes, encodedData, indexes } = queryOptions;
157
+ // we extract the tag indexes into their own object to be inserted separately.
158
+ // we also sanitize the indexes to convert any `boolean` values to `text` representations.
159
+ const { indexes: putIndexes, tags } = extractTagsAndSanitizeIndexes(indexes);
160
+ return async (tx) => {
161
+ const messageIndexValues = {
162
+ tenant,
163
+ messageCid,
164
+ encodedMessageBytes,
165
+ encodedData,
166
+ ...putIndexes
167
+ };
168
+ // we use the dialect-specific `insertThenReturnId` in order to be able to extract the `insertId`
169
+ const result = await this.#dialect
170
+ .insertThenReturnId(tx, 'messageStoreMessages', messageIndexValues, 'id as insertId')
171
+ .executeTakeFirstOrThrow();
172
+ // if tags exist, we execute those within the transaction associating them with the `insertId`.
173
+ if (Object.keys(tags).length > 0) {
174
+ await this.#tags.executeTagsInsert(result.insertId, tags, tx);
175
+ }
176
+ };
177
+ }
178
+ async get(tenant, cid, options) {
179
+ if (!this.#db) {
180
+ throw new Error('Connection to database not open. Call `open` before using `get`.');
181
+ }
182
+ options?.signal?.throwIfAborted();
183
+ const result = await executeUnlessAborted(this.#db
184
+ .selectFrom('messageStoreMessages')
185
+ .selectAll()
186
+ .where('tenant', '=', tenant)
187
+ .where('messageCid', '=', cid)
188
+ .executeTakeFirst(), options?.signal);
189
+ if (!result) {
190
+ return undefined;
191
+ }
192
+ return this.parseEncodedMessage(result.encodedMessageBytes, result.encodedData, options);
193
+ }
194
+ async query(tenant, filters, messageSort, pagination, options) {
195
+ if (!this.#db) {
196
+ throw new Error('Connection to database not open. Call `open` before using `query`.');
197
+ }
198
+ options?.signal?.throwIfAborted();
199
+ // extract sort property and direction from the supplied messageSort
200
+ const { property: sortProperty, direction: sortDirection } = this.extractSortProperties(messageSort);
201
+ let query = this.#db
202
+ .selectFrom('messageStoreMessages')
203
+ .leftJoin('messageStoreRecordsTags', 'messageStoreRecordsTags.messageInsertId', 'messageStoreMessages.id')
204
+ .select('messageCid')
205
+ .distinct()
206
+ .select([
207
+ 'encodedMessageBytes',
208
+ 'encodedData',
209
+ sortProperty,
210
+ ])
211
+ .where('tenant', '=', tenant);
212
+ // filter sanitization takes place within `filterSelectQuery`
213
+ query = filterSelectQuery(filters, query);
214
+ if (pagination?.cursor !== undefined) {
215
+ // currently the sort property is explicitly either `dateCreated` | `messageTimestamp` | `datePublished` which are all strings
216
+ // TODO: https://github.com/TBD54566975/dwn-sdk-js/issues/664 to handle the edge case
217
+ const cursorValue = pagination.cursor.value;
218
+ const cursorMessageId = pagination.cursor.messageCid;
219
+ query = query.where(({ eb, refTuple, tuple }) => {
220
+ const direction = sortDirection === SortDirection.Ascending ? '>' : '<';
221
+ // https://kysely-org.github.io/kysely-apidoc/interfaces/ExpressionBuilder.html#refTuple
222
+ return eb(refTuple(sortProperty, 'messageCid'), direction, tuple(cursorValue, cursorMessageId));
223
+ });
224
+ }
225
+ const orderDirection = sortDirection === SortDirection.Ascending ? 'asc' : 'desc';
226
+ // sorting by the provided sort property, the tiebreak is always in ascending order regardless of sort
227
+ query = query
228
+ .orderBy(sortProperty, orderDirection)
229
+ .orderBy('messageCid', orderDirection);
230
+ if (pagination?.limit !== undefined && pagination?.limit > 0) {
231
+ // we query for one additional record to decide if we return a pagination cursor or not.
232
+ query = query.limit(pagination.limit + 1);
233
+ }
234
+ const results = await executeUnlessAborted(query.execute(), options?.signal);
235
+ // prunes the additional requested message, if it exists, and adds a cursor to the results.
236
+ // also parses the encoded message for each of the returned results.
237
+ return this.processPaginationResults(results, sortProperty, pagination?.limit, options);
238
+ }
239
+ async delete(tenant, cid, options) {
240
+ if (!this.#db) {
241
+ throw new Error('Connection to database not open. Call `open` before using `delete`.');
242
+ }
243
+ options?.signal?.throwIfAborted();
244
+ await executeUnlessAborted(this.#db
245
+ .deleteFrom('messageStoreMessages')
246
+ .where('tenant', '=', tenant)
247
+ .where('messageCid', '=', cid)
248
+ .execute(), options?.signal);
249
+ }
250
+ async clear() {
251
+ if (!this.#db) {
252
+ throw new Error('Connection to database not open. Call `open` before using `clear`.');
253
+ }
254
+ await this.#db
255
+ .deleteFrom('messageStoreMessages')
256
+ .execute();
257
+ }
258
+ async parseEncodedMessage(encodedMessageBytes, encodedData, options) {
259
+ options?.signal?.throwIfAborted();
260
+ const decodedBlock = await block.decode({
261
+ bytes: encodedMessageBytes,
262
+ codec: cbor,
263
+ hasher: sha256
264
+ });
265
+ const message = decodedBlock.value;
266
+ // If encodedData is stored within the MessageStore we include it in the response.
267
+ // We store encodedData when the data is below a certain threshold.
268
+ // https://github.com/TBD54566975/dwn-sdk-js/pull/456
269
+ if (message !== undefined && encodedData !== undefined && encodedData !== null) {
270
+ message.encodedData = encodedData;
271
+ }
272
+ return message;
273
+ }
274
+ /**
275
+ * Processes the paginated query results.
276
+ * Builds a pagination cursor if there are additional messages to paginate.
277
+ * Accepts more messages than the limit, as we query for additional records to check if we should paginate.
278
+ *
279
+ * @param messages a list of messages, potentially larger than the provided limit.
280
+ * @param limit the maximum number of messages to be returned
281
+ *
282
+ * @returns the pruned message results and an optional pagination cursor
283
+ */
284
+ async processPaginationResults(results, sortProperty, limit, options) {
285
+ // we queried for one additional message to determine if there are any additional messages beyond the limit
286
+ // we now check if the returned results are greater than the limit, if so we pluck the last item out of the result set
287
+ // the cursor is always the last item in the *returned* result so we use the last item in the remaining result set to build a cursor
288
+ let cursor;
289
+ if (limit !== undefined && results.length > limit) {
290
+ results = results.slice(0, limit);
291
+ const lastMessage = results.at(-1);
292
+ const cursorValue = lastMessage[sortProperty];
293
+ cursor = { messageCid: lastMessage.messageCid, value: cursorValue };
294
+ }
295
+ // extracts the full encoded message from the stored blob for each result item.
296
+ const messages = results.map(r => this.parseEncodedMessage(r.encodedMessageBytes, r.encodedData, options));
297
+ return { messages: await Promise.all(messages), cursor };
298
+ }
299
+ /**
300
+ * Extracts the appropriate sort property and direction given a MessageSort object.
301
+ */
302
+ extractSortProperties(messageSort) {
303
+ if (messageSort?.dateCreated !== undefined) {
304
+ return { property: 'dateCreated', direction: messageSort.dateCreated };
305
+ }
306
+ else if (messageSort?.datePublished !== undefined) {
307
+ return { property: 'datePublished', direction: messageSort.datePublished };
308
+ }
309
+ else if (messageSort?.messageTimestamp !== undefined) {
310
+ return { property: 'messageTimestamp', direction: messageSort.messageTimestamp };
311
+ }
312
+ else {
313
+ return { property: 'messageTimestamp', direction: SortDirection.Ascending };
314
+ }
315
+ }
316
+ }
317
+ //# sourceMappingURL=message-store-sql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-store-sql.js","sourceRoot":"","sources":["../../../src/message-store-sql.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,141 @@
1
+ import { executeWithRetryIfDatabaseIsLocked } from './utils/transaction.js';
2
+ import { Kysely } from 'kysely';
3
+ import { Cid } from '@enbox/dwn-sdk-js';
4
+ class ResumableTaskStoreSql {
5
+ static taskTimeoutInSeconds = 60;
6
+ #dialect;
7
+ #db = null;
8
+ constructor(dialect) {
9
+ this.#dialect = dialect;
10
+ }
11
+ async open() {
12
+ if (this.#db) {
13
+ return;
14
+ }
15
+ this.#db = new Kysely({ dialect: this.#dialect });
16
+ // if table already exists, there is no more things todo
17
+ const tableName = 'resumableTasks';
18
+ const tableExists = await this.#dialect.hasTable(this.#db, tableName);
19
+ if (tableExists) {
20
+ return;
21
+ }
22
+ // else create the table and corresponding indexes
23
+ let table = this.#db.schema
24
+ .createTable(tableName)
25
+ .ifNotExists() // kept to show supported by all dialects in contrast to ifNotExists() below, though not needed due to hasTable() check above
26
+ .addColumn('id', 'varchar(255)', (col) => col.primaryKey())
27
+ .addColumn('task', 'text')
28
+ .addColumn('timeout', 'bigint')
29
+ .addColumn('retryCount', 'integer');
30
+ await table.execute();
31
+ await this.#db.schema
32
+ .createIndex('index_timeout')
33
+ // .ifNotExists() // intentionally kept commented out code to show that it is not supported by all dialects (ie. MySQL)
34
+ .on('resumableTasks')
35
+ .column('timeout')
36
+ .execute();
37
+ }
38
+ async close() {
39
+ await this.#db?.destroy();
40
+ this.#db = null;
41
+ }
42
+ async register(task, timeoutInSeconds) {
43
+ if (!this.#db) {
44
+ throw new Error('Connection to database not open. Call `open` before using `register`.');
45
+ }
46
+ const id = await Cid.computeCid(task);
47
+ const timeout = Date.now() + timeoutInSeconds * 1000;
48
+ const taskString = JSON.stringify(task);
49
+ const retryCount = 0;
50
+ const taskEntryInDatabase = { id, task: taskString, timeout, retryCount };
51
+ await this.#db.insertInto('resumableTasks').values(taskEntryInDatabase).execute();
52
+ return {
53
+ id,
54
+ task,
55
+ retryCount,
56
+ timeout,
57
+ };
58
+ }
59
+ async grab(count) {
60
+ if (!this.#db) {
61
+ throw new Error('Connection to database not open. Call `open` before using `grab`.');
62
+ }
63
+ const now = Date.now();
64
+ const newTimeout = now + (ResumableTaskStoreSql.taskTimeoutInSeconds * 1000);
65
+ let tasks = [];
66
+ const operation = async (transaction) => {
67
+ tasks = await transaction
68
+ .selectFrom('resumableTasks')
69
+ .selectAll()
70
+ .where('timeout', '<=', now)
71
+ .limit(count)
72
+ .execute();
73
+ if (tasks.length > 0) {
74
+ const ids = tasks.map((task) => task.id);
75
+ await transaction
76
+ .updateTable('resumableTasks')
77
+ .set({ timeout: newTimeout })
78
+ .where((eb) => eb('id', 'in', ids))
79
+ .execute();
80
+ }
81
+ };
82
+ await executeWithRetryIfDatabaseIsLocked(this.#db, operation);
83
+ const tasksToReturn = tasks.map((task) => {
84
+ return {
85
+ id: task.id,
86
+ task: JSON.parse(task.task),
87
+ retryCount: task.retryCount,
88
+ timeout: task.timeout,
89
+ };
90
+ });
91
+ return tasksToReturn;
92
+ }
93
+ async read(taskId) {
94
+ if (!this.#db) {
95
+ throw new Error('Connection to database not open. Call `open` before using `read`.');
96
+ }
97
+ const task = await this.#db
98
+ .selectFrom('resumableTasks')
99
+ .selectAll()
100
+ .where('id', '=', taskId)
101
+ .executeTakeFirst();
102
+ if (task !== undefined) {
103
+ // NOTE: special handling ONLY for PostgreSQL:
104
+ // Even though PostgreSQL stores `bigint` as a 64 bit number, the `pg` library we depend on returns it as a string, hence the conversion.
105
+ if (typeof task.timeout !== 'number') {
106
+ task.timeout = parseInt(task.timeout, 10);
107
+ }
108
+ }
109
+ return task;
110
+ }
111
+ async extend(taskId, timeoutInSeconds) {
112
+ if (!this.#db) {
113
+ throw new Error('Connection to database not open. Call `open` before using `extend`.');
114
+ }
115
+ const timeout = Date.now() + (timeoutInSeconds * 1000);
116
+ await this.#db
117
+ .updateTable('resumableTasks')
118
+ .set({ timeout })
119
+ .where('id', '=', taskId)
120
+ .execute();
121
+ }
122
+ async delete(taskId) {
123
+ if (!this.#db) {
124
+ throw new Error('Connection to database not open. Call `open` before using `delete`.');
125
+ }
126
+ await this.#db
127
+ .deleteFrom('resumableTasks')
128
+ .where('id', '=', taskId)
129
+ .execute();
130
+ }
131
+ async clear() {
132
+ if (!this.#db) {
133
+ throw new Error('Connection to database not open. Call `open` before using `clear`.');
134
+ }
135
+ await this.#db
136
+ .deleteFrom('resumableTasks')
137
+ .execute();
138
+ }
139
+ }
140
+ export { ResumableTaskStoreSql };
141
+ //# sourceMappingURL=resumable-task-store-sql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resumable-task-store-sql.js","sourceRoot":"","sources":["../../../src/resumable-task-store-sql.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,125 @@
1
+ import { DynamicModule } from 'kysely';
2
+ import { sanitizeFiltersAndSeparateTags, sanitizedValue } from './sanitize.js';
3
+ /**
4
+ * Takes multiple Filters and returns a single query.
5
+ * Each filter is evaluated as an OR operation.
6
+ *
7
+ * @param filters Array of filters to be evaluated as OR operations
8
+ * @param query the incoming QueryBuilder.
9
+ * @returns The modified QueryBuilder respecting the provided filters.
10
+ */
11
+ export function filterSelectQuery(filters, query) {
12
+ const sanitizedFilters = sanitizeFiltersAndSeparateTags(filters);
13
+ return query.where((eb) =>
14
+ // evaluate the filters as an OR expression.
15
+ eb.or(sanitizedFilters.map(({ filter, tags }) => {
16
+ // evaluate each filter + tags tuple as an AND expression.
17
+ const andOperands = [];
18
+ processFilter(eb, andOperands, filter);
19
+ processTags(eb, andOperands, tags);
20
+ return eb.and(andOperands);
21
+ })));
22
+ }
23
+ /**
24
+ * Processes each property in the non-tags filter as an AND operand and adds it to the `andOperands` array.
25
+ * If a property has an array of values it will treat it as a OneOf (IN) within the overall AND query.
26
+ *
27
+ * @param eb The ExpressionBuilder from the query.
28
+ * @param andOperands The array of AND operands to append to.
29
+ * @param filter The filter to be evaluated.
30
+ */
31
+ function processFilter(eb, andOperands, filter) {
32
+ for (let property in filter) {
33
+ const value = filter[property];
34
+ const column = new DynamicModule().ref(property);
35
+ if (Array.isArray(value)) { // OneOfFilter
36
+ andOperands.push(eb(column, 'in', value));
37
+ }
38
+ else if (typeof value === 'object') { // RangeFilter
39
+ if (value.gt) {
40
+ andOperands.push(eb(column, '>', sanitizedValue(value.gt)));
41
+ }
42
+ if (value.gte) {
43
+ andOperands.push(eb(column, '>=', sanitizedValue(value.gte)));
44
+ }
45
+ if (value.lt) {
46
+ andOperands.push(eb(column, '<', sanitizedValue(value.lt)));
47
+ }
48
+ if (value.lte) {
49
+ andOperands.push(eb(column, '<=', sanitizedValue(value.lte)));
50
+ }
51
+ }
52
+ else { // EqualFilter
53
+ andOperands.push(eb(column, '=', sanitizedValue(value)));
54
+ }
55
+ }
56
+ }
57
+ /**
58
+ * Processes each property in the tags filter as an AND operand and adds it to the `andOperands` array.
59
+ * If a property has an array of values it will treat it as a OneOf (IN) within the overall AND query.
60
+ *
61
+ * @param eb The ExpressionBuilder from the query.
62
+ * @param andOperands The array of AND operands to append to.
63
+ * @param tag The tags filter to be evaluated.
64
+ */
65
+ function processTags(eb, andOperands, tags) {
66
+ const tagColumn = new DynamicModule().ref('tag');
67
+ const valueNumber = new DynamicModule().ref('valueNumber');
68
+ const valueString = new DynamicModule().ref('valueString');
69
+ // process each tag and add it to the andOperands from the rest of the filters
70
+ for (let property in tags) {
71
+ andOperands.push(eb(tagColumn, '=', property));
72
+ const value = tags[property];
73
+ if (Array.isArray(value)) { // OneOfFilter
74
+ if (value.some(val => typeof val === 'number')) {
75
+ andOperands.push(eb(valueNumber, 'in', value));
76
+ }
77
+ else {
78
+ andOperands.push(eb(valueString, 'in', value.map(v => String(v))));
79
+ }
80
+ }
81
+ else if (typeof value === 'object') { // RangeFilter
82
+ if (value.gt) {
83
+ if (typeof value.gt === 'number') {
84
+ andOperands.push(eb(valueNumber, '>', value.gt));
85
+ }
86
+ else {
87
+ andOperands.push(eb(valueString, '>', String(value.gt)));
88
+ }
89
+ }
90
+ if (value.gte) {
91
+ if (typeof value.gte === 'number') {
92
+ andOperands.push(eb(valueNumber, '>=', value.gte));
93
+ }
94
+ else {
95
+ andOperands.push(eb(valueString, '>=', String(value.gte)));
96
+ }
97
+ }
98
+ if (value.lt) {
99
+ if (typeof value.lt === 'number') {
100
+ andOperands.push(eb(valueNumber, '<', value.lt));
101
+ }
102
+ else {
103
+ andOperands.push(eb(valueString, '<', String(value.lt)));
104
+ }
105
+ }
106
+ if (value.lte) {
107
+ if (typeof value.lte === 'number') {
108
+ andOperands.push(eb(valueNumber, '<=', value.lte));
109
+ }
110
+ else {
111
+ andOperands.push(eb(valueString, '<=', String(value.lte)));
112
+ }
113
+ }
114
+ }
115
+ else { // EqualFilter
116
+ if (typeof value === 'number') {
117
+ andOperands.push(eb(valueNumber, '=', value));
118
+ }
119
+ else {
120
+ andOperands.push(eb(valueString, '=', String(value)));
121
+ }
122
+ }
123
+ }
124
+ }
125
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../../src/utils/filter.ts"],"names":[],"mappings":"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"}