convex-ents 0.1.0 → 0.2.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.
@@ -1,13 +1,13 @@
1
1
  import { GenericEntsDataModel } from './schema.js';
2
2
  import * as convex_values from 'convex/values';
3
3
  import { GenericId } from 'convex/values';
4
- import { TableNamesInDataModel, DocumentByName, FilterBuilder, NamedTableInfo, ExpressionOrValue, PaginationOptions, PaginationResult, IndexNames, FieldTypeFromFieldPath, SearchIndexNames, SearchFilterBuilder, NamedSearchIndex, SearchFilter, GenericDatabaseReader, GenericDatabaseWriter, IndexRangeBuilder, NamedIndex, IndexRange, WithoutSystemFields, WithOptionalSystemFields, GenericDocument } from 'convex/server';
4
+ import { TableNamesInDataModel, DocumentByName, FilterBuilder, NamedTableInfo, ExpressionOrValue, PaginationOptions, IndexNames, FieldTypeFromFieldPath, SearchIndexNames, SearchFilterBuilder, NamedSearchIndex, SearchFilter, PaginationResult, GenericDatabaseReader, GenericDatabaseWriter, IndexRangeBuilder, NamedIndex, IndexRange, WithoutSystemFields, WithOptionalSystemFields, GenericDocument } from 'convex/server';
5
5
  import { WithEdges, WithEdgePatches } from './writer.js';
6
6
 
7
7
  interface PromiseOrderedQueryOrNull<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> extends Promise<Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[] | null> {
8
8
  filter(predicate: (q: FilterBuilder<NamedTableInfo<EntsDataModel, Table>>) => ExpressionOrValue<boolean>): this;
9
9
  map<TOutput>(callbackFn: (value: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>, index: number, array: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[]) => Promise<TOutput> | TOutput): Promise<TOutput[] | null>;
10
- paginate(paginationOpts: PaginationOptions): Promise<PaginationResult<DocumentByName<EntsDataModel, Table>> | null>;
10
+ paginate(paginationOpts: PaginationOptions): PromisePaginationResultOrNull<EntsDataModel, Table>;
11
11
  take(n: number): PromiseEntsOrNull<EntsDataModel, Table>;
12
12
  first(): PromiseEntOrNull<EntsDataModel, Table>;
13
13
  unique(): PromiseEntOrNull<EntsDataModel, Table>;
@@ -66,7 +66,7 @@ interface PromiseTable<EntsDataModel extends GenericEntsDataModel, Table extends
66
66
  }
67
67
  interface PromiseOrderedQueryBase<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> {
68
68
  filter(predicate: (q: FilterBuilder<NamedTableInfo<EntsDataModel, Table>>) => ExpressionOrValue<boolean>): this;
69
- paginate(paginationOpts: PaginationOptions): Promise<PaginationResult<DocumentByName<EntsDataModel, Table>>>;
69
+ paginate(paginationOpts: PaginationOptions): PromisePaginationResult<EntsDataModel, Table>;
70
70
  first(): PromiseEntOrNull<EntsDataModel, Table>;
71
71
  unique(): PromiseEntOrNull<EntsDataModel, Table>;
72
72
  docs(): Promise<DocumentByName<EntsDataModel, Table>[]>;
@@ -80,6 +80,14 @@ interface PromiseOrderedQuery<EntsDataModel extends GenericEntsDataModel, Table
80
80
  interface PromiseQuery<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> extends PromiseOrderedQuery<EntsDataModel, Table> {
81
81
  order(order: "asc" | "desc", indexName?: IndexNames<NamedTableInfo<EntsDataModel, Table>>): PromiseOrderedQuery<EntsDataModel, Table>;
82
82
  }
83
+ interface PromisePaginationResultOrNull<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> extends Promise<PaginationResult<Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>> | null> {
84
+ docs(): Promise<PaginationResult<DocumentByName<EntsDataModel, Table>> | null>;
85
+ map<TOutput>(callbackFn: (value: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>, index: number, array: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[]) => Promise<TOutput> | TOutput): Promise<PaginationResult<TOutput> | null>;
86
+ }
87
+ interface PromisePaginationResult<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> extends Promise<PaginationResult<Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>>> {
88
+ docs(): Promise<PaginationResult<DocumentByName<EntsDataModel, Table>>>;
89
+ map<TOutput>(callbackFn: (value: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>, index: number, array: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[]) => Promise<TOutput> | TOutput): Promise<PaginationResult<TOutput>>;
90
+ }
83
91
  interface PromiseEntsOrNull<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> extends Promise<Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[] | null> {
84
92
  map<TOutput>(callbackFn: (value: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>, index: number, array: Ent<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[]) => Promise<TOutput> | TOutput): Promise<TOutput[] | null>;
85
93
  docs(): Promise<DocumentByName<EntsDataModel, Table>[] | null>;
@@ -135,6 +143,7 @@ type PromiseEdge<EntsDataModel extends GenericEntsDataModel, Table extends Table
135
143
  type PromiseEdgeOrThrow<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>, Edge extends keyof EntsDataModel[Table]["edges"]> = EntsDataModel[Table]["edges"][Edge]["cardinality"] extends "multiple" ? EntsDataModel[Table]["edges"][Edge]["type"] extends "ref" ? PromiseEdgeEnts<EntsDataModel, EntsDataModel[Table]["edges"][Edge]["to"]> : PromiseQuery<EntsDataModel, EntsDataModel[Table]["edges"][Edge]["to"]> : EntsDataModel[Table]["edges"][Edge]["type"] extends "ref" ? PromiseEnt<EntsDataModel, EntsDataModel[Table]["edges"][Edge]["to"]> : PromiseEnt<EntsDataModel, EntsDataModel[Table]["edges"][Edge]["to"]>;
136
144
  type PromiseEdgeOrNull<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>, Edge extends keyof EntsDataModel[Table]["edges"]> = EntsDataModel[Table]["edges"][Edge]["cardinality"] extends "multiple" ? EntsDataModel[Table]["edges"][Edge]["type"] extends "ref" ? PromiseEdgeEntsOrNull<EntsDataModel, EntsDataModel[Table]["edges"][Edge]["to"]> : PromiseQueryOrNull<EntsDataModel, EntsDataModel[Table]["edges"][Edge]["to"]> : PromiseEntOrNull<EntsDataModel, EntsDataModel[Table]["edges"][Edge]["to"]>;
137
145
  interface PromiseOrderedQueryWriter<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> extends Promise<EntWriter<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[]>, PromiseOrderedQueryBase<EntsDataModel, Table> {
146
+ paginate(paginationOpts: PaginationOptions): PromisePaginationResultWriter<EntsDataModel, Table>;
138
147
  map<TOutput>(callbackFn: (value: EntWriter<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>, index: number, array: EntWriter<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[]) => Promise<TOutput> | TOutput): Promise<TOutput[]>;
139
148
  take(n: number): PromiseEntsWriter<EntsDataModel, Table>;
140
149
  firstX(): PromiseEntWriter<EntsDataModel, Table>;
@@ -147,6 +156,10 @@ interface PromiseEntsWriter<EntsDataModel extends GenericEntsDataModel, Table ex
147
156
  firstX(): PromiseEntWriter<EntsDataModel, Table>;
148
157
  uniqueX(): PromiseEntWriter<EntsDataModel, Table>;
149
158
  }
159
+ interface PromisePaginationResultWriter<EntsDataModel extends GenericEntsDataModel, Table extends TableNamesInDataModel<EntsDataModel>> extends Promise<PaginationResult<EntWriter<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>>> {
160
+ docs(): Promise<PaginationResult<DocumentByName<EntsDataModel, Table>>>;
161
+ map<TOutput>(callbackFn: (value: EntWriter<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>, index: number, array: EntWriter<Table, DocumentByName<EntsDataModel, Table>, EntsDataModel>[]) => Promise<TOutput> | TOutput): Promise<PaginationResult<TOutput>>;
162
+ }
150
163
  interface PromiseTableWriter<Table extends TableNamesInDataModel<EntsDataModel>, EntsDataModel extends GenericEntsDataModel> extends PromiseQueryWriter<EntsDataModel, Table>, PromiseTableBase<EntsDataModel, Table> {
151
164
  get<Indexes extends EntsDataModel[Table]["indexes"], Index extends keyof Indexes>(indexName: Index, value0: FieldTypeFromFieldPath<DocumentByName<EntsDataModel, Table>, Indexes[Index][0]>): PromiseEntWriterOrNull<EntsDataModel, Table>;
152
165
  get(id: GenericId<Table>): PromiseEntWriterOrNull<EntsDataModel, Table>;
@@ -300,4 +313,4 @@ declare function getWriteRule(entDefinitions: GenericEntsDataModel, table: strin
300
313
  value: undefined;
301
314
  }) => Promise<boolean>) | undefined;
302
315
 
303
- export { type DocRetriever, type Ent, type GenericEnt, type GenericEntWriter, type PromiseEdge, type PromiseEdgeEnts, type PromiseEdgeEntsOrNull, type PromiseEdgeOrThrow, type PromiseEnt, type PromiseEntId, type PromiseEntOrNull, type PromiseEntWriter, type PromiseEntWriterOrNull, type PromiseEnts, type PromiseEntsOrNull, type PromiseEntsOrNulls, type PromiseEntsWriter, type PromiseOrderedQuery, type PromiseOrderedQueryBase, type PromiseOrderedQueryOrNull, type PromiseOrderedQueryWriter, type PromiseQuery, type PromiseQueryOrNull, type PromiseQueryWriter, type PromiseTable, type PromiseTableBase, type PromiseTableWriter, addEntRules, entWrapper, entsTableFactory, getReadRule, getWriteRule };
316
+ export { type DocRetriever, type Ent, type GenericEnt, type GenericEntWriter, type PromiseEdge, type PromiseEdgeEnts, type PromiseEdgeEntsOrNull, type PromiseEdgeOrThrow, type PromiseEnt, type PromiseEntId, type PromiseEntOrNull, type PromiseEntWriter, type PromiseEntWriterOrNull, type PromiseEnts, type PromiseEntsOrNull, type PromiseEntsOrNulls, type PromiseEntsWriter, type PromiseOrderedQuery, type PromiseOrderedQueryBase, type PromiseOrderedQueryOrNull, type PromiseOrderedQueryWriter, type PromisePaginationResult, type PromisePaginationResultOrNull, type PromisePaginationResultWriter, type PromiseQuery, type PromiseQueryOrNull, type PromiseQueryWriter, type PromiseTable, type PromiseTableBase, type PromiseTableWriter, addEntRules, entWrapper, entsTableFactory, getReadRule, getWriteRule };
package/dist/functions.js CHANGED
@@ -29,12 +29,62 @@ __export(functions_exports, {
29
29
  module.exports = __toCommonJS(functions_exports);
30
30
 
31
31
  // src/writer.ts
32
- var WriterImplBase = class {
32
+ var WriterImplBase = class _WriterImplBase {
33
33
  constructor(db, entDefinitions, table) {
34
34
  this.db = db;
35
35
  this.entDefinitions = entDefinitions;
36
36
  this.table = table;
37
37
  }
38
+ async deleteId(id) {
39
+ await this.checkReadAndWriteRule("delete", id, void 0);
40
+ let memoized = void 0;
41
+ const oldDoc = async () => {
42
+ if (memoized !== void 0) {
43
+ return memoized;
44
+ }
45
+ return memoized = await this.db.get(id);
46
+ };
47
+ const edges = {};
48
+ await Promise.all(
49
+ Object.values(
50
+ this.entDefinitions[this.table].edges
51
+ ).map(async (edgeDefinition) => {
52
+ const key = edgeDefinition.name;
53
+ if (edgeDefinition.cardinality === "single") {
54
+ if (edgeDefinition.type === "ref") {
55
+ edges[key] = {
56
+ remove: (await oldDoc())[key]
57
+ };
58
+ }
59
+ } else {
60
+ if (edgeDefinition.type === "field") {
61
+ const existing = (await this.db.query(edgeDefinition.to).withIndex(
62
+ edgeDefinition.ref,
63
+ (q) => q.eq(edgeDefinition.ref, id)
64
+ ).collect()).map((doc) => doc._id);
65
+ edges[key] = { remove: existing };
66
+ } else {
67
+ const existing = (await this.db.query(edgeDefinition.table).withIndex(
68
+ edgeDefinition.field,
69
+ (q) => q.eq(edgeDefinition.field, id)
70
+ ).collect()).concat(
71
+ edgeDefinition.symmetric ? await this.db.query(edgeDefinition.table).withIndex(
72
+ edgeDefinition.ref,
73
+ (q) => q.eq(edgeDefinition.ref, id)
74
+ ).collect() : []
75
+ ).map((doc) => doc._id);
76
+ edges[key] = { removeEdges: existing };
77
+ }
78
+ }
79
+ })
80
+ );
81
+ await this.db.delete(id);
82
+ await this.writeEdges(id, edges);
83
+ return id;
84
+ }
85
+ async deletedIdIn(id, table) {
86
+ await new _WriterImplBase(this.db, this.entDefinitions, table).deleteId(id);
87
+ }
38
88
  async writeEdges(docId, changes) {
39
89
  await Promise.all(
40
90
  Object.values(
@@ -47,7 +97,10 @@ var WriterImplBase = class {
47
97
  if (edgeDefinition.cardinality === "single") {
48
98
  if (edgeDefinition.type === "ref") {
49
99
  if (idOrIds.remove !== void 0) {
50
- await this.db.delete(idOrIds.remove);
100
+ await this.deletedIdIn(
101
+ idOrIds.remove,
102
+ edgeDefinition.to
103
+ );
51
104
  }
52
105
  if (idOrIds.add !== void 0) {
53
106
  await this.db.patch(
@@ -61,7 +114,7 @@ var WriterImplBase = class {
61
114
  if (idOrIds.remove !== void 0) {
62
115
  await Promise.all(
63
116
  idOrIds.remove.map(
64
- (id) => this.db.delete(id)
117
+ (id) => this.deletedIdIn(id, edgeDefinition.to)
65
118
  )
66
119
  );
67
120
  }
@@ -276,13 +329,14 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
276
329
  }
277
330
  );
278
331
  }
279
- // TODO: RLS for pagination
280
- async paginate(paginationOpts) {
281
- const query = await this.retrieve();
282
- if (query === null) {
283
- return null;
284
- }
285
- return await query.paginate(paginationOpts);
332
+ paginate(paginationOpts) {
333
+ return new PromisePaginationResultOrNullImpl(
334
+ this.db,
335
+ this.entDefinitions,
336
+ this.table,
337
+ this.retrieve,
338
+ paginationOpts
339
+ );
286
340
  }
287
341
  take(n) {
288
342
  return new PromiseEntsOrNullImpl(
@@ -432,6 +486,54 @@ var PromiseQueryOrNullImpl = class _PromiseQueryOrNullImpl extends Promise {
432
486
  return docs;
433
487
  }
434
488
  };
489
+ var PromisePaginationResultOrNullImpl = class extends Promise {
490
+ constructor(db, entDefinitions, table, retrieve, paginationOpts) {
491
+ super(() => {
492
+ });
493
+ this.db = db;
494
+ this.entDefinitions = entDefinitions;
495
+ this.table = table;
496
+ this.retrieve = retrieve;
497
+ this.paginationOpts = paginationOpts;
498
+ }
499
+ async map(callbackFn) {
500
+ const result = await this;
501
+ if (result === null) {
502
+ return null;
503
+ }
504
+ return {
505
+ ...result,
506
+ page: await Promise.all(result.page.map(callbackFn))
507
+ };
508
+ }
509
+ async docs() {
510
+ const query = await this.retrieve();
511
+ if (query === null) {
512
+ return null;
513
+ }
514
+ const result = await query.paginate(this.paginationOpts);
515
+ return {
516
+ ...result,
517
+ page: await filterByReadRule(
518
+ this.db,
519
+ this.entDefinitions,
520
+ this.table,
521
+ result.page,
522
+ false
523
+ )
524
+ };
525
+ }
526
+ then(onfulfilled, onrejected) {
527
+ return this.docs().then(
528
+ (result) => result === null ? null : {
529
+ ...result,
530
+ page: result.page.map(
531
+ (doc) => entWrapper(doc, this.db, this.entDefinitions, this.table)
532
+ )
533
+ }
534
+ ).then(onfulfilled, onrejected);
535
+ }
536
+ };
435
537
  var PromiseTableImpl = class extends PromiseQueryOrNullImpl {
436
538
  constructor(db, entDefinitions, table) {
437
539
  super(db, entDefinitions, table, async () => db.query(table));
@@ -1042,51 +1144,7 @@ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
1042
1144
  async delete() {
1043
1145
  const { id: docId } = await this.retrieve();
1044
1146
  const id = docId;
1045
- await this.base.checkReadAndWriteRule("delete", id, void 0);
1046
- let memoized = void 0;
1047
- const oldDoc = async () => {
1048
- if (memoized !== void 0) {
1049
- return memoized;
1050
- }
1051
- return memoized = await this.db.get(id);
1052
- };
1053
- const edges = {};
1054
- await Promise.all(
1055
- Object.values(
1056
- this.entDefinitions[this.table].edges
1057
- ).map(async (edgeDefinition) => {
1058
- const key = edgeDefinition.name;
1059
- if (edgeDefinition.cardinality === "single") {
1060
- if (edgeDefinition.type === "ref") {
1061
- edges[key] = {
1062
- remove: (await oldDoc())[key]
1063
- };
1064
- }
1065
- } else {
1066
- if (edgeDefinition.type === "field") {
1067
- const existing = (await this.db.query(edgeDefinition.to).withIndex(
1068
- edgeDefinition.ref,
1069
- (q) => q.eq(edgeDefinition.ref, id)
1070
- ).collect()).map((doc) => doc._id);
1071
- edges[key] = { remove: existing };
1072
- } else {
1073
- const existing = (await this.db.query(edgeDefinition.table).withIndex(
1074
- edgeDefinition.field,
1075
- (q) => q.eq(edgeDefinition.field, id)
1076
- ).collect()).concat(
1077
- edgeDefinition.symmetric ? await this.db.query(edgeDefinition.table).withIndex(
1078
- edgeDefinition.ref,
1079
- (q) => q.eq(edgeDefinition.ref, id)
1080
- ).collect() : []
1081
- ).map((doc) => doc._id);
1082
- edges[key] = { removeEdges: existing };
1083
- }
1084
- }
1085
- })
1086
- );
1087
- await this.db.delete(id);
1088
- await this.base.writeEdges(id, edges);
1089
- return id;
1147
+ return this.base.deleteId(id);
1090
1148
  }
1091
1149
  };
1092
1150
  var PromiseEntIdImpl = class extends Promise {
@@ -1132,23 +1190,23 @@ async function filterByReadRule(db, entDefinitions, table, docs, throwIfNull) {
1132
1190
  return null;
1133
1191
  }
1134
1192
  const readPolicy = getReadRule(entDefinitions, table);
1135
- if (readPolicy !== void 0) {
1136
- const decisions = await Promise.all(
1137
- docs.map(async (doc) => {
1138
- const decision = await readPolicy(
1139
- entWrapper(doc, db, entDefinitions, table)
1140
- );
1141
- if (throwIfNull && !decision) {
1142
- throw new Error(
1143
- `Document cannot be read with id \`${doc._id}\` in table "${table}"`
1144
- );
1145
- }
1146
- return decision;
1147
- })
1148
- );
1149
- return docs.filter((_, i) => decisions[i]);
1193
+ if (readPolicy === void 0) {
1194
+ return docs;
1150
1195
  }
1151
- return docs;
1196
+ const decisions = await Promise.all(
1197
+ docs.map(async (doc) => {
1198
+ const decision = await readPolicy(
1199
+ entWrapper(doc, db, entDefinitions, table)
1200
+ );
1201
+ if (throwIfNull && !decision) {
1202
+ throw new Error(
1203
+ `Document cannot be read with id \`${doc._id}\` in table "${table}"`
1204
+ );
1205
+ }
1206
+ return decision;
1207
+ })
1208
+ );
1209
+ return docs.filter((_, i) => decisions[i]);
1152
1210
  }
1153
1211
  function getReadRule(entDefinitions, table) {
1154
1212
  return entDefinitions.rules?.[table]?.read;