convex-ents 0.2.0 → 0.3.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 CHANGED
@@ -23,20 +23,21 @@ __export(writer_exports, {
23
23
  WriterImplBase: () => WriterImplBase
24
24
  });
25
25
  module.exports = __toCommonJS(writer_exports);
26
+ var import_server = require("convex/server");
26
27
 
27
28
  // src/functions.ts
28
29
  var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
29
- constructor(db, entDefinitions, table, retrieve) {
30
+ constructor(ctx, entDefinitions, table, retrieve) {
30
31
  super(() => {
31
32
  });
32
- this.db = db;
33
+ this.ctx = ctx;
33
34
  this.entDefinitions = entDefinitions;
34
35
  this.table = table;
35
36
  this.retrieve = retrieve;
36
37
  }
37
38
  filter(predicate) {
38
39
  return new _PromiseQueryOrNullImpl(
39
- this.db,
40
+ this.ctx,
40
41
  this.entDefinitions,
41
42
  this.table,
42
43
  async () => {
@@ -48,16 +49,18 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
48
49
  }
49
50
  );
50
51
  }
51
- async map(callbackFn) {
52
- const array = await this;
53
- if (array === null) {
54
- return [];
55
- }
56
- return await Promise.all(array.map(callbackFn));
52
+ map(callbackFn) {
53
+ return new PromiseArrayImpl(async () => {
54
+ const array = await this;
55
+ if (array === null) {
56
+ return null;
57
+ }
58
+ return await Promise.all(array.map(callbackFn));
59
+ });
57
60
  }
58
61
  order(order, indexName) {
59
62
  return new _PromiseQueryOrNullImpl(
60
- this.db,
63
+ this.ctx,
61
64
  this.entDefinitions,
62
65
  this.table,
63
66
  async () => {
@@ -74,7 +77,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
74
77
  }
75
78
  paginate(paginationOpts) {
76
79
  return new PromisePaginationResultOrNullImpl(
77
- this.db,
80
+ this.ctx,
78
81
  this.entDefinitions,
79
82
  this.table,
80
83
  this.retrieve,
@@ -83,7 +86,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
83
86
  }
84
87
  take(n) {
85
88
  return new PromiseEntsOrNullImpl(
86
- this.db,
89
+ this.ctx,
87
90
  this.entDefinitions,
88
91
  this.table,
89
92
  async () => {
@@ -94,7 +97,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
94
97
  }
95
98
  first() {
96
99
  return new PromiseEntOrNullImpl(
97
- this.db,
100
+ this.ctx,
98
101
  this.entDefinitions,
99
102
  this.table,
100
103
  async () => {
@@ -110,7 +113,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
110
113
  }
111
114
  firstX() {
112
115
  return new PromiseEntWriterImpl(
113
- this.db,
116
+ this.ctx,
114
117
  this.entDefinitions,
115
118
  this.table,
116
119
  async () => {
@@ -129,7 +132,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
129
132
  }
130
133
  unique() {
131
134
  return new PromiseEntOrNullImpl(
132
- this.db,
135
+ this.ctx,
133
136
  this.entDefinitions,
134
137
  this.table,
135
138
  async () => {
@@ -151,7 +154,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
151
154
  }
152
155
  uniqueX() {
153
156
  return new PromiseEntWriterImpl(
154
- this.db,
157
+ this.ctx,
155
158
  this.entDefinitions,
156
159
  this.table,
157
160
  async () => {
@@ -178,7 +181,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
178
181
  }
179
182
  const docs = await query.collect();
180
183
  return filterByReadRule(
181
- this.db,
184
+ this.ctx,
182
185
  this.entDefinitions,
183
186
  this.table,
184
187
  docs,
@@ -188,7 +191,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
188
191
  then(onfulfilled, onrejected) {
189
192
  return this.docs().then(
190
193
  (documents) => documents === null ? null : documents.map(
191
- (doc) => entWrapper(doc, this.db, this.entDefinitions, this.table)
194
+ (doc) => entWrapper(doc, this.ctx, this.entDefinitions, this.table)
192
195
  )
193
196
  ).then(onfulfilled, onrejected);
194
197
  }
@@ -217,7 +220,7 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
217
220
  }
218
221
  docs.push(
219
222
  ...(await filterByReadRule(
220
- this.db,
223
+ this.ctx,
221
224
  this.entDefinitions,
222
225
  this.table,
223
226
  page,
@@ -230,10 +233,10 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
230
233
  }
231
234
  };
232
235
  var PromisePaginationResultOrNullImpl = class extends Promise {
233
- constructor(db, entDefinitions, table, retrieve, paginationOpts) {
236
+ constructor(ctx, entDefinitions, table, retrieve, paginationOpts) {
234
237
  super(() => {
235
238
  });
236
- this.db = db;
239
+ this.ctx = ctx;
237
240
  this.entDefinitions = entDefinitions;
238
241
  this.table = table;
239
242
  this.retrieve = retrieve;
@@ -258,7 +261,7 @@ var PromisePaginationResultOrNullImpl = class extends Promise {
258
261
  return {
259
262
  ...result,
260
263
  page: await filterByReadRule(
261
- this.db,
264
+ this.ctx,
262
265
  this.entDefinitions,
263
266
  this.table,
264
267
  result.page,
@@ -271,32 +274,34 @@ var PromisePaginationResultOrNullImpl = class extends Promise {
271
274
  (result) => result === null ? null : {
272
275
  ...result,
273
276
  page: result.page.map(
274
- (doc) => entWrapper(doc, this.db, this.entDefinitions, this.table)
277
+ (doc) => entWrapper(doc, this.ctx, this.entDefinitions, this.table)
275
278
  )
276
279
  }
277
280
  ).then(onfulfilled, onrejected);
278
281
  }
279
282
  };
280
283
  var PromiseEntsOrNullImpl = class extends Promise {
281
- constructor(db, entDefinitions, table, retrieve, throwIfNull) {
284
+ constructor(ctx, entDefinitions, table, retrieve, throwIfNull) {
282
285
  super(() => {
283
286
  });
284
- this.db = db;
287
+ this.ctx = ctx;
285
288
  this.entDefinitions = entDefinitions;
286
289
  this.table = table;
287
290
  this.retrieve = retrieve;
288
291
  this.throwIfNull = throwIfNull;
289
292
  }
290
- async map(callbackFn) {
291
- const array = await this;
292
- if (array === null) {
293
- return [];
294
- }
295
- return await Promise.all(array.map(callbackFn));
293
+ map(callbackFn) {
294
+ return new PromiseArrayImpl(async () => {
295
+ const array = await this;
296
+ if (array === null) {
297
+ return null;
298
+ }
299
+ return await Promise.all(array.map(callbackFn));
300
+ });
296
301
  }
297
302
  first() {
298
303
  return new PromiseEntOrNullImpl(
299
- this.db,
304
+ this.ctx,
300
305
  this.entDefinitions,
301
306
  this.table,
302
307
  async () => {
@@ -311,7 +316,7 @@ var PromiseEntsOrNullImpl = class extends Promise {
311
316
  }
312
317
  firstX() {
313
318
  return new PromiseEntOrNullImpl(
314
- this.db,
319
+ this.ctx,
315
320
  this.entDefinitions,
316
321
  this.table,
317
322
  async () => {
@@ -330,7 +335,7 @@ var PromiseEntsOrNullImpl = class extends Promise {
330
335
  }
331
336
  unique() {
332
337
  return new PromiseEntOrNullImpl(
333
- this.db,
338
+ this.ctx,
334
339
  this.entDefinitions,
335
340
  this.table,
336
341
  async () => {
@@ -348,7 +353,7 @@ var PromiseEntsOrNullImpl = class extends Promise {
348
353
  }
349
354
  uniqueX() {
350
355
  return new PromiseEntOrNullImpl(
351
- this.db,
356
+ this.ctx,
352
357
  this.entDefinitions,
353
358
  this.table,
354
359
  async () => {
@@ -370,7 +375,7 @@ var PromiseEntsOrNullImpl = class extends Promise {
370
375
  async docs() {
371
376
  const docs = await this.retrieve();
372
377
  return filterByReadRule(
373
- this.db,
378
+ this.ctx,
374
379
  this.entDefinitions,
375
380
  this.table,
376
381
  docs,
@@ -380,14 +385,14 @@ var PromiseEntsOrNullImpl = class extends Promise {
380
385
  then(onfulfilled, onrejected) {
381
386
  return this.docs().then(
382
387
  (docs) => docs === null ? null : docs.map(
383
- (doc) => entWrapper(doc, this.db, this.entDefinitions, this.table)
388
+ (doc) => entWrapper(doc, this.ctx, this.entDefinitions, this.table)
384
389
  )
385
390
  ).then(onfulfilled, onrejected);
386
391
  }
387
392
  };
388
393
  var PromiseEdgeOrNullImpl = class extends PromiseEntsOrNullImpl {
389
- constructor(db, entDefinitions, table, field, retrieveRange) {
390
- super(db, entDefinitions, table, () => retrieveRange((q) => q), false);
394
+ constructor(ctx, entDefinitions, table, field, retrieveRange) {
395
+ super(ctx, entDefinitions, table, () => retrieveRange((q) => q), false);
391
396
  this.field = field;
392
397
  this.retrieveRange = retrieveRange;
393
398
  }
@@ -397,10 +402,10 @@ var PromiseEdgeOrNullImpl = class extends PromiseEntsOrNullImpl {
397
402
  }
398
403
  };
399
404
  var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
400
- constructor(db, entDefinitions, table, retrieve, throwIfNull) {
405
+ constructor(ctx, entDefinitions, table, retrieve, throwIfNull) {
401
406
  super(() => {
402
407
  });
403
- this.db = db;
408
+ this.ctx = ctx;
404
409
  this.entDefinitions = entDefinitions;
405
410
  this.table = table;
406
411
  this.retrieve = retrieve;
@@ -418,7 +423,7 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
418
423
  const readPolicy = getReadRule(this.entDefinitions, this.table);
419
424
  if (readPolicy !== void 0) {
420
425
  const decision = await readPolicy(
421
- entWrapper(doc, this.db, this.entDefinitions, this.table)
426
+ entWrapper(doc, this.ctx, this.entDefinitions, this.table)
422
427
  );
423
428
  if (this.throwIfNull && !decision) {
424
429
  throw new Error(
@@ -431,7 +436,7 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
431
436
  }
432
437
  then(onfulfilled, onrejected) {
433
438
  return this.doc().then(
434
- (doc) => doc === null ? null : entWrapper(doc, this.db, this.entDefinitions, this.table)
439
+ (doc) => doc === null ? null : entWrapper(doc, this.ctx, this.entDefinitions, this.table)
435
440
  ).then(onfulfilled, onrejected);
436
441
  }
437
442
  edge(edge) {
@@ -441,11 +446,11 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
441
446
  return this.edgeImpl(edge, true);
442
447
  }
443
448
  edgeImpl(edge, throwIfNull = false) {
444
- const edgeDefinition = this.entDefinitions[this.table].edges[edge];
449
+ const edgeDefinition = getEdgeDefinitions(this.entDefinitions, this.table)[edge];
445
450
  if (edgeDefinition.cardinality === "multiple") {
446
451
  if (edgeDefinition.type === "ref") {
447
452
  return new PromiseEdgeOrNullImpl(
448
- this.db,
453
+ this.ctx,
449
454
  this.entDefinitions,
450
455
  edgeDefinition.to,
451
456
  edgeDefinition.ref,
@@ -454,13 +459,13 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
454
459
  if (id === null) {
455
460
  return null;
456
461
  }
457
- const edgeDocs = await this.db.query(edgeDefinition.table).withIndex(
462
+ const edgeDocs = await this.ctx.db.query(edgeDefinition.table).withIndex(
458
463
  edgeDefinition.field,
459
464
  (q) => indexRange(q.eq(edgeDefinition.field, id))
460
465
  ).collect();
461
466
  return (await Promise.all(
462
467
  edgeDocs.map(
463
- (edgeDoc) => this.db.get(edgeDoc[edgeDefinition.ref])
468
+ (edgeDoc) => this.ctx.db.get(edgeDoc[edgeDefinition.ref])
464
469
  )
465
470
  )).filter((doc, i) => {
466
471
  if (doc === null) {
@@ -474,7 +479,7 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
474
479
  );
475
480
  }
476
481
  return new PromiseQueryOrNullImpl(
477
- this.db,
482
+ this.ctx,
478
483
  this.entDefinitions,
479
484
  edgeDefinition.to,
480
485
  async () => {
@@ -482,7 +487,7 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
482
487
  if (id === null) {
483
488
  return null;
484
489
  }
485
- return this.db.query(edgeDefinition.to).withIndex(
490
+ return this.ctx.db.query(edgeDefinition.to).withIndex(
486
491
  edgeDefinition.ref,
487
492
  (q) => q.eq(edgeDefinition.ref, id)
488
493
  );
@@ -490,7 +495,7 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
490
495
  );
491
496
  }
492
497
  return new _PromiseEntOrNullImpl(
493
- this.db,
498
+ this.ctx,
494
499
  this.entDefinitions,
495
500
  edgeDefinition.to,
496
501
  async () => {
@@ -499,7 +504,7 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
499
504
  return nullRetriever;
500
505
  }
501
506
  if (edgeDefinition.type === "ref") {
502
- const otherDoc = await this.db.query(edgeDefinition.to).withIndex(
507
+ const otherDoc = await this.ctx.db.query(edgeDefinition.to).withIndex(
503
508
  edgeDefinition.ref,
504
509
  (q) => q.eq(edgeDefinition.ref, id)
505
510
  ).unique();
@@ -515,7 +520,7 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
515
520
  return {
516
521
  id: otherId,
517
522
  doc: async () => {
518
- const otherDoc = await this.db.get(otherId);
523
+ const otherDoc = await this.ctx.db.get(otherId);
519
524
  if (otherDoc === null) {
520
525
  throw new Error(
521
526
  `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}".`
@@ -529,10 +534,27 @@ var PromiseEntOrNullImpl = class _PromiseEntOrNullImpl extends Promise {
529
534
  );
530
535
  }
531
536
  };
532
- function entWrapper(fields, db, entDefinitions, table) {
537
+ var PromiseArrayImpl = class extends Promise {
538
+ constructor(retrieve) {
539
+ super(() => {
540
+ });
541
+ this.retrieve = retrieve;
542
+ }
543
+ async filter(predicate) {
544
+ const array = await this.retrieve();
545
+ if (array === null) {
546
+ return null;
547
+ }
548
+ return array.filter(predicate);
549
+ }
550
+ then(onfulfilled, onrejected) {
551
+ return this.retrieve().then(onfulfilled, onrejected);
552
+ }
553
+ };
554
+ function entWrapper(fields, ctx, entDefinitions, table) {
533
555
  const doc = { ...fields };
534
556
  const queryInterface = new PromiseEntWriterImpl(
535
- db,
557
+ ctx,
536
558
  entDefinitions,
537
559
  table,
538
560
  async () => ({ id: doc._id, doc: async () => doc }),
@@ -555,6 +577,14 @@ function entWrapper(fields, db, entDefinitions, table) {
555
577
  writable: false,
556
578
  configurable: false
557
579
  });
580
+ Object.defineProperty(doc, "doc", {
581
+ value: () => {
582
+ return doc;
583
+ },
584
+ enumerable: false,
585
+ writable: false,
586
+ configurable: false
587
+ });
558
588
  Object.defineProperty(doc, "patch", {
559
589
  value: (value) => {
560
590
  return queryInterface.patch(value);
@@ -589,19 +619,19 @@ function entWrapper(fields, db, entDefinitions, table) {
589
619
  return doc;
590
620
  }
591
621
  var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
592
- constructor(db, entDefinitions, table, retrieve, throwIfNull) {
593
- super(db, entDefinitions, table, retrieve, throwIfNull);
594
- this.db = db;
622
+ constructor(ctx, entDefinitions, table, retrieve, throwIfNull) {
623
+ super(ctx, entDefinitions, table, retrieve, throwIfNull);
624
+ this.ctx = ctx;
595
625
  this.entDefinitions = entDefinitions;
596
626
  this.table = table;
597
627
  this.retrieve = retrieve;
598
628
  this.throwIfNull = throwIfNull;
599
- this.base = new WriterImplBase(db, entDefinitions, table);
629
+ this.base = new WriterImplBase(ctx, entDefinitions, table);
600
630
  }
601
631
  base;
602
632
  patch(value) {
603
633
  return new PromiseEntIdImpl(
604
- this.db,
634
+ this.ctx,
605
635
  this.entDefinitions,
606
636
  this.table,
607
637
  async () => {
@@ -610,18 +640,52 @@ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
610
640
  await this.base.checkReadAndWriteRule("update", id, value);
611
641
  await this.base.checkUniqueness(value, id);
612
642
  const fields = this.base.fieldsOnly(value);
613
- await this.db.patch(id, fields);
643
+ await this.ctx.db.patch(id, fields);
614
644
  const edges = {};
615
645
  await Promise.all(
616
646
  Object.keys(value).map(async (key) => {
617
- const edgeDefinition = this.entDefinitions[this.table].edges[key];
647
+ const edgeDefinition = getEdgeDefinitions(
648
+ this.entDefinitions,
649
+ this.table
650
+ )[key];
618
651
  if (edgeDefinition === void 0 || edgeDefinition.cardinality === "single" && edgeDefinition.type === "field") {
619
652
  return;
620
653
  }
621
654
  if (edgeDefinition.cardinality === "single") {
622
- throw new Error("Cannot set 1:1 edge from optional end.");
655
+ throw new Error(
656
+ `Cannot set 1:1 edge "${edgeDefinition.name}" on ent in table "${this.table}", update the ent in "${edgeDefinition.to}" table instead.`
657
+ );
623
658
  } else {
624
- edges[key] = value[key];
659
+ if (edgeDefinition.type === "field") {
660
+ throw new Error(
661
+ `Cannot set 1:many edges "${edgeDefinition.name}" on ent in table "${this.table}", update the ents in "${edgeDefinition.to}" table instead.`
662
+ );
663
+ } else {
664
+ const { add, remove } = value[key];
665
+ const removeEdges = (await Promise.all(
666
+ (remove ?? []).map(
667
+ async (edgeId) => (await this.ctx.db.query(edgeDefinition.table).withIndex(
668
+ edgeDefinition.field,
669
+ (q) => q.eq(edgeDefinition.field, id).eq(
670
+ edgeDefinition.ref,
671
+ edgeId
672
+ )
673
+ ).collect()).concat(
674
+ edgeDefinition.symmetric ? await this.ctx.db.query(edgeDefinition.table).withIndex(
675
+ edgeDefinition.ref,
676
+ (q) => q.eq(edgeDefinition.ref, id).eq(
677
+ edgeDefinition.field,
678
+ edgeId
679
+ )
680
+ ).collect() : []
681
+ )
682
+ )
683
+ )).map((edgeDoc) => edgeDoc._id);
684
+ edges[key] = {
685
+ add,
686
+ removeEdges
687
+ };
688
+ }
625
689
  }
626
690
  })
627
691
  );
@@ -632,7 +696,7 @@ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
632
696
  }
633
697
  replace(value) {
634
698
  return new PromiseEntIdImpl(
635
- this.db,
699
+ this.ctx,
636
700
  this.entDefinitions,
637
701
  this.table,
638
702
  async () => {
@@ -641,38 +705,33 @@ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
641
705
  await this.base.checkReadAndWriteRule("update", docId, value);
642
706
  await this.base.checkUniqueness(value, docId);
643
707
  const fields = this.base.fieldsOnly(value);
644
- await this.db.replace(docId, fields);
708
+ await this.ctx.db.replace(docId, fields);
645
709
  const edges = {};
646
710
  await Promise.all(
647
711
  Object.values(
648
- this.entDefinitions[this.table].edges
712
+ getEdgeDefinitions(this.entDefinitions, this.table)
649
713
  ).map(async (edgeDefinition) => {
650
714
  const key = edgeDefinition.name;
651
715
  const idOrIds = value[key];
652
716
  if (edgeDefinition.cardinality === "single") {
653
717
  if (edgeDefinition.type === "ref") {
654
- const oldDoc = await this.db.get(docId);
718
+ const oldDoc = await this.ctx.db.get(docId);
655
719
  if (oldDoc[key] !== void 0 && oldDoc[key] !== idOrIds) {
656
720
  throw new Error("Cannot set 1:1 edge from optional end.");
657
721
  }
658
722
  }
659
723
  } else {
660
724
  if (edgeDefinition.type === "field") {
661
- const existing = (await this.db.query(edgeDefinition.to).withIndex(
662
- edgeDefinition.ref,
663
- (q) => q.eq(edgeDefinition.ref, docId)
664
- ).collect()).map((doc) => doc._id);
665
- edges[key] = {
666
- add: idOrIds,
667
- remove: existing
668
- };
725
+ if (idOrIds !== void 0) {
726
+ throw new Error("Cannot set 1:many edge from many end.");
727
+ }
669
728
  } else {
670
729
  const requested = new Set(idOrIds ?? []);
671
- const remove = (await this.db.query(edgeDefinition.table).withIndex(
730
+ const removeEdges = (await this.ctx.db.query(edgeDefinition.table).withIndex(
672
731
  edgeDefinition.field,
673
732
  (q) => q.eq(edgeDefinition.field, docId)
674
733
  ).collect()).map((doc) => [doc._id, doc[edgeDefinition.ref]]).concat(
675
- edgeDefinition.symmetric ? (await this.db.query(edgeDefinition.table).withIndex(
734
+ edgeDefinition.symmetric ? (await this.ctx.db.query(edgeDefinition.table).withIndex(
676
735
  edgeDefinition.ref,
677
736
  (q) => q.eq(edgeDefinition.ref, docId)
678
737
  ).collect()).map(
@@ -686,8 +745,8 @@ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
686
745
  return true;
687
746
  }).map(([edgeId]) => edgeId);
688
747
  edges[key] = {
689
- add: Array.from(requested),
690
- removeEdges: remove
748
+ add: idOrIds ?? [],
749
+ removeEdges
691
750
  };
692
751
  }
693
752
  }
@@ -701,26 +760,26 @@ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
701
760
  async delete() {
702
761
  const { id: docId } = await this.retrieve();
703
762
  const id = docId;
704
- return this.base.deleteId(id);
763
+ return this.base.deleteId(id, "default");
705
764
  }
706
765
  };
707
766
  var PromiseEntIdImpl = class extends Promise {
708
- constructor(db, entDefinitions, table, retrieve) {
767
+ constructor(ctx, entDefinitions, table, retrieve) {
709
768
  super(() => {
710
769
  });
711
- this.db = db;
770
+ this.ctx = ctx;
712
771
  this.entDefinitions = entDefinitions;
713
772
  this.table = table;
714
773
  this.retrieve = retrieve;
715
774
  }
716
775
  get() {
717
776
  return new PromiseEntOrNullImpl(
718
- this.db,
777
+ this.ctx,
719
778
  this.entDefinitions,
720
779
  this.table,
721
780
  async () => {
722
781
  const id = await this.retrieve();
723
- return { id, doc: async () => this.db.get(id) };
782
+ return { id, doc: async () => this.ctx.db.get(id) };
724
783
  },
725
784
  true
726
785
  );
@@ -739,7 +798,7 @@ function loadedRetriever(doc) {
739
798
  doc: async () => doc
740
799
  };
741
800
  }
742
- async function filterByReadRule(db, entDefinitions, table, docs, throwIfNull) {
801
+ async function filterByReadRule(ctx, entDefinitions, table, docs, throwIfNull) {
743
802
  if (docs === null) {
744
803
  return null;
745
804
  }
@@ -750,7 +809,7 @@ async function filterByReadRule(db, entDefinitions, table, docs, throwIfNull) {
750
809
  const decisions = await Promise.all(
751
810
  docs.map(async (doc) => {
752
811
  const decision = await readPolicy(
753
- entWrapper(doc, db, entDefinitions, table)
812
+ entWrapper(doc, ctx, entDefinitions, table)
754
813
  );
755
814
  if (throwIfNull && !decision) {
756
815
  throw new Error(
@@ -768,134 +827,126 @@ function getReadRule(entDefinitions, table) {
768
827
  function getWriteRule(entDefinitions, table) {
769
828
  return entDefinitions.rules?.[table]?.write;
770
829
  }
830
+ function getEdgeDefinitions(entDefinitions, table) {
831
+ return entDefinitions[table].edges;
832
+ }
833
+ function getDeletionConfig(entDefinitions, table) {
834
+ return entDefinitions[table].deletionConfig;
835
+ }
771
836
 
772
837
  // src/writer.ts
773
838
  var WriterImplBase = class _WriterImplBase {
774
- constructor(db, entDefinitions, table) {
775
- this.db = db;
839
+ constructor(ctx, entDefinitions, table) {
840
+ this.ctx = ctx;
776
841
  this.entDefinitions = entDefinitions;
777
842
  this.table = table;
778
843
  }
779
- async deleteId(id) {
844
+ async deleteId(id, behavior) {
780
845
  await this.checkReadAndWriteRule("delete", id, void 0);
781
- let memoized = void 0;
782
- const oldDoc = async () => {
783
- if (memoized !== void 0) {
784
- return memoized;
785
- }
786
- return memoized = await this.db.get(id);
787
- };
846
+ const deletionConfig = getDeletionConfig(this.entDefinitions, this.table);
847
+ const isDeletingSoftly = behavior !== "hard" && deletionConfig !== void 0 && (deletionConfig.type === "soft" || deletionConfig.type === "scheduled");
848
+ if (behavior === "soft" && !isDeletingSoftly) {
849
+ throw new Error(
850
+ `Cannot soft delete document with ID "${id}" in table "${this.table}" because it does not have an "allowSoft", "soft" or "scheduled" deletion behavior configured.`
851
+ );
852
+ }
788
853
  const edges = {};
789
854
  await Promise.all(
790
- Object.values(
791
- this.entDefinitions[this.table].edges
792
- ).map(async (edgeDefinition) => {
793
- const key = edgeDefinition.name;
794
- if (edgeDefinition.cardinality === "single") {
795
- if (edgeDefinition.type === "ref") {
796
- edges[key] = {
797
- remove: (await oldDoc())[key]
798
- };
799
- }
800
- } else {
801
- if (edgeDefinition.type === "field") {
802
- const existing = (await this.db.query(edgeDefinition.to).withIndex(
803
- edgeDefinition.ref,
804
- (q) => q.eq(edgeDefinition.ref, id)
805
- ).collect()).map((doc) => doc._id);
806
- edges[key] = { remove: existing };
807
- } else {
808
- const existing = (await this.db.query(edgeDefinition.table).withIndex(
809
- edgeDefinition.field,
810
- (q) => q.eq(edgeDefinition.field, id)
811
- ).collect()).concat(
812
- edgeDefinition.symmetric ? await this.db.query(edgeDefinition.table).withIndex(
855
+ Object.values(getEdgeDefinitions(this.entDefinitions, this.table)).map(
856
+ async (edgeDefinition) => {
857
+ const key = edgeDefinition.name;
858
+ if (edgeDefinition.cardinality === "single" && edgeDefinition.type === "ref" || edgeDefinition.cardinality === "multiple" && edgeDefinition.type === "field") {
859
+ if (!isDeletingSoftly || edgeDefinition.deletion === "soft") {
860
+ const remove = (await this.ctx.db.query(edgeDefinition.to).withIndex(
813
861
  edgeDefinition.ref,
814
862
  (q) => q.eq(edgeDefinition.ref, id)
815
- ).collect() : []
816
- ).map((doc) => doc._id);
817
- edges[key] = { removeEdges: existing };
863
+ ).collect()).map((doc) => doc._id);
864
+ edges[key] = { remove };
865
+ }
866
+ } else if (edgeDefinition.cardinality === "multiple") {
867
+ if (!isDeletingSoftly) {
868
+ const removeEdges = (await this.ctx.db.query(edgeDefinition.table).withIndex(
869
+ edgeDefinition.field,
870
+ (q) => q.eq(edgeDefinition.field, id)
871
+ ).collect()).concat(
872
+ edgeDefinition.symmetric ? await this.ctx.db.query(edgeDefinition.table).withIndex(
873
+ edgeDefinition.ref,
874
+ (q) => q.eq(edgeDefinition.ref, id)
875
+ ).collect() : []
876
+ ).map((doc) => doc._id);
877
+ edges[key] = { removeEdges };
878
+ }
818
879
  }
819
880
  }
820
- })
881
+ )
821
882
  );
822
- await this.db.delete(id);
823
- await this.writeEdges(id, edges);
883
+ const deletionTime = +/* @__PURE__ */ new Date();
884
+ if (isDeletingSoftly) {
885
+ await this.ctx.db.patch(id, { deletionTime });
886
+ } else {
887
+ try {
888
+ await this.ctx.db.delete(id);
889
+ } catch (e) {
890
+ }
891
+ }
892
+ await this.writeEdges(id, edges, isDeletingSoftly);
893
+ if (deletionConfig !== void 0 && deletionConfig.type === "scheduled") {
894
+ const fnRef = this.ctx.scheduledDelete ?? (0, import_server.makeFunctionReference)(
895
+ "functions:scheduledDelete"
896
+ );
897
+ await this.ctx.scheduler.runAfter(deletionConfig.delayMs ?? 0, fnRef, {
898
+ origin: {
899
+ id,
900
+ table: this.table,
901
+ deletionTime
902
+ },
903
+ inProgress: false,
904
+ stack: []
905
+ });
906
+ }
824
907
  return id;
825
908
  }
826
- async deletedIdIn(id, table) {
827
- await new _WriterImplBase(this.db, this.entDefinitions, table).deleteId(id);
909
+ async deletedIdIn(id, table, cascadingSoft) {
910
+ await new _WriterImplBase(this.ctx, this.entDefinitions, table).deleteId(
911
+ id,
912
+ cascadingSoft ? "soft" : "hard"
913
+ );
828
914
  }
829
- async writeEdges(docId, changes) {
915
+ async writeEdges(docId, changes, deleteSoftly) {
830
916
  await Promise.all(
831
- Object.values(
832
- this.entDefinitions[this.table].edges
833
- ).map(async (edgeDefinition) => {
834
- const idOrIds = changes[edgeDefinition.name];
835
- if (idOrIds === void 0) {
836
- return;
837
- }
838
- if (edgeDefinition.cardinality === "single") {
839
- if (edgeDefinition.type === "ref") {
840
- if (idOrIds.remove !== void 0) {
841
- await this.deletedIdIn(
842
- idOrIds.remove,
843
- edgeDefinition.to
844
- );
845
- }
846
- if (idOrIds.add !== void 0) {
847
- await this.db.patch(
848
- idOrIds.add,
849
- { [edgeDefinition.ref]: docId }
850
- );
851
- }
917
+ Object.values(getEdgeDefinitions(this.entDefinitions, this.table)).map(
918
+ async (edgeDefinition) => {
919
+ const idOrIds = changes[edgeDefinition.name];
920
+ if (idOrIds === void 0) {
921
+ return;
852
922
  }
853
- } else {
854
- if (edgeDefinition.type === "field") {
855
- if (idOrIds.remove !== void 0) {
923
+ if (edgeDefinition.cardinality === "single" && edgeDefinition.type === "ref" || edgeDefinition.cardinality === "multiple" && edgeDefinition.type === "field") {
924
+ if (idOrIds.remove !== void 0 && idOrIds.remove.length > 0) {
856
925
  await Promise.all(
857
926
  idOrIds.remove.map(
858
- (id) => this.deletedIdIn(id, edgeDefinition.to)
927
+ (id) => this.deletedIdIn(
928
+ id,
929
+ edgeDefinition.to,
930
+ (deleteSoftly ?? false) && edgeDefinition.deletion === "soft"
931
+ )
859
932
  )
860
933
  );
861
934
  }
862
- if (idOrIds.add !== void 0) {
935
+ if (idOrIds.add !== void 0 && idOrIds.add.length > 0) {
863
936
  await Promise.all(
864
937
  idOrIds.add.map(
865
- async (id) => this.db.patch(id, {
938
+ async (id) => this.ctx.db.patch(id, {
866
939
  [edgeDefinition.ref]: docId
867
940
  })
868
941
  )
869
942
  );
870
943
  }
871
- } else {
872
- let removeEdges = [];
873
- if (idOrIds.remove !== void 0) {
874
- removeEdges = (await Promise.all(
875
- idOrIds.remove.map(
876
- async (id) => (await this.db.query(edgeDefinition.table).withIndex(
877
- edgeDefinition.field,
878
- (q) => q.eq(edgeDefinition.field, docId).eq(
879
- edgeDefinition.ref,
880
- id
881
- )
882
- ).collect()).concat(
883
- edgeDefinition.symmetric ? await this.db.query(edgeDefinition.table).withIndex(
884
- edgeDefinition.ref,
885
- (q) => q.eq(edgeDefinition.ref, docId).eq(edgeDefinition.field, id)
886
- ).collect() : []
887
- )
888
- )
889
- )).map((doc) => doc._id);
890
- }
891
- if (idOrIds.removeEdges !== void 0) {
892
- removeEdges = idOrIds.removeEdges;
893
- }
894
- if (removeEdges.length > 0) {
944
+ } else if (edgeDefinition.cardinality === "multiple") {
945
+ if ((idOrIds.removeEdges ?? []).length > 0) {
895
946
  await Promise.all(
896
- removeEdges.map(async (id) => {
947
+ idOrIds.removeEdges.map(async (id) => {
897
948
  try {
898
- await this.db.delete(id);
949
+ await this.ctx.db.delete(id);
899
950
  } catch (e) {
900
951
  }
901
952
  })
@@ -904,12 +955,12 @@ var WriterImplBase = class _WriterImplBase {
904
955
  if (idOrIds.add !== void 0) {
905
956
  await Promise.all(
906
957
  idOrIds.add.map(async (id) => {
907
- await this.db.insert(edgeDefinition.table, {
958
+ await this.ctx.db.insert(edgeDefinition.table, {
908
959
  [edgeDefinition.field]: docId,
909
960
  [edgeDefinition.ref]: id
910
961
  });
911
962
  if (edgeDefinition.symmetric) {
912
- await this.db.insert(edgeDefinition.table, {
963
+ await this.ctx.db.insert(edgeDefinition.table, {
913
964
  [edgeDefinition.field]: id,
914
965
  [edgeDefinition.ref]: docId
915
966
  });
@@ -919,7 +970,7 @@ var WriterImplBase = class _WriterImplBase {
919
970
  }
920
971
  }
921
972
  }
922
- })
973
+ )
923
974
  );
924
975
  }
925
976
  async checkUniqueness(value, id) {
@@ -930,7 +981,7 @@ var WriterImplBase = class _WriterImplBase {
930
981
  if (fieldDefinition.unique) {
931
982
  const key = fieldDefinition.name;
932
983
  const fieldValue = value[key];
933
- const existing = await this.db.query(this.table).withIndex(key, (q) => q.eq(key, value[key])).unique();
984
+ const existing = await this.ctx.db.query(this.table).withIndex(key, (q) => q.eq(key, value[key])).unique();
934
985
  if (existing !== null && (id === void 0 || existing._id !== id)) {
935
986
  throw new Error(
936
987
  `In table "${this.table}" cannot create a duplicate document with field "${key}" of value \`${fieldValue}\`, existing document with ID "${existing._id}" already has it.`
@@ -940,28 +991,31 @@ var WriterImplBase = class _WriterImplBase {
940
991
  })
941
992
  );
942
993
  await Promise.all(
943
- Object.values(
944
- this.entDefinitions[this.table].edges
945
- ).map(async (edgeDefinition) => {
946
- if (edgeDefinition.cardinality === "single" && edgeDefinition.type === "field" && edgeDefinition.unique) {
947
- const key = edgeDefinition.field;
948
- if (value[key] === void 0) {
949
- return;
950
- }
951
- const existing = await this.db.query(this.table).withIndex(key, (q) => q.eq(key, value[key])).unique();
952
- if (existing !== null && (id === void 0 || existing._id !== id)) {
953
- throw new Error(
954
- `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.`
955
- );
994
+ Object.values(getEdgeDefinitions(this.entDefinitions, this.table)).map(
995
+ async (edgeDefinition) => {
996
+ if (edgeDefinition.cardinality === "single" && edgeDefinition.type === "field" && edgeDefinition.unique) {
997
+ const key = edgeDefinition.field;
998
+ if (value[key] === void 0) {
999
+ return;
1000
+ }
1001
+ const existing = await this.ctx.db.query(this.table).withIndex(key, (q) => q.eq(key, value[key])).unique();
1002
+ if (existing !== null && (id === void 0 || existing._id !== id)) {
1003
+ throw new Error(
1004
+ `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.`
1005
+ );
1006
+ }
956
1007
  }
957
1008
  }
958
- })
1009
+ )
959
1010
  );
960
1011
  }
961
1012
  fieldsOnly(value) {
962
1013
  const fields = {};
963
1014
  Object.keys(value).forEach((key) => {
964
- const edgeDefinition = this.entDefinitions[this.table].edges[key];
1015
+ const edgeDefinition = getEdgeDefinitions(
1016
+ this.entDefinitions,
1017
+ this.table
1018
+ )[key];
965
1019
  if (edgeDefinition === void 0) {
966
1020
  fields[key] = value[key];
967
1021
  }
@@ -972,7 +1026,7 @@ var WriterImplBase = class _WriterImplBase {
972
1026
  if (id !== void 0) {
973
1027
  const readPolicy = getReadRule(this.entDefinitions, this.table);
974
1028
  if (readPolicy !== void 0) {
975
- const doc = await this.db.get(id);
1029
+ const doc = await this.ctx.db.get(id);
976
1030
  if (doc === null) {
977
1031
  throw new Error(
978
1032
  `Cannot update document with ID "${id}" in table "${this.table} because it does not exist"`
@@ -991,8 +1045,8 @@ var WriterImplBase = class _WriterImplBase {
991
1045
  return;
992
1046
  }
993
1047
  const ent = id === void 0 ? void 0 : entWrapper(
994
- await this.db.get(id),
995
- this.db,
1048
+ await this.ctx.db.get(id),
1049
+ this.ctx,
996
1050
  this.entDefinitions,
997
1051
  this.table
998
1052
  );