@event-driven-io/pongo 0.17.0-beta.41 → 0.17.0-beta.43

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/dist/cli.cjs +571 -16
  2. package/dist/cli.cjs.map +1 -1
  3. package/dist/cli.js +560 -3
  4. package/dist/cli.js.map +1 -1
  5. package/dist/cloudflare-CzlFuMmZ.js +38 -0
  6. package/dist/cloudflare-CzlFuMmZ.js.map +1 -0
  7. package/dist/cloudflare-WGIFZXop.cjs +42 -0
  8. package/dist/cloudflare-WGIFZXop.cjs.map +1 -0
  9. package/dist/cloudflare.cjs +1463 -12
  10. package/dist/cloudflare.cjs.map +1 -1
  11. package/dist/cloudflare.d.cts +435 -2
  12. package/dist/cloudflare.d.ts +435 -2
  13. package/dist/cloudflare.js +1455 -3
  14. package/dist/cloudflare.js.map +1 -1
  15. package/dist/{core-CH0SOCr3.js → core-BW9XZXW8.js} +33 -46
  16. package/dist/core-BW9XZXW8.js.map +1 -0
  17. package/dist/{core-CkmE5dkK.cjs → core-BicrzIKX.cjs} +38 -52
  18. package/dist/core-BicrzIKX.cjs.map +1 -0
  19. package/dist/index.cjs +1477 -52
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +717 -1
  22. package/dist/index.d.ts +717 -1
  23. package/dist/index.js +1429 -4
  24. package/dist/index.js.map +1 -1
  25. package/dist/pg-BdtFllz7.cjs +399 -0
  26. package/dist/pg-BdtFllz7.cjs.map +1 -0
  27. package/dist/pg-BtUtOO8Y.js +395 -0
  28. package/dist/pg-BtUtOO8Y.js.map +1 -0
  29. package/dist/pg.cjs +1155 -38
  30. package/dist/pg.cjs.map +1 -1
  31. package/dist/pg.d.cts +430 -2
  32. package/dist/pg.d.ts +430 -2
  33. package/dist/pg.js +1145 -27
  34. package/dist/pg.js.map +1 -1
  35. package/dist/shim.cjs +554 -6
  36. package/dist/shim.cjs.map +1 -1
  37. package/dist/shim.d.cts +445 -1
  38. package/dist/shim.d.ts +445 -1
  39. package/dist/shim.js +551 -2
  40. package/dist/shim.js.map +1 -1
  41. package/dist/sqlite3-CFwH2ot3.cjs +44 -0
  42. package/dist/sqlite3-CFwH2ot3.cjs.map +1 -0
  43. package/dist/sqlite3-Dv0y-lg6.js +40 -0
  44. package/dist/sqlite3-Dv0y-lg6.js.map +1 -0
  45. package/dist/sqlite3.cjs +1463 -12
  46. package/dist/sqlite3.cjs.map +1 -1
  47. package/dist/sqlite3.d.cts +435 -2
  48. package/dist/sqlite3.d.ts +435 -2
  49. package/dist/sqlite3.js +1455 -3
  50. package/dist/sqlite3.js.map +1 -1
  51. package/package.json +2 -2
  52. package/dist/core-BHdOCUrr.js +0 -1429
  53. package/dist/core-BHdOCUrr.js.map +0 -1
  54. package/dist/core-C9SB3XMx.cjs +0 -1717
  55. package/dist/core-C9SB3XMx.cjs.map +0 -1
  56. package/dist/core-CH0SOCr3.js.map +0 -1
  57. package/dist/core-CkmE5dkK.cjs.map +0 -1
  58. package/dist/index-C3pnS1S_.d.cts +0 -720
  59. package/dist/index-CZOmOsQt.d.ts +0 -10
  60. package/dist/index-FXnldVnn.d.cts +0 -10
  61. package/dist/index-r7V4paf_.d.ts +0 -720
package/dist/pg.js CHANGED
@@ -1,29 +1,1146 @@
1
- import { C as expectedVersionPredicate, F as hasOperators, L as PongoCollectionSchemaComponent, N as OperatorMap, P as QueryOperators, d as PongoDatabase, f as PongoDatabaseSchemaComponent, j as objectEntries, p as pongoSchema, u as pongoDriverRegistry } from "./core-BHdOCUrr.js";
2
- import { JSONSerializer, SQL, dumbo, isSQL, sqlMigration } from "@event-driven-io/dumbo";
1
+ import { JSONParam, JSONSerializer, SQL, dumbo, isSQL, mapColumnToBigint, mapColumnToJSON, runSQLMigrations, schemaComponent, single, sqlMigration } from "@event-driven-io/dumbo";
2
+ import { PostgreSQLJSON } from "@event-driven-io/dumbo/postgresql";
3
+ import { LRUCache } from "lru-cache";
4
+ import { v7 } from "uuid";
3
5
  import { PgDriverType, pgDumboDriver, postgreSQLMetadata } from "@event-driven-io/dumbo/pg";
4
6
 
7
+ //#region src/core/cache/pongoCacheWrapper.ts
8
+ const pongoCacheWrapper = (options) => {
9
+ const { provider, hooks } = options;
10
+ const onError = (error, operation) => {
11
+ hooks?.onError?.(error, operation);
12
+ };
13
+ let isClosed = false;
14
+ return {
15
+ cacheType: provider.cacheType,
16
+ async get(key) {
17
+ try {
18
+ const result = await provider.get(key);
19
+ if (result !== void 0) hooks?.onHit?.(key);
20
+ else hooks?.onMiss?.(key);
21
+ return result;
22
+ } catch (error) {
23
+ onError(error, "get");
24
+ hooks?.onMiss?.(key);
25
+ return;
26
+ }
27
+ },
28
+ async getMany(keys) {
29
+ try {
30
+ return await provider.getMany(keys);
31
+ } catch (error) {
32
+ onError(error, "getMany");
33
+ return [];
34
+ }
35
+ },
36
+ async set(key, value) {
37
+ try {
38
+ await provider.set(key, value);
39
+ } catch (error) {
40
+ onError(error, "set");
41
+ }
42
+ },
43
+ async setMany(entries) {
44
+ try {
45
+ await provider.setMany(entries);
46
+ } catch (error) {
47
+ onError(error, "setMany");
48
+ }
49
+ },
50
+ async update(key, updater) {
51
+ try {
52
+ await provider.update(key, updater);
53
+ } catch (error) {
54
+ onError(error, "update");
55
+ }
56
+ },
57
+ async updateMany(keys, updater) {
58
+ try {
59
+ await provider.updateMany(keys, updater);
60
+ } catch (error) {
61
+ onError(error, "updateMany");
62
+ }
63
+ },
64
+ async delete(key) {
65
+ try {
66
+ await provider.delete(key);
67
+ hooks?.onEvict?.(key);
68
+ } catch (error) {
69
+ onError(error, "delete");
70
+ }
71
+ },
72
+ async deleteMany(keys) {
73
+ try {
74
+ await provider.deleteMany(keys);
75
+ for (const key of keys) hooks?.onEvict?.(key);
76
+ } catch (error) {
77
+ onError(error, "deleteMany");
78
+ }
79
+ },
80
+ clear() {
81
+ return provider.clear();
82
+ },
83
+ close() {
84
+ if (isClosed) return;
85
+ isClosed = true;
86
+ return provider.close();
87
+ }
88
+ };
89
+ };
90
+
91
+ //#endregion
92
+ //#region src/core/cache/providers/identityMapCache.ts
93
+ const identityMapCache = () => {
94
+ const store = /* @__PURE__ */ new Map();
95
+ return {
96
+ cacheType: "pongo:cache:identity-map",
97
+ get: (key) => Promise.resolve(store.has(key) ? store.get(key) : void 0),
98
+ getMany: (keys) => keys.map((k) => store.has(k) ? store.get(k) : void 0),
99
+ set: (key, value) => {
100
+ store.set(key, value);
101
+ },
102
+ setMany: (entries) => {
103
+ for (const { key, value } of entries) store.set(key, value);
104
+ },
105
+ update: (key, _updater) => {
106
+ store.delete(key);
107
+ },
108
+ updateMany: (keys, _updater) => {
109
+ for (const key of keys) store.delete(key);
110
+ },
111
+ delete: (key) => {
112
+ store.delete(key);
113
+ },
114
+ deleteMany: (keys) => {
115
+ for (const key of keys) store.delete(key);
116
+ },
117
+ clear: () => {
118
+ store.clear();
119
+ },
120
+ close: () => {
121
+ store.clear();
122
+ }
123
+ };
124
+ };
125
+
126
+ //#endregion
127
+ //#region src/core/cache/providers/lruCache.ts
128
+ const defaultLRUCacheOptions = { max: 1e3 };
129
+ const lruCache = (options) => {
130
+ const cache = new LRUCache({
131
+ ...defaultLRUCacheOptions,
132
+ ...options
133
+ });
134
+ return {
135
+ cacheType: "pongo:cache:lru",
136
+ get: (key) => {
137
+ const entry = cache.get(key);
138
+ if (entry === void 0) return void 0;
139
+ return entry.doc;
140
+ },
141
+ getMany: (keys) => keys.map((k) => {
142
+ const entry = cache.get(k);
143
+ if (entry === void 0) return void 0;
144
+ return entry.doc;
145
+ }),
146
+ set: (key, value) => {
147
+ cache.set(key, { doc: value });
148
+ },
149
+ setMany: (entries) => {
150
+ for (const { key, value } of entries) cache.set(key, { doc: value });
151
+ },
152
+ update: (key, _updater) => {
153
+ cache.delete(key);
154
+ },
155
+ updateMany(keys, _updater) {
156
+ for (const key of keys) cache.delete(key);
157
+ },
158
+ delete: (key) => {
159
+ cache.delete(key);
160
+ },
161
+ deleteMany: (keys) => {
162
+ for (const key of keys) cache.delete(key);
163
+ },
164
+ clear: () => {
165
+ cache.clear();
166
+ },
167
+ close: () => {
168
+ cache.clear();
169
+ }
170
+ };
171
+ };
172
+
173
+ //#endregion
174
+ //#region src/core/cache/providers/noopCache.ts
175
+ const noopCacheProvider = {
176
+ cacheType: "pongo:cache:no-op",
177
+ get: () => void 0,
178
+ set: () => {},
179
+ update: () => {},
180
+ delete: () => {},
181
+ getMany: (keys) => keys.map(() => void 0),
182
+ setMany: () => {},
183
+ updateMany: () => {},
184
+ deleteMany: () => {},
185
+ clear: () => {},
186
+ close: () => {}
187
+ };
188
+
189
+ //#endregion
190
+ //#region src/core/cache/pongoCache.ts
191
+ const DEFAULT_CONFIG = { type: "in-memory" };
192
+ const pongoCache = (options) => {
193
+ if (options === void 0 || options === "disabled") return noopCacheProvider;
194
+ if ("cacheType" in options) return options;
195
+ const config = options ?? DEFAULT_CONFIG;
196
+ if (config.type === "identity-map") return identityMapCache();
197
+ return pongoCacheWrapper({ provider: lruCache(config) });
198
+ };
199
+
200
+ //#endregion
201
+ //#region src/core/collection/filters/filters.ts
202
+ const asPlainObjectWithSingleKey = (filter, key) => filter && typeof filter === "object" && !Array.isArray(filter) && Object.keys(filter).length === 1 && key in filter ? { [key]: filter[key] } : void 0;
203
+ const idFromFilter = (filter) => {
204
+ const idFilter = asPlainObjectWithSingleKey(filter, "_id");
205
+ return typeof idFilter?.["_id"] === "string" ? idFilter["_id"] : void 0;
206
+ };
207
+ const getIdsFromIdOnlyFilter = (filter) => {
208
+ const idFilter = asPlainObjectWithSingleKey(filter, "_id");
209
+ if (!idFilter) return void 0;
210
+ const idValue = idFilter["_id"];
211
+ if (typeof idValue === "string") return [idValue];
212
+ const $in = idValue && typeof idValue === "object" && "$in" in idValue ? idValue["$in"] : void 0;
213
+ return Array.isArray($in) && $in.every((i) => typeof i === "string") ? $in : void 0;
214
+ };
215
+
216
+ //#endregion
217
+ //#region src/core/collection/handle.ts
218
+ function DocumentCommandHandler(deps) {
219
+ const fn = async (input, handle, options) => {
220
+ const results = (await handleDocuments(deps.storage, normalizeInput(input), handle, options)).map(({ change, result: outcome }) => toHandleResult(deps, change, outcome));
221
+ return Array.isArray(input) ? results : results[0];
222
+ };
223
+ return fn;
224
+ }
225
+ async function handleDocuments(storage, inputs, handle, options) {
226
+ if (inputs.length === 0) return [];
227
+ const { parallel, ...operationOptions } = options ?? {};
228
+ await storage.ensureCollectionCreated(operationOptions);
229
+ const docs = await storage.fetchByIds(inputs.map((i) => i._id), operationOptions);
230
+ return await executeStorageChanges(storage, await mapAsync(inputs, (item, i) => handleDocument({
231
+ ...item,
232
+ existing: docs[i] ?? null
233
+ }, handle), { parallel }), operationOptions);
234
+ }
235
+ async function handleDocument(item, handle) {
236
+ const { _id: id, existing, expectedVersion } = item;
237
+ if (hasVersionMismatch(existing, expectedVersion)) return {
238
+ type: "noop",
239
+ existing,
240
+ versionMismatch: true
241
+ };
242
+ const handlers = Array.isArray(handle) ? handle : [handle];
243
+ let result = existing ? { ...existing } : null;
244
+ for (const handler of handlers) result = await handler(result, id);
245
+ return toDocumentChange(id, existing, result);
246
+ }
247
+ async function executeStorageChanges(storage, changes, operationOptions) {
248
+ const toInsert = changes.flatMap((c) => c.type === "insert" ? [c.doc] : []);
249
+ const toReplace = changes.flatMap((c) => {
250
+ if (c.type !== "replace") return [];
251
+ const { _version: _, ...cleanResult } = c.result;
252
+ return [c._version !== void 0 ? {
253
+ ...cleanResult,
254
+ _version: c._version
255
+ } : cleanResult];
256
+ });
257
+ const toDelete = changes.flatMap((c) => c.type === "delete" ? [c._version !== void 0 ? {
258
+ _id: c.docId,
259
+ _version: c._version
260
+ } : { _id: c.docId }] : []);
261
+ const insertedIds = toInsert.length > 0 ? new Set((await storage.insertMany(toInsert, operationOptions)).insertedIds) : /* @__PURE__ */ new Set();
262
+ const replaceResult = toReplace.length > 0 ? await storage.replaceMany(toReplace, operationOptions) : null;
263
+ const deletedIds = toDelete.length > 0 ? (await storage.deleteManyByIds(toDelete, operationOptions)).deletedIds : /* @__PURE__ */ new Set();
264
+ const toDocumentHandlerResult = (change) => {
265
+ if (change.type === "noop") return { succeeded: !change.versionMismatch };
266
+ if (change.type === "insert") return {
267
+ succeeded: insertedIds.has(change.doc._id),
268
+ newVersion: 1n
269
+ };
270
+ if (change.type === "delete") return { succeeded: deletedIds.has(change.docId) };
271
+ const id = change.result._id;
272
+ return {
273
+ succeeded: replaceResult?.modifiedIds.includes(id) ?? false,
274
+ newVersion: replaceResult?.nextExpectedVersions.get(id) ?? 0n
275
+ };
276
+ };
277
+ return changes.map((change) => ({
278
+ change,
279
+ result: toDocumentHandlerResult(change)
280
+ }));
281
+ }
282
+ function normalizeInput(input) {
283
+ if (typeof input === "string") return [{ _id: input }];
284
+ if (!Array.isArray(input)) return [input];
285
+ return input.map((item) => typeof item === "string" ? { _id: item } : item);
286
+ }
287
+ function hasVersionMismatch(existing, version) {
288
+ const expected = expectedVersionValue(version);
289
+ return existing == null && version === "DOCUMENT_EXISTS" || existing == null && expected != null || existing != null && version === "DOCUMENT_DOES_NOT_EXIST" || existing != null && expected !== null && existing._version !== expected;
290
+ }
291
+ function toDocumentChange(docId, existing, result) {
292
+ if (deepEquals(existing, result)) return {
293
+ type: "noop",
294
+ existing
295
+ };
296
+ if (!existing && result) return {
297
+ type: "insert",
298
+ doc: {
299
+ ...result,
300
+ _id: docId
301
+ }
302
+ };
303
+ if (existing && !result) return {
304
+ type: "delete",
305
+ docId,
306
+ _version: existing._version
307
+ };
308
+ return {
309
+ type: "replace",
310
+ existing,
311
+ result: {
312
+ ...result,
313
+ _id: docId
314
+ },
315
+ _version: existing._version
316
+ };
317
+ }
318
+ function toHandleResult(deps, change, { succeeded, newVersion }) {
319
+ const opMeta = {
320
+ operationName: "handle",
321
+ collectionName: deps.collectionName,
322
+ serializer: deps.serializer,
323
+ errors: deps.errors
324
+ };
325
+ const toResult = (op, document) => ({
326
+ ...operationResult(op, opMeta),
327
+ document
328
+ });
329
+ if (change.type === "noop") return toResult({ successful: succeeded }, change.existing);
330
+ if (change.type === "insert") return toResult({
331
+ successful: succeeded,
332
+ insertedId: succeeded ? change.doc._id : null,
333
+ nextExpectedVersion: 1n
334
+ }, succeeded ? {
335
+ ...change.doc,
336
+ _version: 1n
337
+ } : null);
338
+ if (change.type === "delete") return toResult({
339
+ successful: succeeded,
340
+ deletedCount: succeeded ? 1 : 0,
341
+ matchedCount: 1
342
+ }, null);
343
+ return toResult({
344
+ successful: succeeded,
345
+ modifiedCount: succeeded ? 1 : 0,
346
+ matchedCount: 1,
347
+ nextExpectedVersion: newVersion ?? 0n
348
+ }, succeeded ? {
349
+ ...change.result,
350
+ _version: newVersion
351
+ } : change.existing);
352
+ }
353
+
354
+ //#endregion
355
+ //#region src/core/collection/pongoCollection.ts
356
+ const enlistIntoTransactionIfActive = async (db, options) => {
357
+ const transaction = options?.session?.transaction;
358
+ if (!transaction || !transaction.isActive) return null;
359
+ return await transaction.enlistDatabase(db);
360
+ };
361
+ const transactionExecutorOrDefault = async (db, options, defaultSqlExecutor) => {
362
+ return (await enlistIntoTransactionIfActive(db, options))?.execute ?? defaultSqlExecutor;
363
+ };
364
+ const pongoCollection = ({ db, collectionName, pool, schemaComponent, schema, errors, serializer, cache: cacheOptions }) => {
365
+ const SqlFor = schemaComponent.sqlBuilder;
366
+ const sqlExecutor = pool.execute;
367
+ const cache = pongoCache(cacheOptions);
368
+ const columnMapping = { mapping: {
369
+ ...mapColumnToJSON("data", serializer),
370
+ ...mapColumnToBigint("_version")
371
+ } };
372
+ const command = async (sql, options) => (await transactionExecutorOrDefault(db, options, sqlExecutor)).command(sql, columnMapping);
373
+ const query = async (sql, options) => (await transactionExecutorOrDefault(db, options, sqlExecutor)).query(sql, columnMapping);
374
+ let shouldMigrate = schema?.autoMigration !== "None";
375
+ const createCollection = (options) => {
376
+ shouldMigrate = false;
377
+ if (options?.session) return command(SqlFor.createCollection(), options);
378
+ else return command(SqlFor.createCollection());
379
+ };
380
+ const ensureCollectionCreated = (options) => {
381
+ if (!shouldMigrate) return Promise.resolve();
382
+ return createCollection(options);
383
+ };
384
+ const upcast = schema?.versioning?.upcast ?? ((doc) => doc);
385
+ const downcast = schema?.versioning?.downcast ?? ((doc) => doc);
386
+ const toStored = (document) => ({
387
+ ...downcast(document),
388
+ _id: document._id,
389
+ _version: document._version
390
+ });
391
+ const fromStored = (stored) => ({
392
+ ...upcast(stored),
393
+ _id: stored._id,
394
+ _version: stored._version
395
+ });
396
+ const findOneStoredFromDb = async (filter, options) => {
397
+ const row = (await query(SqlFor.findOne(filter), options)).rows[0];
398
+ return row ? {
399
+ ...row.data,
400
+ _id: row._id,
401
+ _version: row._version
402
+ } : null;
403
+ };
404
+ const cacheKey = (id) => `${db.databaseName}:${collectionName}:${id}`;
405
+ const txCacheFor = (options) => options?.session?.transaction?.cache ?? null;
406
+ const resolveFromCache = async (key, options) => {
407
+ const txCache = txCacheFor(options);
408
+ if (txCache) {
409
+ const cached = await txCache.get(key);
410
+ if (cached !== void 0) return cached;
411
+ }
412
+ return cache.get(key);
413
+ };
414
+ const findManyFromCache = async (keys, options) => {
415
+ const txCache = txCacheFor(options);
416
+ if (!txCache) return cache.getMany(keys);
417
+ const txResults = await txCache.getMany(keys);
418
+ const mainResults = await cache.getMany(keys);
419
+ return keys.map((_, i) => txResults[i] !== void 0 ? txResults[i] : mainResults[i]);
420
+ };
421
+ const fetchByIds = async (ids, options) => {
422
+ const cachedResults = await findManyFromCache(ids.map(cacheKey), options);
423
+ const missIds = ids.filter((_, i) => cachedResults[i] === void 0);
424
+ let dbDocsById = /* @__PURE__ */ new Map();
425
+ if (missIds.length > 0) {
426
+ const dbDocs = (await query(SqlFor.find({ _id: { $in: missIds } }, options))).rows.map((row) => ({
427
+ ...row.data,
428
+ _id: row._id,
429
+ _version: row._version
430
+ }));
431
+ dbDocsById = new Map(dbDocs.map((d) => [d._id, d]));
432
+ const leftovers = missIds.map((id) => [id, dbDocsById.get(id) ?? null]);
433
+ await cacheSetMany(leftovers.map((d) => d[1]).filter((d) => d !== null), options);
434
+ await cacheDeleteMany(leftovers.filter(([, doc]) => doc === null).map(([id]) => id), options);
435
+ }
436
+ return ids.map((id, i) => {
437
+ const cached = cachedResults[i];
438
+ if (cached !== void 0) return cached;
439
+ return dbDocsById.get(id) ?? null;
440
+ });
441
+ };
442
+ const findManyByIds = async (ids, options) => {
443
+ return (await fetchByIds(ids, options)).filter((doc) => doc !== null).map(fromStored);
444
+ };
445
+ const cacheSet = (value, options) => {
446
+ const key = cacheKey(value._id);
447
+ const txCache = txCacheFor(options);
448
+ if (txCache) return txCache.set(key, value, { mainCache: cache });
449
+ return cache.set(key, value);
450
+ };
451
+ const cacheSetMany = (documents, options) => {
452
+ const txCache = txCacheFor(options);
453
+ const entries = documents.map((d) => ({
454
+ key: cacheKey(d._id),
455
+ value: d
456
+ }));
457
+ if (txCache) return txCache.setMany(entries, { mainCache: cache });
458
+ return cache.setMany(entries);
459
+ };
460
+ const cacheDelete = (id, options) => {
461
+ const txCache = txCacheFor(options);
462
+ const key = cacheKey(id);
463
+ if (txCache) return txCache.delete(key, { mainCache: cache });
464
+ return cache.delete(key);
465
+ };
466
+ const cacheDeleteMany = (ids, options) => {
467
+ const txCache = txCacheFor(options);
468
+ const keys = ids.map(cacheKey);
469
+ if (txCache) return txCache.deleteMany(keys, { mainCache: cache });
470
+ return cache.deleteMany(keys);
471
+ };
472
+ const deleteManyByIds = async (ids, options) => {
473
+ await ensureCollectionCreated(options);
474
+ const result = await command(SqlFor.deleteManyByIds(ids), options);
475
+ const deletedIds = new Set(result.rows.filter((row) => (row.deleted ?? 1) > 0).map((row) => row._id));
476
+ if (!options?.skipCache) await cacheDeleteMany([...deletedIds], options);
477
+ return operationResult({
478
+ successful: deletedIds.size > 0,
479
+ deletedCount: deletedIds.size,
480
+ matchedCount: ids.length,
481
+ deletedIds
482
+ }, {
483
+ operationName: "deleteManyByIds",
484
+ collectionName,
485
+ serializer,
486
+ errors
487
+ });
488
+ };
489
+ const executeUpsert = async (storedRows, options) => {
490
+ const result = await command(SqlFor.insertOrReplace(storedRows), options);
491
+ const writtenIds = result.rows.map((r) => r._id);
492
+ const versions = new Map(result.rows.map((r) => [r._id, BigInt(r.version ?? 1n)]));
493
+ if (!options?.skipCache) {
494
+ const writtenSet = new Set(writtenIds);
495
+ const cacheEntries = storedRows.filter((r) => writtenSet.has(r._id)).map((r) => ({
496
+ ...r,
497
+ _version: versions.get(r._id) ?? 1n
498
+ }));
499
+ if (cacheEntries.length > 0) await cacheSetMany(cacheEntries, options);
500
+ }
501
+ return {
502
+ writtenIds,
503
+ versions
504
+ };
505
+ };
506
+ const collection = {
507
+ dbName: db.databaseName,
508
+ collectionName,
509
+ createCollection: async (options) => {
510
+ await createCollection(options);
511
+ },
512
+ insertOne: async (document, options) => {
513
+ await ensureCollectionCreated(options);
514
+ const _id = document._id ?? v7();
515
+ const _version = document._version ?? 1n;
516
+ const stored = toStored({
517
+ ...document,
518
+ _id,
519
+ _version
520
+ });
521
+ if (options?.upsert) {
522
+ const { writtenIds, versions } = await executeUpsert([stored], options);
523
+ const successful = writtenIds.length > 0;
524
+ const nextExpectedVersion = versions.get(_id) ?? _version;
525
+ return operationResult({
526
+ successful,
527
+ insertedId: successful ? _id : null,
528
+ nextExpectedVersion
529
+ }, {
530
+ operationName: "insertOne",
531
+ collectionName,
532
+ serializer,
533
+ errors
534
+ });
535
+ }
536
+ const successful = ((await command(SqlFor.insertOne(stored), options)).rowCount ?? 0) > 0;
537
+ if (successful && !options?.skipCache) await cacheSet(stored, options);
538
+ return operationResult({
539
+ successful,
540
+ insertedId: successful ? _id : null,
541
+ nextExpectedVersion: _version
542
+ }, {
543
+ operationName: "insertOne",
544
+ collectionName,
545
+ serializer,
546
+ errors
547
+ });
548
+ },
549
+ insertMany: async (documents, options) => {
550
+ await ensureCollectionCreated(options);
551
+ const rows = documents.map((doc) => doc._id && doc._version ? doc : {
552
+ ...doc,
553
+ _id: doc._id ?? v7(),
554
+ _version: doc._version ?? 1n
555
+ }).map((d) => toStored(d));
556
+ if (options?.upsert) {
557
+ const { writtenIds } = await executeUpsert(rows, options);
558
+ return operationResult({
559
+ successful: writtenIds.length === rows.length,
560
+ insertedCount: writtenIds.length,
561
+ insertedIds: writtenIds
562
+ }, {
563
+ operationName: "insertMany",
564
+ collectionName,
565
+ serializer,
566
+ errors
567
+ });
568
+ }
569
+ const result = await command(SqlFor.insertMany(rows), options);
570
+ if (!options?.skipCache) {
571
+ const insertedIdSet = new Set(result.rows.map((d) => d._id));
572
+ await cacheSetMany(rows.filter((r) => insertedIdSet.has(r._id)), options);
573
+ }
574
+ return operationResult({
575
+ successful: result.rowCount === rows.length,
576
+ insertedCount: result.rowCount ?? 0,
577
+ insertedIds: result.rows.map((d) => d._id)
578
+ }, {
579
+ operationName: "insertMany",
580
+ collectionName,
581
+ serializer,
582
+ errors
583
+ });
584
+ },
585
+ updateOne: async (filter, update, options) => {
586
+ await ensureCollectionCreated(options);
587
+ const result = await command(SqlFor.updateOne(filter, update, options), options);
588
+ const opResult = operationResult({
589
+ successful: result.rows.length > 0 && result.rows[0].modified === result.rows[0].matched,
590
+ modifiedCount: Number(result.rows[0]?.modified ?? 0),
591
+ matchedCount: Number(result.rows[0]?.matched ?? 0),
592
+ upsertedId: null,
593
+ upsertedCount: 0,
594
+ nextExpectedVersion: BigInt(result.rows[0]?.version ?? 0n)
595
+ }, {
596
+ operationName: "updateOne",
597
+ collectionName,
598
+ serializer,
599
+ errors
600
+ });
601
+ if (opResult.successful && !options?.skipCache) {
602
+ const id = idFromFilter(filter);
603
+ if (id) await cacheDelete(id, options);
604
+ }
605
+ return opResult;
606
+ },
607
+ replaceOne: async (filter, document, options) => {
608
+ await ensureCollectionCreated(options);
609
+ const noConcurrencyCheck = options?.expectedVersion === void 0 || options.expectedVersion === "NO_CONCURRENCY_CHECK";
610
+ if (options?.upsert && noConcurrencyCheck) {
611
+ const _id = idFromFilter(filter) ?? v7();
612
+ const { writtenIds, versions } = await executeUpsert([toStored({
613
+ ...document,
614
+ _id,
615
+ _version: 1n
616
+ })], options);
617
+ const written = writtenIds.length > 0;
618
+ const nextExpectedVersion = versions.get(_id) ?? 1n;
619
+ const inserted = written && nextExpectedVersion === 1n;
620
+ return operationResult({
621
+ successful: written,
622
+ modifiedCount: written && !inserted ? 1 : 0,
623
+ matchedCount: written && !inserted ? 1 : 0,
624
+ upsertedId: inserted ? _id : null,
625
+ upsertedCount: inserted ? 1 : 0,
626
+ nextExpectedVersion
627
+ }, {
628
+ operationName: "replaceOne",
629
+ collectionName,
630
+ serializer,
631
+ errors
632
+ });
633
+ }
634
+ const downcasted = downcast(document);
635
+ const result = await command(SqlFor.replaceOne(filter, downcasted, options), options);
636
+ const opResult = operationResult({
637
+ successful: result.rows.length > 0 && result.rows[0].modified > 0,
638
+ modifiedCount: Number(result.rows[0]?.modified ?? 0),
639
+ matchedCount: Number(result.rows[0]?.matched ?? 0),
640
+ upsertedId: null,
641
+ upsertedCount: 0,
642
+ nextExpectedVersion: BigInt(result.rows[0]?.version ?? 0n)
643
+ }, {
644
+ operationName: "replaceOne",
645
+ collectionName,
646
+ serializer,
647
+ errors
648
+ });
649
+ if (opResult.successful && !options?.skipCache) {
650
+ const _id = idFromFilter(filter);
651
+ if (_id) await cacheSet(toStored({
652
+ ...document,
653
+ _id,
654
+ _version: opResult.nextExpectedVersion
655
+ }), options);
656
+ }
657
+ return opResult;
658
+ },
659
+ updateMany: async (filter, update, options) => {
660
+ await ensureCollectionCreated(options);
661
+ const result = await command(SqlFor.updateMany(filter, update), options);
662
+ return operationResult({
663
+ successful: true,
664
+ modifiedCount: result.rowCount ?? 0,
665
+ matchedCount: result.rowCount ?? 0
666
+ }, {
667
+ operationName: "updateMany",
668
+ collectionName,
669
+ serializer,
670
+ errors
671
+ });
672
+ },
673
+ deleteOne: async (filter, options) => {
674
+ await ensureCollectionCreated(options);
675
+ const result = await command(SqlFor.deleteOne(filter ?? {}, options), options);
676
+ const opResult = operationResult({
677
+ successful: result.rows.length > 0 && result.rows[0].deleted > 0,
678
+ deletedCount: Number(result.rows[0]?.deleted ?? 0),
679
+ matchedCount: Number(result.rows[0]?.matched ?? 0)
680
+ }, {
681
+ operationName: "deleteOne",
682
+ collectionName,
683
+ serializer,
684
+ errors
685
+ });
686
+ if (opResult.successful && !options?.skipCache && filter) {
687
+ const id = idFromFilter(filter);
688
+ if (id) await cacheDelete(id, options);
689
+ }
690
+ return opResult;
691
+ },
692
+ deleteMany: async (filter, options) => {
693
+ const ids = filter ? getIdsFromIdOnlyFilter(filter) : null;
694
+ if (ids) return deleteManyByIds(ids.map((id) => ({ _id: id })), options);
695
+ await ensureCollectionCreated(options);
696
+ const result = await command(SqlFor.deleteMany(filter ?? {}), options);
697
+ return operationResult({
698
+ successful: (result.rowCount ?? 0) > 0,
699
+ deletedCount: result.rowCount ?? 0,
700
+ matchedCount: result.rowCount ?? 0
701
+ }, {
702
+ operationName: "deleteMany",
703
+ collectionName,
704
+ serializer,
705
+ errors
706
+ });
707
+ },
708
+ findOne: async (filter, options) => {
709
+ await ensureCollectionCreated(options);
710
+ const id = filter && !options?.skipCache ? idFromFilter(filter) : null;
711
+ if (id) {
712
+ const cached = await resolveFromCache(cacheKey(id), options);
713
+ if (cached !== void 0) return cached !== null ? fromStored(cached) : null;
714
+ const stored = await findOneStoredFromDb(filter, options);
715
+ if (stored) await cacheSet(stored, options);
716
+ else await cacheDelete(id, options);
717
+ return stored ? fromStored(stored) : null;
718
+ }
719
+ const stored = await findOneStoredFromDb(filter ?? {}, options);
720
+ return stored ? fromStored(stored) : null;
721
+ },
722
+ findOneAndDelete: async (filter, options) => {
723
+ await ensureCollectionCreated(options);
724
+ const existingDoc = await collection.findOne(filter, options);
725
+ if (existingDoc === null) return null;
726
+ await collection.deleteOne(filter, options);
727
+ return existingDoc;
728
+ },
729
+ findOneAndReplace: async (filter, replacement, options) => {
730
+ await ensureCollectionCreated(options);
731
+ const existingDoc = await collection.findOne(filter, options);
732
+ if (existingDoc === null) return null;
733
+ await collection.replaceOne(filter, replacement, options);
734
+ return existingDoc;
735
+ },
736
+ findOneAndUpdate: async (filter, update, options) => {
737
+ await ensureCollectionCreated(options);
738
+ const existingDoc = await collection.findOne(filter, options);
739
+ if (existingDoc === null) return null;
740
+ await collection.updateOne(filter, update, options);
741
+ return existingDoc;
742
+ },
743
+ replaceMany: async (documents, options) => {
744
+ await ensureCollectionCreated(options);
745
+ const rows = documents.map((d) => toStored(d));
746
+ if (options?.upsert) {
747
+ const versioned = documents.map((d) => "_version" in d && d._version !== void 0);
748
+ const hasVersions = versioned.some((v) => v);
749
+ const allVersioned = versioned.every((v) => v);
750
+ if (hasVersions && !allVersioned) throw new Error("replaceMany with upsert cannot mix documents with and without _version in a single batch");
751
+ if (!hasVersions) {
752
+ const { writtenIds, versions } = await executeUpsert(rows, options);
753
+ const conflictIds = documents.map((d) => d._id).filter((id) => !writtenIds.includes(id));
754
+ return operationResult({
755
+ successful: conflictIds.length === 0,
756
+ modifiedCount: writtenIds.length,
757
+ matchedCount: documents.length,
758
+ modifiedIds: [...writtenIds],
759
+ conflictIds: [...conflictIds],
760
+ nextExpectedVersions: versions
761
+ }, {
762
+ operationName: "replaceMany",
763
+ collectionName,
764
+ serializer,
765
+ errors
766
+ });
767
+ }
768
+ }
769
+ const result = await command(SqlFor.replaceMany(rows), options);
770
+ const modifiedIds = result.rows.map((row) => row._id);
771
+ const conflictIds = documents.map((d) => d._id).filter((id) => !modifiedIds.includes(id));
772
+ const versions = new Map(result.rows.map((row) => [row._id, BigInt(row.version ?? 1n)]));
773
+ if (!options?.skipCache) {
774
+ const cacheEntries = rows.filter((r) => modifiedIds.includes(r._id)).map((r) => ({
775
+ ...r,
776
+ _version: versions.get(r._id) ?? r._version ?? 1n
777
+ }));
778
+ if (cacheEntries.length > 0) await cacheSetMany(cacheEntries, options);
779
+ if (conflictIds.length > 0) await cacheDeleteMany([...conflictIds], options);
780
+ }
781
+ return operationResult({
782
+ successful: modifiedIds.length > 0 && conflictIds.length === 0,
783
+ modifiedCount: modifiedIds.length,
784
+ matchedCount: documents.length,
785
+ modifiedIds: [...modifiedIds],
786
+ conflictIds: [...conflictIds],
787
+ nextExpectedVersions: versions
788
+ }, {
789
+ operationName: "replaceMany",
790
+ collectionName,
791
+ serializer,
792
+ errors
793
+ });
794
+ },
795
+ handle: DocumentCommandHandler({
796
+ collectionName,
797
+ serializer,
798
+ errors,
799
+ storage: {
800
+ ensureCollectionCreated,
801
+ fetchByIds: (ids, options) => fetchByIds(ids, options).then((rows) => rows.map((stored) => stored ? fromStored(stored) : null)),
802
+ insertMany: (docs, options) => collection.insertMany(docs, options),
803
+ replaceMany: (docs, options) => collection.replaceMany(docs, options),
804
+ deleteManyByIds
805
+ }
806
+ }),
807
+ find: async (filter, options) => {
808
+ await ensureCollectionCreated(options);
809
+ if (!options?.skipCache && filter) {
810
+ const ids = getIdsFromIdOnlyFilter(filter);
811
+ if (ids && ids.length > 0) return findManyByIds(ids, options);
812
+ }
813
+ return (await query(SqlFor.find(filter ?? {}, options))).rows.map((row) => fromStored({
814
+ ...row.data,
815
+ _id: row._id,
816
+ _version: row._version
817
+ }));
818
+ },
819
+ countDocuments: async (filter, options) => {
820
+ await ensureCollectionCreated(options);
821
+ const { count } = await single(query(SqlFor.countDocuments(filter ?? {})));
822
+ return count;
823
+ },
824
+ drop: async (options) => {
825
+ await ensureCollectionCreated(options);
826
+ return ((await command(SqlFor.drop()))?.rowCount ?? 0) > 0;
827
+ },
828
+ rename: async (newName, options) => {
829
+ await ensureCollectionCreated(options);
830
+ await command(SqlFor.rename(newName));
831
+ collectionName = newName;
832
+ return collection;
833
+ },
834
+ close: () => cache.close(),
835
+ sql: {
836
+ async query(sql, options) {
837
+ await ensureCollectionCreated(options);
838
+ return (await query(sql, options)).rows;
839
+ },
840
+ async command(sql, options) {
841
+ await ensureCollectionCreated(options);
842
+ return command(sql, options);
843
+ }
844
+ },
845
+ schema: {
846
+ component: schemaComponent,
847
+ migrate: (options) => runSQLMigrations(pool, schemaComponent.migrations, options)
848
+ }
849
+ };
850
+ return collection;
851
+ };
852
+
853
+ //#endregion
854
+ //#region src/core/collection/pongoCollectionSchemaComponent.ts
855
+ const PongoCollectionSchemaComponent = ({ definition, migrationsOrSchemaComponents, sqlBuilder }) => ({
856
+ ...schemaComponent(`sc:pongo:collection:${definition.name}`, migrationsOrSchemaComponents),
857
+ sqlBuilder,
858
+ definition,
859
+ collectionName: definition.name
860
+ });
861
+
862
+ //#endregion
863
+ //#region src/core/collection/query.ts
864
+ const QueryOperators = {
865
+ $eq: "$eq",
866
+ $gt: "$gt",
867
+ $gte: "$gte",
868
+ $lt: "$lt",
869
+ $lte: "$lte",
870
+ $ne: "$ne",
871
+ $in: "$in",
872
+ $nin: "$nin",
873
+ $elemMatch: "$elemMatch",
874
+ $all: "$all",
875
+ $size: "$size"
876
+ };
877
+ const OperatorMap = {
878
+ $gt: ">",
879
+ $gte: ">=",
880
+ $lt: "<",
881
+ $lte: "<=",
882
+ $ne: "!="
883
+ };
884
+ const isOperator = (key) => key.startsWith("$");
885
+ const hasOperators = (value) => Object.keys(value).some(isOperator);
886
+
887
+ //#endregion
888
+ //#region src/core/typing/entries.ts
889
+ const objectEntries = (obj) => Object.entries(obj).map(([key, value]) => [key, value]);
890
+
891
+ //#endregion
892
+ //#region src/core/errors/index.ts
893
+ const isNumber = (val) => typeof val === "number" && val === val;
894
+ const isString = (val) => typeof val === "string";
895
+ var PongoError = class PongoError extends Error {
896
+ errorCode;
897
+ constructor(options) {
898
+ const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : 500;
899
+ const message = options && typeof options === "object" && "message" in options ? options.message : isString(options) ? options : `Error with status code '${errorCode}' ocurred during Pongo processing`;
900
+ super(message);
901
+ this.errorCode = errorCode;
902
+ Object.setPrototypeOf(this, PongoError.prototype);
903
+ }
904
+ };
905
+ var ConcurrencyError = class ConcurrencyError extends PongoError {
906
+ constructor(message) {
907
+ super({
908
+ errorCode: 412,
909
+ message: message ?? `Expected document state does not match current one!`
910
+ });
911
+ Object.setPrototypeOf(this, ConcurrencyError.prototype);
912
+ }
913
+ };
914
+
915
+ //#endregion
916
+ //#region src/core/typing/operations.ts
917
+ const DOCUMENT_DOES_NOT_EXIST = "DOCUMENT_DOES_NOT_EXIST";
918
+ const NO_CONCURRENCY_CHECK = "NO_CONCURRENCY_CHECK";
919
+ const isGeneralExpectedDocumentVersion = (version) => version === "DOCUMENT_DOES_NOT_EXIST" || version === "DOCUMENT_EXISTS" || version === "NO_CONCURRENCY_CHECK";
920
+ const expectedVersionValue = (version) => version === void 0 || isGeneralExpectedDocumentVersion(version) ? null : version;
921
+ const expectedVersionPredicate = (version) => {
922
+ if (version === void 0) return { operator: "none" };
923
+ if (isGeneralExpectedDocumentVersion(version)) return version === "DOCUMENT_DOES_NOT_EXIST" ? {
924
+ operator: "=",
925
+ value: 0n
926
+ } : { operator: "none" };
927
+ return {
928
+ operator: "=",
929
+ value: version
930
+ };
931
+ };
932
+ const operationResult = (result, options) => {
933
+ const operationResult = {
934
+ ...result,
935
+ acknowledged: true,
936
+ successful: result.successful,
937
+ assertSuccessful: (errorMessage) => {
938
+ const { successful } = result;
939
+ const { operationName, collectionName } = options;
940
+ if (!successful) throw new ConcurrencyError(errorMessage ?? `${operationName} on ${collectionName} failed. Expected document state does not match current one! Result: ${options.serializer.serialize(result)}!`);
941
+ }
942
+ };
943
+ if (options.errors?.throwOnOperationFailures) operationResult.assertSuccessful();
944
+ return operationResult;
945
+ };
946
+
947
+ //#endregion
948
+ //#region src/core/schema/index.ts
949
+ const pongoCollectionSchema = (name) => ({ name });
950
+ pongoCollectionSchema.from = (collectionNames) => collectionNames.reduce((acc, collectionName) => (acc[collectionName] = pongoSchema.collection(collectionName), acc), {});
951
+ function pongoDbSchema(nameOrCollections, collections) {
952
+ if (collections === void 0) {
953
+ if (typeof nameOrCollections === "string") throw new Error("You need to provide colleciton definition");
954
+ return { collections: nameOrCollections };
955
+ }
956
+ return nameOrCollections && typeof nameOrCollections === "string" ? {
957
+ name: nameOrCollections,
958
+ collections
959
+ } : { collections };
960
+ }
961
+ pongoDbSchema.from = (databaseName, collectionNames) => databaseName ? pongoDbSchema(databaseName, pongoCollectionSchema.from(collectionNames)) : pongoDbSchema(pongoCollectionSchema.from(collectionNames));
962
+ const pongoClientSchema = (dbs) => ({ dbs });
963
+ const pongoSchema = {
964
+ client: pongoClientSchema,
965
+ db: pongoDbSchema,
966
+ collection: pongoCollectionSchema
967
+ };
968
+ const proxyPongoDbWithSchema = (pongoDb, dbSchema, collections) => {
969
+ const collectionNames = Object.keys(dbSchema.collections);
970
+ for (const collectionName of collectionNames) collections.set(collectionName, pongoDb.collection(collectionName));
971
+ return new Proxy(pongoDb, { get(target, prop) {
972
+ return collections.get(prop) ?? target[prop];
973
+ } });
974
+ };
975
+
976
+ //#endregion
977
+ //#region src/core/database/pongoDatabaseSchemaComponent.ts
978
+ const PongoDatabaseSchemaComponent = ({ definition, collectionFactory }) => {
979
+ const collections = Object.values(definition.collections).map(collectionFactory) ?? [];
980
+ return {
981
+ ...schemaComponent(`sc:dumbo:database:${definition.name}`, { components: collections }),
982
+ definition,
983
+ collections,
984
+ collection: (schema) => {
985
+ const existing = collections.find((c) => c.collectionName === schema.name);
986
+ if (existing) return existing;
987
+ const newCollection = collectionFactory(pongoSchema.collection(schema.name));
988
+ collections.push(newCollection);
989
+ definition.collections[schema.name] = schema;
990
+ return newCollection;
991
+ }
992
+ };
993
+ };
994
+
995
+ //#endregion
996
+ //#region src/core/database/pongoDb.ts
997
+ const PongoDatabase = (options) => {
998
+ const { databaseName, schemaComponent, pool, cache: cacheOptions, serializer } = options;
999
+ const cache = cacheOptions === "disabled" || cacheOptions === void 0 ? "disabled" : pongoCache(cacheOptions);
1000
+ const collections = /* @__PURE__ */ new Map();
1001
+ const command = async (sql, options) => (await transactionExecutorOrDefault(db, options, pool.execute)).command(sql, options);
1002
+ const query = async (sql, options) => (await transactionExecutorOrDefault(db, options, pool.execute)).query(sql, options);
1003
+ const db = {
1004
+ driverType: pool.driverType,
1005
+ databaseName,
1006
+ connect: () => Promise.resolve(),
1007
+ close: async () => {
1008
+ await Promise.allSettled([
1009
+ pool.close(),
1010
+ cache !== "disabled" ? cache.close() : Promise.resolve(),
1011
+ ...collections.values().map((collection) => collection.close())
1012
+ ]);
1013
+ },
1014
+ collections: () => [...collections.values()],
1015
+ collection: (collectionName, collectionOptions) => collections.get(collectionName) ?? pongoCollection({
1016
+ collectionName,
1017
+ db,
1018
+ pool,
1019
+ schemaComponent: schemaComponent.collection(pongoSchema.collection(collectionName)),
1020
+ schema: {
1021
+ ...options.schema,
1022
+ ...collectionOptions?.schema
1023
+ },
1024
+ serializer,
1025
+ errors: {
1026
+ ...options.errors,
1027
+ ...collectionOptions?.errors
1028
+ },
1029
+ cache: collectionOptions?.cache !== void 0 ? collectionOptions.cache : cache
1030
+ }),
1031
+ transaction: () => pool.transaction(),
1032
+ withTransaction: (handle) => pool.withTransaction(handle),
1033
+ schema: {
1034
+ component: schemaComponent,
1035
+ migrate: (options) => runSQLMigrations(pool, schemaComponent.migrations, options)
1036
+ },
1037
+ sql: {
1038
+ async query(sql, options) {
1039
+ return (await query(sql, options)).rows;
1040
+ },
1041
+ async command(sql, options) {
1042
+ return command(sql, options);
1043
+ }
1044
+ }
1045
+ };
1046
+ const dbSchema = options?.schema?.definition;
1047
+ if (dbSchema) return proxyPongoDbWithSchema(db, dbSchema, collections);
1048
+ return db;
1049
+ };
1050
+
1051
+ //#endregion
1052
+ //#region src/core/drivers/databaseDriver.ts
1053
+ const PongoDriverRegistry = () => {
1054
+ const drivers = /* @__PURE__ */ new Map();
1055
+ const register = (driverType, driver) => {
1056
+ const entry = drivers.get(driverType);
1057
+ if (entry && (typeof entry !== "function" || typeof driver === "function")) return;
1058
+ drivers.set(driverType, driver);
1059
+ };
1060
+ const tryResolve = async (driverType) => {
1061
+ const entry = drivers.get(driverType);
1062
+ if (!entry) return null;
1063
+ if (typeof entry !== "function") return entry;
1064
+ const driver = await entry();
1065
+ register(driverType, driver);
1066
+ return driver;
1067
+ };
1068
+ const tryGet = (driverType) => {
1069
+ const entry = drivers.get(driverType);
1070
+ return entry && typeof entry !== "function" ? entry : null;
1071
+ };
1072
+ const has = (driverType) => drivers.has(driverType);
1073
+ return {
1074
+ register,
1075
+ tryResolve,
1076
+ tryGet,
1077
+ has,
1078
+ get databaseDriverTypes() {
1079
+ return Array.from(drivers.keys());
1080
+ }
1081
+ };
1082
+ };
1083
+ const pongoDriverRegistry = globalThis.pongoDriverRegistry = globalThis.pongoDriverRegistry ?? PongoDriverRegistry();
1084
+
1085
+ //#endregion
1086
+ //#region src/core/utils/deepEquals.ts
1087
+ const deepEquals = (left, right) => {
1088
+ if (isEquatable(left)) return left.equals(right);
1089
+ if (Array.isArray(left)) return Array.isArray(right) && left.length === right.length && left.every((val, index) => deepEquals(val, right[index]));
1090
+ if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return left === right;
1091
+ if (Array.isArray(right)) return false;
1092
+ const keys1 = Object.keys(left);
1093
+ const keys2 = Object.keys(right);
1094
+ if (keys1.length !== keys2.length || !keys1.every((key) => keys2.includes(key))) return false;
1095
+ for (const key in left) {
1096
+ if (left[key] instanceof Function && right[key] instanceof Function) continue;
1097
+ if (!deepEquals(left[key], right[key])) return false;
1098
+ }
1099
+ return true;
1100
+ };
1101
+ const isEquatable = (left) => {
1102
+ return left && typeof left === "object" && "equals" in left && typeof left["equals"] === "function";
1103
+ };
1104
+
1105
+ //#endregion
1106
+ //#region src/core/utils/mapAsync.ts
1107
+ async function mapSequential(items, fn) {
1108
+ const results = [];
1109
+ for (let i = 0; i < items.length; i++) results.push(await fn(items[i], i));
1110
+ return results;
1111
+ }
1112
+ const mapParallel = (items, fn) => Promise.all(items.map(fn));
1113
+ const mapAsync = (items, fn, options = { parallel: false }) => options?.parallel ? mapParallel(items, fn) : mapSequential(items, fn);
1114
+
1115
+ //#endregion
5
1116
  //#region src/storage/postgresql/core/sqlBuilder/filter/queryOperators.ts
6
1117
  const handleOperator = (path, operator, value, serializer) => {
7
1118
  if (path === "_id" || path === "_version") return handleMetadataOperator(path, operator, value);
8
1119
  switch (operator) {
9
1120
  case "$eq": {
10
- const nestedPath = serializer.serialize(buildNestedObject(path, value));
11
- const serializedValue = serializer.serialize(value);
12
- return SQL`(data @> ${nestedPath}::jsonb OR jsonb_path_exists(data, '$.${SQL.plain(path)}[*] ? (@ == ${SQL.plain(serializedValue)})'))`;
1121
+ const field = PostgreSQLJSON.field(SQL`data`, path);
1122
+ return SQL`(${field} = ${JSONParam.value(value, serializer)}::jsonb OR ${field} @> ${JSONParam.arrayContaining(value, serializer)}::jsonb)`;
13
1123
  }
14
1124
  case "$gt":
15
1125
  case "$gte":
16
1126
  case "$lt":
17
1127
  case "$lte":
18
- case "$ne": return SQL`data ->> '${SQL.plain(path.split(".").join(","))}' ${SQL.plain(OperatorMap[operator])} ${value}`;
19
- case "$in": return SQL`data #>> ${`{${path.split(".").join(",")}}`} = ANY (${value})`;
20
- case "$nin": return SQL`data #>> ${`{${path.split(".").join(",")}}`} != ALL (${value})`;
1128
+ case "$ne": return SQL`${PostgreSQLJSON.textField(SQL`data`, path)} ${SQL.plain(OperatorMap[operator])} ${value}`;
1129
+ case "$in": return SQL`${PostgreSQLJSON.textField(SQL`data`, path)} = ANY (${value})`;
1130
+ case "$nin": return SQL`${PostgreSQLJSON.textField(SQL`data`, path)} != ALL (${value})`;
21
1131
  case "$elemMatch": {
22
- const subQuery = objectEntries(value).map(([subKey, subValue]) => `@."${subKey}" == ${serializer.serialize(subValue)}`).join(" && ");
23
- return SQL`jsonb_path_exists(data, '$.${SQL.plain(path)}[*] ? (${SQL.plain(subQuery)})')`;
1132
+ const field = PostgreSQLJSON.field(SQL`data`, path);
1133
+ return SQL`EXISTS (
1134
+ SELECT 1
1135
+ FROM jsonb_array_elements(${SQL`CASE WHEN jsonb_typeof(${field}) = 'array' THEN ${field} ELSE '[]'::jsonb END`}) AS elem(value)
1136
+ WHERE elem.value @> ${JSONParam.value(value, serializer)}::jsonb
1137
+ )`;
24
1138
  }
25
- case "$all": return SQL`data @> ${serializer.serialize(buildNestedObject(path, value))}::jsonb`;
26
- case "$size": return SQL`jsonb_array_length(data #> ${`{${path.split(".").join(",")}}`}) = ${value}`;
1139
+ case "$all": {
1140
+ const serializedValue = JSONParam.value(value, serializer);
1141
+ return SQL`${PostgreSQLJSON.field(SQL`data`, path)} @> ${serializedValue}::jsonb`;
1142
+ }
1143
+ case "$size": return SQL`jsonb_array_length(${PostgreSQLJSON.field(SQL`data`, path)}) = ${value}`;
27
1144
  default: throw new Error(`Unsupported operator: ${operator}`);
28
1145
  }
29
1146
  };
@@ -40,7 +1157,6 @@ const handleMetadataOperator = (fieldName, operator, value) => {
40
1157
  default: throw new Error(`Unsupported operator: ${operator}`);
41
1158
  }
42
1159
  };
43
- const buildNestedObject = (path, value) => path.split(".").reverse().reduce((acc, key) => ({ [key]: acc }), value);
44
1160
 
45
1161
  //#endregion
46
1162
  //#region src/storage/postgresql/core/sqlBuilder/filter/index.ts
@@ -103,16 +1219,20 @@ const buildUpdateQuery = (update, serializer) => objectEntries(update).reduce((c
103
1219
  default: return currentUpdateQuery;
104
1220
  }
105
1221
  }, SQL`data`);
106
- const buildSetQuery = (set, currentUpdateQuery, serializer) => SQL`${currentUpdateQuery} || ${serializer.serialize(set)}::jsonb`;
107
- const buildUnsetQuery = (unset, currentUpdateQuery) => SQL`${currentUpdateQuery} - ${Object.keys(unset).map((k) => `{${k}}`).join(", ")}`;
1222
+ const buildSetQuery = (set, currentUpdateQuery, serializer) => SQL`${currentUpdateQuery} || ${JSONParam.value(set, serializer)}::jsonb`;
1223
+ const buildUnsetQuery = (unset, currentUpdateQuery) => {
1224
+ let query = currentUpdateQuery;
1225
+ for (const key of Object.keys(unset)) query = SQL`${query} - ${key}`;
1226
+ return query;
1227
+ };
108
1228
  const buildIncQuery = (inc, currentUpdateQuery) => {
109
- for (const [key, value] of Object.entries(inc)) currentUpdateQuery = typeof value === "bigint" ? SQL`jsonb_set(${currentUpdateQuery}, '{${SQL.plain(key)}}', to_jsonb((COALESCE((data->>'${SQL.plain(key)}')::BIGINT, 0) + ${value})::TEXT), true)` : SQL`jsonb_set(${currentUpdateQuery}, '{${SQL.plain(key)}}', to_jsonb(COALESCE((data->>'${SQL.plain(key)}')::NUMERIC, 0) + ${value}), true)`;
1229
+ for (const [key, value] of Object.entries(inc)) currentUpdateQuery = typeof value === "bigint" ? SQL`jsonb_set(${currentUpdateQuery}, ${PostgreSQLJSON.path(key)}, to_jsonb((COALESCE((${PostgreSQLJSON.textField(SQL`data`, key)})::BIGINT, 0) + ${value})::TEXT), true)` : SQL`jsonb_set(${currentUpdateQuery}, ${PostgreSQLJSON.path(key)}, to_jsonb(COALESCE((${PostgreSQLJSON.textField(SQL`data`, key)})::NUMERIC, 0) + ${value}), true)`;
110
1230
  return currentUpdateQuery;
111
1231
  };
112
1232
  const buildPushQuery = (push, currentUpdateQuery, serializer) => {
113
1233
  for (const [key, value] of Object.entries(push)) {
114
- const serializedValue = serializer.serialize([value]);
115
- currentUpdateQuery = SQL`jsonb_set(${currentUpdateQuery}, '{${SQL.plain(key)}}', (coalesce(data->'${SQL.plain(key)}', '[]'::jsonb) || ${serializedValue}::jsonb), true)`;
1234
+ const serializedValue = JSONParam.arrayContaining(value, serializer);
1235
+ currentUpdateQuery = SQL`jsonb_set(${currentUpdateQuery}, ${PostgreSQLJSON.path(key)}, (coalesce(${PostgreSQLJSON.field(SQL`data`, key)}, '[]'::jsonb) || ${serializedValue}::jsonb), true)`;
116
1236
  }
117
1237
  return currentUpdateQuery;
118
1238
  };
@@ -139,7 +1259,7 @@ const pongoCollectionPostgreSQLMigrations = (collectionName) => [sqlMigration(`p
139
1259
  const postgresSQLBuilder = (collectionName, serializer) => ({
140
1260
  createCollection: () => createCollection(collectionName),
141
1261
  insertOne: (document) => {
142
- const serialized = serializer.serialize(document);
1262
+ const serialized = JSONParam.document(document, serializer);
143
1263
  const id = document._id;
144
1264
  const version = document._version ?? 1n;
145
1265
  return SQL`
@@ -147,7 +1267,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
147
1267
  VALUES (${id}, ${serialized}, ${version}) ON CONFLICT(_id) DO NOTHING;`;
148
1268
  },
149
1269
  insertMany: (documents) => {
150
- const values = SQL.merge(documents.map((doc) => SQL`(${doc._id}, ${serializer.serialize(doc)}, ${doc._version ?? 1n})`), ",");
1270
+ const values = SQL.merge(documents.map((doc) => SQL`(${doc._id}, ${JSONParam.document(doc, serializer)}, ${doc._version ?? 1n})`), ",");
151
1271
  return SQL`
152
1272
  INSERT INTO ${SQL.identifier(collectionName)} (_id, data, _version) VALUES ${values}
153
1273
  ON CONFLICT(_id) DO NOTHING
@@ -157,7 +1277,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
157
1277
  const col = SQL.identifier(collectionName);
158
1278
  return SQL`
159
1279
  INSERT INTO ${col} (_id, data, _version)
160
- VALUES ${SQL.merge(documents.map((d) => SQL`(${d._id}::text, ${serializer.serialize(d)}::jsonb || jsonb_build_object('_id', ${d._id}::text) || jsonb_build_object('_version', '1'::text), 1::bigint)`), ",")}
1280
+ VALUES ${SQL.merge(documents.map((d) => SQL`(${d._id}::text, ${JSONParam.document(d, serializer)}::jsonb || jsonb_build_object('_id', ${d._id}::text) || jsonb_build_object('_version', '1'::text), 1::bigint)`), ",")}
161
1281
  ON CONFLICT(_id) DO UPDATE SET
162
1282
  data = EXCLUDED.data
163
1283
  || jsonb_build_object('_id', ${col}._id)
@@ -205,7 +1325,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
205
1325
  updated AS (
206
1326
  UPDATE ${SQL.identifier(collectionName)}
207
1327
  SET
208
- data = ${serializer.serialize(document)} || jsonb_build_object('_id', ${SQL.identifier(collectionName)}._id) || jsonb_build_object('_version', (_version + 1)::text),
1328
+ data = ${JSONParam.document(document, serializer)} || jsonb_build_object('_id', ${SQL.identifier(collectionName)}._id) || jsonb_build_object('_version', (_version + 1)::text),
209
1329
  _version = _version + 1
210
1330
  FROM existing
211
1331
  WHERE ${SQL.identifier(collectionName)}._id = existing._id ${expectedVersionUpdate}
@@ -262,7 +1382,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
262
1382
  WITH replacements(_id, data, expected_version) AS (
263
1383
  VALUES ${SQL.merge(documents.map((d) => {
264
1384
  const expectedVersion = d._version;
265
- return expectedVersion !== void 0 ? SQL`(${d._id}::text, ${serializer.serialize(d)}::jsonb, ${expectedVersion}::bigint)` : SQL`(${d._id}::text, ${serializer.serialize(d)}::jsonb, NULL::bigint)`;
1385
+ return expectedVersion !== void 0 ? SQL`(${d._id}::text, ${JSONParam.document(d, serializer)}::jsonb, ${expectedVersion}::bigint)` : SQL`(${d._id}::text, ${JSONParam.document(d, serializer)}::jsonb, NULL::bigint)`;
266
1386
  }), ",")}
267
1387
  )
268
1388
  UPDATE ${SQL.identifier(collectionName)} t
@@ -276,7 +1396,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
276
1396
  RETURNING t._id, t._version AS version;`;
277
1397
  return SQL`
278
1398
  WITH replacements(_id, data) AS (
279
- VALUES ${SQL.merge(documents.map((d) => SQL`(${d._id}::text, ${serializer.serialize(d)}::jsonb)`), ",")}
1399
+ VALUES ${SQL.merge(documents.map((d) => SQL`(${d._id}::text, ${JSONParam.document(d, serializer)}::jsonb)`), ",")}
280
1400
  )
281
1401
  UPDATE ${SQL.identifier(collectionName)} t
282
1402
  SET
@@ -329,9 +1449,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
329
1449
  query.push(where(filterQuery));
330
1450
  if (options?.sort && Object.keys(options.sort).length > 0) {
331
1451
  const clauses = Object.entries(options.sort).map(([field, dir]) => {
332
- const isMetadata = field === "_id" || field === "_version";
333
- const isNested = !isMetadata && field.includes(".");
334
- const accessor = isMetadata ? SQL`${SQL.plain(field)}` : isNested ? SQL`data #> '${SQL.plain(`{${field.split(".").join(",")}}`)}'` : SQL`data -> '${SQL.plain(field)}'`;
1452
+ const accessor = field === "_id" || field === "_version" ? SQL`${SQL.plain(field)}` : PostgreSQLJSON.field(SQL`data`, field);
335
1453
  return dir === 1 ? SQL`${accessor} ASC NULLS FIRST` : SQL`${accessor} DESC NULLS LAST`;
336
1454
  });
337
1455
  query.push(SQL`ORDER BY ${SQL.merge(clauses, ",")}`);