convex-ents 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/writer.js ADDED
@@ -0,0 +1,970 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/writer.ts
21
+ var writer_exports = {};
22
+ __export(writer_exports, {
23
+ WriterImplBase: () => WriterImplBase
24
+ });
25
+ module.exports = __toCommonJS(writer_exports);
26
+
27
+ // src/functions.ts
28
+ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
29
+ constructor(db, entDefinitions, table, retrieve) {
30
+ super(() => {
31
+ });
32
+ this.db = db;
33
+ this.entDefinitions = entDefinitions;
34
+ this.table = table;
35
+ this.retrieve = retrieve;
36
+ }
37
+ filter(predicate) {
38
+ return new _PromiseQueryOrNullImpl(
39
+ this.db,
40
+ this.entDefinitions,
41
+ this.table,
42
+ async () => {
43
+ const query = await this.retrieve();
44
+ if (query === null) {
45
+ return null;
46
+ }
47
+ return query.filter(predicate);
48
+ }
49
+ );
50
+ }
51
+ async map(callbackFn) {
52
+ const array = await this;
53
+ if (array === null) {
54
+ return [];
55
+ }
56
+ return await Promise.all(array.map(callbackFn));
57
+ }
58
+ order(order, indexName) {
59
+ return new _PromiseQueryOrNullImpl(
60
+ this.db,
61
+ this.entDefinitions,
62
+ this.table,
63
+ async () => {
64
+ const query = await this.retrieve();
65
+ if (query === null) {
66
+ return null;
67
+ }
68
+ if (indexName !== void 0) {
69
+ return query.withIndex(indexName).order(order);
70
+ }
71
+ return query.order(order);
72
+ }
73
+ );
74
+ }
75
+ // TODO: RLS for pagination
76
+ async paginate(paginationOpts) {
77
+ const query = await this.retrieve();
78
+ if (query === null) {
79
+ return null;
80
+ }
81
+ return await query.paginate(paginationOpts);
82
+ }
83
+ take(n) {
84
+ return new PromiseEntsOrNullImpl(
85
+ this.db,
86
+ this.entDefinitions,
87
+ this.table,
88
+ async () => {
89
+ return await this._take(n);
90
+ },
91
+ false
92
+ );
93
+ }
94
+ first() {
95
+ return new PromiseEntOrNullImpl(
96
+ this.db,
97
+ this.entDefinitions,
98
+ this.table,
99
+ async () => {
100
+ const docs = await this._take(1);
101
+ if (docs === null) {
102
+ return nullRetriever;
103
+ }
104
+ const [doc] = docs;
105
+ return loadedRetriever(doc);
106
+ },
107
+ false
108
+ );
109
+ }
110
+ firstX() {
111
+ return new PromiseEntWriterImpl(
112
+ this.db,
113
+ this.entDefinitions,
114
+ this.table,
115
+ async () => {
116
+ const docs = await this._take(1);
117
+ if (docs === null) {
118
+ return nullRetriever;
119
+ }
120
+ const [doc] = docs;
121
+ if (doc === void 0) {
122
+ throw new Error("Query returned no documents");
123
+ }
124
+ return loadedRetriever(doc);
125
+ },
126
+ false
127
+ );
128
+ }
129
+ unique() {
130
+ return new PromiseEntOrNullImpl(
131
+ this.db,
132
+ this.entDefinitions,
133
+ this.table,
134
+ async () => {
135
+ const docs = await this._take(2);
136
+ if (docs === null) {
137
+ return nullRetriever;
138
+ }
139
+ if (docs.length === 0) {
140
+ return nullRetriever;
141
+ }
142
+ if (docs.length === 2) {
143
+ throw new Error("unique() query returned more than one result");
144
+ }
145
+ const [doc] = docs;
146
+ return loadedRetriever(doc);
147
+ },
148
+ false
149
+ );
150
+ }
151
+ uniqueX() {
152
+ return new PromiseEntWriterImpl(
153
+ this.db,
154
+ this.entDefinitions,
155
+ this.table,
156
+ async () => {
157
+ const docs = await this._take(2);
158
+ if (docs === null) {
159
+ return nullRetriever;
160
+ }
161
+ if (docs.length === 0) {
162
+ throw new Error("Query returned no documents");
163
+ }
164
+ if (docs.length === 2) {
165
+ throw new Error("unique() query returned more than one result");
166
+ }
167
+ const [doc] = docs;
168
+ return loadedRetriever(doc);
169
+ },
170
+ true
171
+ );
172
+ }
173
+ async docs() {
174
+ const query = await this.retrieve();
175
+ if (query === null) {
176
+ return null;
177
+ }
178
+ const docs = await query.collect();
179
+ return filterByReadRule(
180
+ this.db,
181
+ this.entDefinitions,
182
+ this.table,
183
+ docs,
184
+ false
185
+ );
186
+ }
187
+ then(onfulfilled, onrejected) {
188
+ return this.docs().then(
189
+ (documents) => documents === null ? null : documents.map(
190
+ (doc) => entWrapper(doc, this.db, this.entDefinitions, this.table)
191
+ )
192
+ ).then(onfulfilled, onrejected);
193
+ }
194
+ async _take(n) {
195
+ const query = await this.retrieve();
196
+ if (query === null) {
197
+ return null;
198
+ }
199
+ const readPolicy = getReadRule(this.entDefinitions, this.table);
200
+ if (readPolicy === void 0) {
201
+ return await query.take(n);
202
+ }
203
+ let numItems = n;
204
+ const docs = [];
205
+ let hasMore = true;
206
+ const iterator = query[Symbol.asyncIterator]();
207
+ while (hasMore && docs.length < n) {
208
+ const page = [];
209
+ for (let i = 0; i < numItems; i++) {
210
+ const { done, value } = await iterator.next();
211
+ if (done) {
212
+ hasMore = false;
213
+ break;
214
+ }
215
+ page.push(value);
216
+ }
217
+ docs.push(
218
+ ...(await filterByReadRule(
219
+ this.db,
220
+ this.entDefinitions,
221
+ this.table,
222
+ page,
223
+ false
224
+ )).slice(0, n - docs.length)
225
+ );
226
+ numItems = Math.min(64, numItems * 2);
227
+ }
228
+ return docs;
229
+ }
230
+ };
231
+ var PromiseEntsOrNullImpl = class extends Promise {
232
+ constructor(db, entDefinitions, table, retrieve, throwIfNull) {
233
+ super(() => {
234
+ });
235
+ this.db = db;
236
+ this.entDefinitions = entDefinitions;
237
+ this.table = table;
238
+ this.retrieve = retrieve;
239
+ this.throwIfNull = throwIfNull;
240
+ }
241
+ async map(callbackFn) {
242
+ const array = await this;
243
+ if (array === null) {
244
+ return [];
245
+ }
246
+ return await Promise.all(array.map(callbackFn));
247
+ }
248
+ first() {
249
+ return new PromiseEntOrNullImpl(
250
+ this.db,
251
+ this.entDefinitions,
252
+ this.table,
253
+ async () => {
254
+ const docs = await this.retrieve();
255
+ if (docs === null) {
256
+ return nullRetriever;
257
+ }
258
+ return loadedRetriever(docs[0] ?? null);
259
+ },
260
+ false
261
+ );
262
+ }
263
+ firstX() {
264
+ return new PromiseEntOrNullImpl(
265
+ this.db,
266
+ this.entDefinitions,
267
+ this.table,
268
+ async () => {
269
+ const docs = await this.retrieve();
270
+ if (docs === null) {
271
+ return nullRetriever;
272
+ }
273
+ const doc = docs[0] ?? void 0;
274
+ if (doc === void 0) {
275
+ throw new Error("Query returned no documents");
276
+ }
277
+ return loadedRetriever(doc);
278
+ },
279
+ true
280
+ );
281
+ }
282
+ unique() {
283
+ return new PromiseEntOrNullImpl(
284
+ this.db,
285
+ this.entDefinitions,
286
+ this.table,
287
+ async () => {
288
+ const docs = await this.retrieve();
289
+ if (docs === null) {
290
+ return nullRetriever;
291
+ }
292
+ if (docs.length > 1) {
293
+ throw new Error("unique() query returned more than one result");
294
+ }
295
+ return loadedRetriever(docs[0] ?? null);
296
+ },
297
+ false
298
+ );
299
+ }
300
+ uniqueX() {
301
+ return new PromiseEntOrNullImpl(
302
+ this.db,
303
+ this.entDefinitions,
304
+ this.table,
305
+ async () => {
306
+ const docs = await this.retrieve();
307
+ if (docs === null) {
308
+ return nullRetriever;
309
+ }
310
+ if (docs.length > 1) {
311
+ throw new Error("unique() query returned more than one result");
312
+ }
313
+ if (docs.length < 1) {
314
+ throw new Error("unique() query returned no documents");
315
+ }
316
+ return loadedRetriever(docs[0]);
317
+ },
318
+ true
319
+ );
320
+ }
321
+ async docs() {
322
+ const docs = await this.retrieve();
323
+ return filterByReadRule(
324
+ this.db,
325
+ this.entDefinitions,
326
+ this.table,
327
+ docs,
328
+ this.throwIfNull
329
+ );
330
+ }
331
+ then(onfulfilled, onrejected) {
332
+ return this.docs().then(
333
+ (docs) => docs === null ? null : docs.map(
334
+ (doc) => entWrapper(doc, this.db, this.entDefinitions, this.table)
335
+ )
336
+ ).then(onfulfilled, onrejected);
337
+ }
338
+ };
339
+ var PromiseEdgeOrNullImpl = class extends PromiseEntsOrNullImpl {
340
+ constructor(db, entDefinitions, table, field, retrieveRange) {
341
+ super(db, entDefinitions, table, () => retrieveRange((q) => q), false);
342
+ this.field = field;
343
+ this.retrieveRange = retrieveRange;
344
+ }
345
+ async has(id) {
346
+ const docs = await this.retrieveRange((q) => q.eq(this.field, id));
347
+ return (docs?.length ?? 0) > 0;
348
+ }
349
+ };
350
+ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
351
+ constructor(db, entDefinitions, table, retrieve, throwIfNull) {
352
+ super(() => {
353
+ });
354
+ this.db = db;
355
+ this.entDefinitions = entDefinitions;
356
+ this.table = table;
357
+ this.retrieve = retrieve;
358
+ this.throwIfNull = throwIfNull;
359
+ }
360
+ async doc() {
361
+ const { id, doc: getDoc } = await this.retrieve();
362
+ if (id === null) {
363
+ return null;
364
+ }
365
+ const doc = await getDoc();
366
+ if (doc === null) {
367
+ return null;
368
+ }
369
+ const readPolicy = getReadRule(this.entDefinitions, this.table);
370
+ if (readPolicy !== void 0) {
371
+ const decision = await readPolicy(
372
+ entWrapper(doc, this.db, this.entDefinitions, this.table)
373
+ );
374
+ if (this.throwIfNull && !decision) {
375
+ throw new Error(
376
+ `Document cannot be read with id \`${doc._id}\` in table "${this.table}"`
377
+ );
378
+ }
379
+ return decision ? doc : null;
380
+ }
381
+ return doc;
382
+ }
383
+ then(onfulfilled, onrejected) {
384
+ return this.doc().then(
385
+ (doc) => doc === null ? null : entWrapper(doc, this.db, this.entDefinitions, this.table)
386
+ ).then(onfulfilled, onrejected);
387
+ }
388
+ edge(edge) {
389
+ return this.edgeImpl(edge);
390
+ }
391
+ edgeX(edge) {
392
+ return this.edgeImpl(edge, true);
393
+ }
394
+ edgeImpl(edge, throwIfNull = false) {
395
+ const edgeDefinition = this.entDefinitions[this.table].edges[edge];
396
+ if (edgeDefinition.cardinality === "multiple") {
397
+ if (edgeDefinition.type === "ref") {
398
+ return new PromiseEdgeOrNullImpl(
399
+ this.db,
400
+ this.entDefinitions,
401
+ edgeDefinition.to,
402
+ edgeDefinition.ref,
403
+ async (indexRange) => {
404
+ const { id } = await this.retrieve();
405
+ if (id === null) {
406
+ return null;
407
+ }
408
+ const edgeDocs = await this.db.query(edgeDefinition.table).withIndex(
409
+ edgeDefinition.field,
410
+ (q) => indexRange(q.eq(edgeDefinition.field, id))
411
+ ).collect();
412
+ return (await Promise.all(
413
+ edgeDocs.map(
414
+ (edgeDoc) => this.db.get(edgeDoc[edgeDefinition.ref])
415
+ )
416
+ )).filter((doc, i) => {
417
+ if (doc === null) {
418
+ throw new Error(
419
+ `Dangling reference for edge "${edgeDefinition.name}" in table "${this.table}" for document with ID "${id}": Could not find a document with ID "${edgeDocs[i][edgeDefinition.field]}" in table "${edgeDefinition.to}" (edge document ID is "${edgeDocs[i]._id}").`
420
+ );
421
+ }
422
+ return true;
423
+ });
424
+ }
425
+ );
426
+ }
427
+ return new PromiseQueryOrNullImpl(
428
+ this.db,
429
+ this.entDefinitions,
430
+ edgeDefinition.to,
431
+ async () => {
432
+ const { id } = await this.retrieve();
433
+ if (id === null) {
434
+ return null;
435
+ }
436
+ return this.db.query(edgeDefinition.to).withIndex(
437
+ edgeDefinition.ref,
438
+ (q) => q.eq(edgeDefinition.ref, id)
439
+ );
440
+ }
441
+ );
442
+ }
443
+ return new _PromiseEntOrNullImpl(
444
+ this.db,
445
+ this.entDefinitions,
446
+ edgeDefinition.to,
447
+ async () => {
448
+ const { id, doc: getDoc } = await this.retrieve();
449
+ if (id === null) {
450
+ return nullRetriever;
451
+ }
452
+ if (edgeDefinition.type === "ref") {
453
+ const otherDoc = await this.db.query(edgeDefinition.to).withIndex(
454
+ edgeDefinition.ref,
455
+ (q) => q.eq(edgeDefinition.ref, id)
456
+ ).unique();
457
+ if (throwIfNull && otherDoc === null) {
458
+ throw new Error(
459
+ `Edge "${edgeDefinition.name}" does not exist for document with ID "${id}"`
460
+ );
461
+ }
462
+ return loadedRetriever(otherDoc);
463
+ }
464
+ const doc = await getDoc();
465
+ const otherId = doc[edgeDefinition.field];
466
+ return {
467
+ id: otherId,
468
+ doc: async () => {
469
+ const otherDoc = await this.db.get(otherId);
470
+ if (otherDoc === null) {
471
+ throw new Error(
472
+ `Dangling reference for edge "${edgeDefinition.name}" in table "${this.table}" for document with ID "${id}": Could not find a document with ID "${otherId}" in table "${edgeDefinition.to}".`
473
+ );
474
+ }
475
+ return otherDoc;
476
+ }
477
+ };
478
+ },
479
+ throwIfNull
480
+ );
481
+ }
482
+ };
483
+ function entWrapper(fields, db, entDefinitions, table) {
484
+ const doc = { ...fields };
485
+ const queryInterface = new PromiseEntWriterImpl(
486
+ db,
487
+ entDefinitions,
488
+ table,
489
+ async () => ({ id: doc._id, doc: async () => doc }),
490
+ // this `true` doesn't matter, the queryInterface cannot be awaited
491
+ true
492
+ );
493
+ Object.defineProperty(doc, "edge", {
494
+ value: (edge) => {
495
+ return queryInterface.edge(edge);
496
+ },
497
+ enumerable: false,
498
+ writable: false,
499
+ configurable: false
500
+ });
501
+ Object.defineProperty(doc, "edgeX", {
502
+ value: (edge) => {
503
+ return queryInterface.edgeX(edge);
504
+ },
505
+ enumerable: false,
506
+ writable: false,
507
+ configurable: false
508
+ });
509
+ Object.defineProperty(doc, "patch", {
510
+ value: (value) => {
511
+ return queryInterface.patch(value);
512
+ },
513
+ enumerable: false,
514
+ writable: false,
515
+ configurable: false
516
+ });
517
+ Object.defineProperty(doc, "replace", {
518
+ value: (value) => {
519
+ return queryInterface.replace(value);
520
+ },
521
+ enumerable: false,
522
+ writable: false,
523
+ configurable: false
524
+ });
525
+ Object.defineProperty(doc, "delete", {
526
+ value: () => {
527
+ return queryInterface.delete();
528
+ },
529
+ enumerable: false,
530
+ writable: false,
531
+ configurable: false
532
+ });
533
+ Object.entries(entDefinitions[table].defaults).map(
534
+ ([field, value]) => {
535
+ if (doc[field] === void 0) {
536
+ doc[field] = value;
537
+ }
538
+ }
539
+ );
540
+ return doc;
541
+ }
542
+ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
543
+ constructor(db, entDefinitions, table, retrieve, throwIfNull) {
544
+ super(db, entDefinitions, table, retrieve, throwIfNull);
545
+ this.db = db;
546
+ this.entDefinitions = entDefinitions;
547
+ this.table = table;
548
+ this.retrieve = retrieve;
549
+ this.throwIfNull = throwIfNull;
550
+ this.base = new WriterImplBase(db, entDefinitions, table);
551
+ }
552
+ base;
553
+ patch(value) {
554
+ return new PromiseEntIdImpl(
555
+ this.db,
556
+ this.entDefinitions,
557
+ this.table,
558
+ async () => {
559
+ const { id: docId } = await this.retrieve();
560
+ const id = docId;
561
+ await this.base.checkReadAndWriteRule("update", id, value);
562
+ await this.base.checkUniqueness(value, id);
563
+ const fields = this.base.fieldsOnly(value);
564
+ await this.db.patch(id, fields);
565
+ const edges = {};
566
+ await Promise.all(
567
+ Object.keys(value).map(async (key) => {
568
+ const edgeDefinition = this.entDefinitions[this.table].edges[key];
569
+ if (edgeDefinition === void 0 || edgeDefinition.cardinality === "single" && edgeDefinition.type === "field") {
570
+ return;
571
+ }
572
+ if (edgeDefinition.cardinality === "single") {
573
+ throw new Error("Cannot set 1:1 edge from optional end.");
574
+ } else {
575
+ edges[key] = value[key];
576
+ }
577
+ })
578
+ );
579
+ await this.base.writeEdges(id, edges);
580
+ return id;
581
+ }
582
+ );
583
+ }
584
+ replace(value) {
585
+ return new PromiseEntIdImpl(
586
+ this.db,
587
+ this.entDefinitions,
588
+ this.table,
589
+ async () => {
590
+ const { id } = await this.retrieve();
591
+ const docId = id;
592
+ await this.base.checkReadAndWriteRule("update", docId, value);
593
+ await this.base.checkUniqueness(value, docId);
594
+ const fields = this.base.fieldsOnly(value);
595
+ await this.db.replace(docId, fields);
596
+ const edges = {};
597
+ await Promise.all(
598
+ Object.values(
599
+ this.entDefinitions[this.table].edges
600
+ ).map(async (edgeDefinition) => {
601
+ const key = edgeDefinition.name;
602
+ const idOrIds = value[key];
603
+ if (edgeDefinition.cardinality === "single") {
604
+ if (edgeDefinition.type === "ref") {
605
+ const oldDoc = await this.db.get(docId);
606
+ if (oldDoc[key] !== void 0 && oldDoc[key] !== idOrIds) {
607
+ throw new Error("Cannot set 1:1 edge from optional end.");
608
+ }
609
+ }
610
+ } else {
611
+ if (edgeDefinition.type === "field") {
612
+ const existing = (await this.db.query(edgeDefinition.to).withIndex(
613
+ edgeDefinition.ref,
614
+ (q) => q.eq(edgeDefinition.ref, docId)
615
+ ).collect()).map((doc) => doc._id);
616
+ edges[key] = {
617
+ add: idOrIds,
618
+ remove: existing
619
+ };
620
+ } else {
621
+ const requested = new Set(idOrIds ?? []);
622
+ const remove = (await this.db.query(edgeDefinition.table).withIndex(
623
+ edgeDefinition.field,
624
+ (q) => q.eq(edgeDefinition.field, docId)
625
+ ).collect()).map((doc) => [doc._id, doc[edgeDefinition.ref]]).concat(
626
+ edgeDefinition.symmetric ? (await this.db.query(edgeDefinition.table).withIndex(
627
+ edgeDefinition.ref,
628
+ (q) => q.eq(edgeDefinition.ref, docId)
629
+ ).collect()).map(
630
+ (doc) => [doc._id, doc[edgeDefinition.field]]
631
+ ) : []
632
+ ).filter(([_edgeId, otherId]) => {
633
+ if (requested.has(otherId)) {
634
+ requested.delete(otherId);
635
+ return false;
636
+ }
637
+ return true;
638
+ }).map(([edgeId]) => edgeId);
639
+ edges[key] = {
640
+ add: Array.from(requested),
641
+ removeEdges: remove
642
+ };
643
+ }
644
+ }
645
+ })
646
+ );
647
+ await this.base.writeEdges(docId, edges);
648
+ return docId;
649
+ }
650
+ );
651
+ }
652
+ async delete() {
653
+ const { id: docId } = await this.retrieve();
654
+ const id = docId;
655
+ await this.base.checkReadAndWriteRule("delete", id, void 0);
656
+ let memoized = void 0;
657
+ const oldDoc = async () => {
658
+ if (memoized !== void 0) {
659
+ return memoized;
660
+ }
661
+ return memoized = await this.db.get(id);
662
+ };
663
+ const edges = {};
664
+ await Promise.all(
665
+ Object.values(
666
+ this.entDefinitions[this.table].edges
667
+ ).map(async (edgeDefinition) => {
668
+ const key = edgeDefinition.name;
669
+ if (edgeDefinition.cardinality === "single") {
670
+ if (edgeDefinition.type === "ref") {
671
+ edges[key] = {
672
+ remove: (await oldDoc())[key]
673
+ };
674
+ }
675
+ } else {
676
+ if (edgeDefinition.type === "field") {
677
+ const existing = (await this.db.query(edgeDefinition.to).withIndex(
678
+ edgeDefinition.ref,
679
+ (q) => q.eq(edgeDefinition.ref, id)
680
+ ).collect()).map((doc) => doc._id);
681
+ edges[key] = { remove: existing };
682
+ } else {
683
+ const existing = (await this.db.query(edgeDefinition.table).withIndex(
684
+ edgeDefinition.field,
685
+ (q) => q.eq(edgeDefinition.field, id)
686
+ ).collect()).concat(
687
+ edgeDefinition.symmetric ? await this.db.query(edgeDefinition.table).withIndex(
688
+ edgeDefinition.ref,
689
+ (q) => q.eq(edgeDefinition.ref, id)
690
+ ).collect() : []
691
+ ).map((doc) => doc._id);
692
+ edges[key] = { removeEdges: existing };
693
+ }
694
+ }
695
+ })
696
+ );
697
+ await this.db.delete(id);
698
+ await this.base.writeEdges(id, edges);
699
+ return id;
700
+ }
701
+ };
702
+ var PromiseEntIdImpl = class extends Promise {
703
+ constructor(db, entDefinitions, table, retrieve) {
704
+ super(() => {
705
+ });
706
+ this.db = db;
707
+ this.entDefinitions = entDefinitions;
708
+ this.table = table;
709
+ this.retrieve = retrieve;
710
+ }
711
+ get() {
712
+ return new PromiseEntOrNullImpl(
713
+ this.db,
714
+ this.entDefinitions,
715
+ this.table,
716
+ async () => {
717
+ const id = await this.retrieve();
718
+ return { id, doc: async () => this.db.get(id) };
719
+ },
720
+ true
721
+ );
722
+ }
723
+ then(onfulfilled, onrejected) {
724
+ return this.retrieve().then(onfulfilled, onrejected);
725
+ }
726
+ };
727
+ var nullRetriever = {
728
+ id: null,
729
+ doc: async () => null
730
+ };
731
+ function loadedRetriever(doc) {
732
+ return {
733
+ id: doc?._id ?? null,
734
+ doc: async () => doc
735
+ };
736
+ }
737
+ async function filterByReadRule(db, entDefinitions, table, docs, throwIfNull) {
738
+ if (docs === null) {
739
+ return null;
740
+ }
741
+ const readPolicy = getReadRule(entDefinitions, table);
742
+ if (readPolicy !== void 0) {
743
+ const decisions = await Promise.all(
744
+ docs.map(async (doc) => {
745
+ const decision = await readPolicy(
746
+ entWrapper(doc, db, entDefinitions, table)
747
+ );
748
+ if (throwIfNull && !decision) {
749
+ throw new Error(
750
+ `Document cannot be read with id \`${doc._id}\` in table "${table}"`
751
+ );
752
+ }
753
+ return decision;
754
+ })
755
+ );
756
+ return docs.filter((_, i) => decisions[i]);
757
+ }
758
+ return docs;
759
+ }
760
+ function getReadRule(entDefinitions, table) {
761
+ return entDefinitions.rules?.[table]?.read;
762
+ }
763
+ function getWriteRule(entDefinitions, table) {
764
+ return entDefinitions.rules?.[table]?.write;
765
+ }
766
+
767
+ // src/writer.ts
768
+ var WriterImplBase = class {
769
+ constructor(db, entDefinitions, table) {
770
+ this.db = db;
771
+ this.entDefinitions = entDefinitions;
772
+ this.table = table;
773
+ }
774
+ async writeEdges(docId, changes) {
775
+ await Promise.all(
776
+ Object.values(
777
+ this.entDefinitions[this.table].edges
778
+ ).map(async (edgeDefinition) => {
779
+ const idOrIds = changes[edgeDefinition.name];
780
+ if (idOrIds === void 0) {
781
+ return;
782
+ }
783
+ if (edgeDefinition.cardinality === "single") {
784
+ if (edgeDefinition.type === "ref") {
785
+ if (idOrIds.remove !== void 0) {
786
+ await this.db.delete(idOrIds.remove);
787
+ }
788
+ if (idOrIds.add !== void 0) {
789
+ await this.db.patch(
790
+ idOrIds.add,
791
+ { [edgeDefinition.ref]: docId }
792
+ );
793
+ }
794
+ }
795
+ } else {
796
+ if (edgeDefinition.type === "field") {
797
+ if (idOrIds.remove !== void 0) {
798
+ await Promise.all(
799
+ idOrIds.remove.map(
800
+ (id) => this.db.delete(id)
801
+ )
802
+ );
803
+ }
804
+ if (idOrIds.add !== void 0) {
805
+ await Promise.all(
806
+ idOrIds.add.map(
807
+ async (id) => this.db.patch(id, {
808
+ [edgeDefinition.ref]: docId
809
+ })
810
+ )
811
+ );
812
+ }
813
+ } else {
814
+ let removeEdges = [];
815
+ if (idOrIds.remove !== void 0) {
816
+ removeEdges = (await Promise.all(
817
+ idOrIds.remove.map(
818
+ async (id) => (await this.db.query(edgeDefinition.table).withIndex(
819
+ edgeDefinition.field,
820
+ (q) => q.eq(edgeDefinition.field, docId).eq(
821
+ edgeDefinition.ref,
822
+ id
823
+ )
824
+ ).collect()).concat(
825
+ edgeDefinition.symmetric ? await this.db.query(edgeDefinition.table).withIndex(
826
+ edgeDefinition.ref,
827
+ (q) => q.eq(edgeDefinition.ref, docId).eq(edgeDefinition.field, id)
828
+ ).collect() : []
829
+ )
830
+ )
831
+ )).map((doc) => doc._id);
832
+ }
833
+ if (idOrIds.removeEdges !== void 0) {
834
+ removeEdges = idOrIds.removeEdges;
835
+ }
836
+ if (removeEdges.length > 0) {
837
+ await Promise.all(
838
+ removeEdges.map(async (id) => {
839
+ try {
840
+ await this.db.delete(id);
841
+ } catch (e) {
842
+ }
843
+ })
844
+ );
845
+ }
846
+ if (idOrIds.add !== void 0) {
847
+ await Promise.all(
848
+ idOrIds.add.map(async (id) => {
849
+ await this.db.insert(edgeDefinition.table, {
850
+ [edgeDefinition.field]: docId,
851
+ [edgeDefinition.ref]: id
852
+ });
853
+ if (edgeDefinition.symmetric) {
854
+ await this.db.insert(edgeDefinition.table, {
855
+ [edgeDefinition.field]: id,
856
+ [edgeDefinition.ref]: docId
857
+ });
858
+ }
859
+ })
860
+ );
861
+ }
862
+ }
863
+ }
864
+ })
865
+ );
866
+ }
867
+ async checkUniqueness(value, id) {
868
+ await Promise.all(
869
+ Object.values(
870
+ this.entDefinitions[this.table].fields
871
+ ).map(async (fieldDefinition) => {
872
+ if (fieldDefinition.unique) {
873
+ const key = fieldDefinition.name;
874
+ const fieldValue = value[key];
875
+ const existing = await this.db.query(this.table).withIndex(key, (q) => q.eq(key, value[key])).unique();
876
+ if (existing !== null && (id === void 0 || existing._id !== id)) {
877
+ throw new Error(
878
+ `In table "${this.table}" cannot create a duplicate document with field "${key}" of value \`${fieldValue}\`, existing document with ID "${existing._id}" already has it.`
879
+ );
880
+ }
881
+ }
882
+ })
883
+ );
884
+ await Promise.all(
885
+ Object.values(
886
+ this.entDefinitions[this.table].edges
887
+ ).map(async (edgeDefinition) => {
888
+ if (edgeDefinition.cardinality === "single" && edgeDefinition.type === "field" && edgeDefinition.unique) {
889
+ const key = edgeDefinition.field;
890
+ if (value[key] === void 0) {
891
+ return;
892
+ }
893
+ const existing = await this.db.query(this.table).withIndex(key, (q) => q.eq(key, value[key])).unique();
894
+ if (existing !== null && (id === void 0 || existing._id !== id)) {
895
+ throw new Error(
896
+ `In table "${this.table}" cannot create a duplicate 1:1 edge "${edgeDefinition.name}" to ID "${value[key]}", existing document with ID "${existing._id}" already has it.`
897
+ );
898
+ }
899
+ }
900
+ })
901
+ );
902
+ }
903
+ fieldsOnly(value) {
904
+ const fields = {};
905
+ Object.keys(value).forEach((key) => {
906
+ const edgeDefinition = this.entDefinitions[this.table].edges[key];
907
+ if (edgeDefinition === void 0) {
908
+ fields[key] = value[key];
909
+ }
910
+ });
911
+ return fields;
912
+ }
913
+ async checkReadAndWriteRule(operation, id, value) {
914
+ if (id !== void 0) {
915
+ const readPolicy = getReadRule(this.entDefinitions, this.table);
916
+ if (readPolicy !== void 0) {
917
+ const doc = await this.db.get(id);
918
+ if (doc === null) {
919
+ throw new Error(
920
+ `Cannot update document with ID "${id}" in table "${this.table} because it does not exist"`
921
+ );
922
+ }
923
+ const decision2 = await readPolicy(doc);
924
+ if (!decision2) {
925
+ throw new Error(
926
+ `Cannot update document with ID "${id}" from table "${this.table}"`
927
+ );
928
+ }
929
+ }
930
+ }
931
+ const writePolicy = getWriteRule(this.entDefinitions, this.table);
932
+ if (writePolicy === void 0) {
933
+ return;
934
+ }
935
+ const ent = id === void 0 ? void 0 : entWrapper(
936
+ await this.db.get(id),
937
+ this.db,
938
+ this.entDefinitions,
939
+ this.table
940
+ );
941
+ const { _id, _creationTime, ...safeValue } = value ?? {};
942
+ const decision = await writePolicy({
943
+ operation,
944
+ ent,
945
+ value: value !== void 0 ? safeValue : void 0
946
+ });
947
+ if (!decision) {
948
+ if (id === void 0) {
949
+ throw new Error(
950
+ `Cannot insert into table "${this.table}": \`${JSON.stringify(
951
+ value
952
+ )}\``
953
+ );
954
+ } else if (value === void 0) {
955
+ throw new Error(
956
+ `Cannot delete from table "${this.table}" with ID "${id}"`
957
+ );
958
+ } else {
959
+ throw new Error(
960
+ `Cannot update document with ID "${id}" in table "${this.table}" with: \`${JSON.stringify(value)}\``
961
+ );
962
+ }
963
+ }
964
+ }
965
+ };
966
+ // Annotate the CommonJS export names for ESM import in node:
967
+ 0 && (module.exports = {
968
+ WriterImplBase
969
+ });
970
+ //# sourceMappingURL=writer.js.map