convex-ents 0.12.0 → 0.13.0-alpha.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/schema.d.ts CHANGED
@@ -77,7 +77,9 @@ interface EntDefinition<DocumentType extends Validator<any, any, any> = Validato
77
77
  field<FieldName extends string, T extends Validator<any, "required", any>>(field: FieldName, validator: T, options: {
78
78
  default: T["type"];
79
79
  }): EntDefinition<AddField<DocumentType, FieldName, T>, Indexes, SearchIndexes, VectorIndexes, Edges>;
80
- edge<EdgeName extends string>(edge: EdgeName): EntDefinition<AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${EdgeName}s`>>>, Indexes & {
80
+ edge<EdgeName extends string>(edge: EdgeName, options?: {
81
+ deletion: "hard" | "soft";
82
+ }): EntDefinition<AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${EdgeName}s`>>>, Indexes & {
81
83
  [key in `${EdgeName}Id`]: [`${EdgeName}Id`, "_creationTime"];
82
84
  }, SearchIndexes, VectorIndexes, Edges & {
83
85
  [key in EdgeName]: {
@@ -90,6 +92,7 @@ interface EntDefinition<DocumentType extends Validator<any, any, any> = Validato
90
92
  }>;
91
93
  edge<EdgeName extends string, const FieldName extends string>(edge: EdgeName, options: {
92
94
  field: FieldName;
95
+ deletion?: "hard" | "soft";
93
96
  }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${EdgeName}s`>>>, Indexes & {
94
97
  [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"];
95
98
  }, SearchIndexes, VectorIndexes, Edges & {
@@ -104,6 +107,7 @@ interface EntDefinition<DocumentType extends Validator<any, any, any> = Validato
104
107
  edge<EdgeName extends string, const FieldName extends string>(edge: EdgeName, options: {
105
108
  field: FieldName;
106
109
  optional: true;
110
+ deletion?: "hard" | "soft";
107
111
  }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VOptional<VId<GenericId<`${EdgeName}s`>>>>, Indexes & {
108
112
  [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"];
109
113
  }, SearchIndexes, VectorIndexes, Edges & {
@@ -118,6 +122,7 @@ interface EntDefinition<DocumentType extends Validator<any, any, any> = Validato
118
122
  edge<EdgeName extends string, const FieldName extends string, const ToTable extends string>(edge: EdgeName, options: {
119
123
  field: FieldName;
120
124
  to: ToTable;
125
+ deletion?: ToTable extends "_storage" | "_scheduled_functions" ? "hard" : "hard" | "soft";
121
126
  }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${ToTable}`>>>, Indexes & {
122
127
  [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"];
123
128
  }, SearchIndexes, VectorIndexes, Edges & {
@@ -129,11 +134,26 @@ interface EntDefinition<DocumentType extends Validator<any, any, any> = Validato
129
134
  optional: false;
130
135
  };
131
136
  }>;
137
+ edge<EdgeName extends string, const ToTable extends string>(edge: EdgeName, options: {
138
+ to: ToTable;
139
+ deletion?: ToTable extends "_storage" | "_scheduled_functions" ? "hard" : "hard" | "soft";
140
+ }): EntDefinition<AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${ToTable}`>>>, Indexes & {
141
+ [key in `${EdgeName}Id`]: [`${EdgeName}Id`, "_creationTime"];
142
+ }, SearchIndexes, VectorIndexes, Edges & {
143
+ [key in EdgeName]: {
144
+ name: EdgeName;
145
+ to: ToTable;
146
+ type: "field";
147
+ cardinality: "single";
148
+ optional: false;
149
+ };
150
+ }>;
132
151
  edge<EdgeName extends string, const FieldName extends string, const ToTable extends string>(edge: EdgeName, options: {
133
152
  field: FieldName;
134
153
  to: ToTable;
135
154
  optional: true;
136
- }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VOptional<VId<GenericId<`${EdgeName}s`>>>>, Indexes & {
155
+ deletion?: ToTable extends "_storage" | "_scheduled_functions" ? "hard" : "hard" | "soft";
156
+ }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VOptional<VId<GenericId<ToTable>>>>, Indexes & {
137
157
  [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"];
138
158
  }, SearchIndexes, VectorIndexes, Edges & {
139
159
  [key in EdgeName]: {
@@ -145,8 +165,7 @@ interface EntDefinition<DocumentType extends Validator<any, any, any> = Validato
145
165
  };
146
166
  }>;
147
167
  edge<EdgeName extends string>(edge: EdgeName, options: {
148
- optional: true;
149
- ref?: string;
168
+ ref: true | string;
150
169
  deletion?: "soft";
151
170
  }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & {
152
171
  [key in EdgeName]: {
@@ -157,9 +176,8 @@ interface EntDefinition<DocumentType extends Validator<any, any, any> = Validato
157
176
  };
158
177
  }>;
159
178
  edge<EdgeName extends string, const ToTable extends string>(edge: EdgeName, options: {
160
- optional: true;
161
179
  to: ToTable;
162
- ref?: string;
180
+ ref: true | string;
163
181
  deletion?: "soft";
164
182
  }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & {
165
183
  [key in EdgeName]: {
@@ -319,6 +337,7 @@ type EdgeConfig = {
319
337
  field: string;
320
338
  unique: boolean;
321
339
  optional: boolean;
340
+ deletion?: "soft" | "hard";
322
341
  } | {
323
342
  type: "ref";
324
343
  ref: string;
package/dist/schema.js CHANGED
@@ -37,13 +37,31 @@ function defineEntSchema(schema, options) {
37
37
  for (const edge of edgeConfigsBeforeDefineSchema(table)) {
38
38
  if (
39
39
  // Skip inverse edges, we process their forward edges
40
- edge.cardinality === "multiple" && edge.type === "ref" && edge.inverse !== void 0 || // symmetric is only set by defineEntSchema,
40
+ edge.cardinality === "multiple" && edge.type === "ref" && (edge.inverse !== void 0 || // symmetric is only set by defineEntSchema,
41
41
  // so we already processed the pair
42
- edge.symmetric !== void 0
42
+ edge.symmetric !== void 0)
43
43
  ) {
44
44
  continue;
45
45
  }
46
46
  const otherTableName = edge.to;
47
+ if (otherTableName.startsWith("_")) {
48
+ if (edge.cardinality !== "single") {
49
+ throw new Error(
50
+ `Many:many edge "${edge.name}" in table "${tableName}" points to a system table "${otherTableName}", but only 1:1 edges can point to system tables`
51
+ );
52
+ }
53
+ if (edge.type !== "field") {
54
+ throw new Error(
55
+ `Edge "${edge.name}" in table "${tableName}" pointing to a system table "${otherTableName}" must store the edge by storing the system document ID. Remove the \`ref\` option.`
56
+ );
57
+ }
58
+ if (edge.deletion === "soft") {
59
+ throw new Error(
60
+ `Edge "${edge.name}" in table "${tableName}" pointing to a system table "${otherTableName}" cannot use soft deletion, because system documents cannot be soft deleted.`
61
+ );
62
+ }
63
+ continue;
64
+ }
47
65
  const otherTable = schema[otherTableName];
48
66
  if (otherTable === void 0) {
49
67
  throw new Error(
@@ -73,7 +91,7 @@ function defineEntSchema(schema, options) {
73
91
  }
74
92
  if (inverseEdge.cardinality === "single" && inverseEdge.type === "ref") {
75
93
  throw new Error(
76
- `Both edge "${edge.name}" in table "${inverseEdge.to}" and edge "${inverseEdge.name}" in table "${edge.to}" are marked as optional, choose one to be required.`
94
+ `Both edge "${edge.name}" in table "${inverseEdge.to}" and edge "${inverseEdge.name}" in table "${edge.to}" are marked as references, choose one to store the edge by removing the \`ref\` option.`
77
95
  );
78
96
  }
79
97
  if (inverseEdge.cardinality !== "single" || inverseEdge.type !== "field") {
@@ -86,7 +104,7 @@ function defineEntSchema(schema, options) {
86
104
  }
87
105
  inverseEdge.unique = true;
88
106
  }
89
- if (edge.cardinality === "single" && edge.type === "ref" || edge.cardinality === "multiple" && edge.type === "field") {
107
+ if (edge.cardinality === "single" || edge.cardinality === "multiple" && edge.type === "field") {
90
108
  if (edge.deletion !== void 0 && deletionConfigFromEntDefinition(otherTable) === void 0) {
91
109
  throw new Error(
92
110
  `Cannot specify soft deletion behavior for edge "${edge.name}" in table "${tableName}" because the target table "${otherTableName}" does not have a "soft" or "scheduled" deletion behavior configured.`
@@ -102,7 +120,7 @@ function defineEntSchema(schema, options) {
102
120
  if (inverseEdge?.cardinality === "single") {
103
121
  if (inverseEdge.type === "ref") {
104
122
  throw new Error(
105
- `The edge "${inverseEdge.name}" in table "${otherTableName}" cannot be optional, as it must store the 1:many edge as a field. Check the its inverse edge "${edge.name}" in table "${tableName}".`
123
+ `The edge "${inverseEdge.name}" in table "${otherTableName}" specified \`ref\`, but it must store the 1:many edge as a field. Check the its inverse edge "${edge.name}" in table "${tableName}".`
106
124
  );
107
125
  }
108
126
  if (edge.type === "ref") {
@@ -142,17 +160,19 @@ function defineEntSchema(schema, options) {
142
160
  ]);
143
161
  }
144
162
  schema[edgeTableName] = edgeTable;
145
- edge.type = "ref";
146
- edge.table = edgeTableName;
147
- edge.field = forwardId;
148
- edge.ref = inverseId;
149
- edge.symmetric = inverseEdge === void 0;
163
+ const edgeConfig = edge;
164
+ edgeConfig.type = "ref";
165
+ edgeConfig.table = edgeTableName;
166
+ edgeConfig.field = forwardId;
167
+ edgeConfig.ref = inverseId;
168
+ edgeConfig.symmetric = inverseEdge === void 0;
150
169
  if (inverseEdge !== void 0) {
151
170
  inverseEdge.type = "ref";
152
- inverseEdge.table = edgeTableName;
153
- inverseEdge.field = inverseId;
154
- inverseEdge.ref = forwardId;
155
- inverseEdge.symmetric = false;
171
+ const inverseEdgeConfig = inverseEdge;
172
+ inverseEdgeConfig.table = edgeTableName;
173
+ inverseEdgeConfig.field = inverseId;
174
+ inverseEdgeConfig.ref = forwardId;
175
+ inverseEdgeConfig.symmetric = false;
156
176
  }
157
177
  }
158
178
  }
@@ -301,7 +321,12 @@ var EntDefinitionImpl = class {
301
321
  throw new Error(`Duplicate edge "${edgeName}"`);
302
322
  }
303
323
  const to = options?.to ?? edgeName + "s";
304
- if (options?.field !== void 0 || options?.optional !== true) {
324
+ if (options?.field !== void 0 && options?.ref !== void 0) {
325
+ throw new Error(
326
+ `Cannot specify both \`field\` and \`ref\` for the same edge, choose one to be the reference and the other to store the foreign key.`
327
+ );
328
+ }
329
+ if (options?.field !== void 0 || options?.ref === void 0) {
305
330
  const fieldName = options?.field ?? edgeName + "Id";
306
331
  this.documentSchema = {
307
332
  ...this.documentSchema,
@@ -313,7 +338,8 @@ var EntDefinitionImpl = class {
313
338
  cardinality: "single",
314
339
  type: "field",
315
340
  field: fieldName,
316
- optional: options?.optional === true
341
+ optional: options?.optional === true,
342
+ deletion: options?.deletion
317
343
  };
318
344
  this.indexes.push({
319
345
  indexDescriptor: fieldName,
@@ -321,15 +347,14 @@ var EntDefinitionImpl = class {
321
347
  });
322
348
  return this;
323
349
  }
324
- if (options.optional === true) {
325
- this.edgeConfigs[edgeName] = {
326
- name: edgeName,
327
- to,
328
- cardinality: "single",
329
- type: "ref",
330
- ref: options.ref ?? null
331
- };
332
- }
350
+ this.edgeConfigs[edgeName] = {
351
+ name: edgeName,
352
+ to,
353
+ cardinality: "single",
354
+ type: "ref",
355
+ ref: options.ref === true ? null : options.ref,
356
+ deletion: options.deletion
357
+ };
333
358
  return this;
334
359
  }
335
360
  edges(name, options) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema.ts"],"sourcesContent":["import {\n DataModelFromSchemaDefinition,\n DefineSchemaOptions,\n GenericDataModel,\n GenericTableIndexes,\n GenericTableSearchIndexes,\n GenericTableVectorIndexes,\n SchemaDefinition,\n SearchIndexConfig,\n TableDefinition,\n VectorIndexConfig,\n defineSchema,\n} from \"convex/server\";\nimport {\n GenericId,\n GenericValidator,\n ObjectType,\n PropertyValidators,\n VAny,\n VFloat64,\n VId,\n VObject,\n VOptional,\n Validator,\n v,\n} from \"convex/values\";\n\nexport function defineEntSchema<\n Schema extends Record<string, EntDefinition>,\n StrictTableNameTypes extends boolean = true,\n>(\n schema: Schema,\n options?: DefineSchemaOptions<StrictTableNameTypes>,\n): SchemaDefinition<Schema, StrictTableNameTypes> {\n // Set the properties of edges which requires knowing their inverses,\n // and add edge tables.\n const tableNames = Object.keys(schema);\n for (const tableName of tableNames) {\n const table = schema[tableName];\n for (const edge of edgeConfigsBeforeDefineSchema(table)) {\n if (\n // Skip inverse edges, we process their forward edges\n (edge.cardinality === \"multiple\" &&\n edge.type === \"ref\" &&\n edge.inverse !== undefined) ||\n // symmetric is only set by defineEntSchema,\n // so we already processed the pair\n (edge as any).symmetric !== undefined\n ) {\n continue;\n }\n\n const otherTableName = edge.to;\n const otherTable = schema[otherTableName];\n if (otherTable === undefined) {\n throw new Error(\n `Edge \"${edge.name}\" in table \"${tableName}\" ` +\n `points to an undefined table \"${otherTableName}\"`,\n );\n }\n\n const isSelfDirected = edge.to === tableName;\n\n const inverseEdgeCandidates = edgeConfigsBeforeDefineSchema(\n otherTable,\n ).filter(canBeInverseEdge(tableName, edge, isSelfDirected));\n if (inverseEdgeCandidates.length > 1) {\n throw new Error(\n `Edge \"${edge.name}\" in table \"${tableName}\" ` +\n `has too many potential inverse edges in table \"${otherTableName}\": ` +\n `${inverseEdgeCandidates\n .map((edge) => `\"${edge.name}\"`)\n .join(\", \")}`,\n );\n }\n const inverseEdge: EdgeConfigBeforeDefineSchema | undefined =\n inverseEdgeCandidates[0];\n\n if (\n edge.cardinality === \"single\" &&\n edge.type === \"field\" &&\n inverseEdge === undefined\n ) {\n throw new Error(\n `Missing inverse edge in table \"${otherTableName}\" ` +\n `for edge \"${edge.name}\" in table \"${tableName}\"`,\n );\n }\n\n // Default `ref` on the multiple end of the edge,\n if (edge.cardinality === \"single\" && edge.type === \"ref\") {\n if (inverseEdge === undefined) {\n throw new Error(\n `Missing inverse edge in table \"${otherTableName}\" ${\n edge.ref !== null ? `with field \"${edge.ref}\" ` : \"\"\n }for edge \"${edge.name}\" in table \"${tableName}\"`,\n );\n }\n if (\n inverseEdge.cardinality === \"single\" &&\n inverseEdge.type === \"ref\"\n ) {\n throw new Error(\n `Both edge \"${edge.name}\" in table \"${inverseEdge.to}\" and ` +\n `edge \"${inverseEdge.name}\" in table \"${edge.to}\" are marked ` +\n `as optional, choose one to be required.`,\n );\n }\n if (\n inverseEdge.cardinality !== \"single\" ||\n inverseEdge.type !== \"field\"\n ) {\n throw new Error(\n `Unexpected inverse edge type ${edge.name}, ${inverseEdge?.name}`,\n );\n }\n if (edge.ref === null) {\n (edge as any).ref = inverseEdge.field;\n }\n // For now the the non-optional end is always unique\n (inverseEdge as any).unique = true;\n }\n if (\n (edge.cardinality === \"single\" && edge.type === \"ref\") ||\n (edge.cardinality === \"multiple\" && edge.type === \"field\")\n ) {\n if (\n edge.deletion !== undefined &&\n deletionConfigFromEntDefinition(otherTable) === undefined\n ) {\n throw new Error(\n `Cannot specify soft deletion behavior for edge ` +\n `\"${edge.name}\" in table \"${tableName}\" ` +\n `because the target table \"${otherTableName}\" does not have ` +\n `a \"soft\" or \"scheduled\" deletion behavior ` +\n `configured.`,\n );\n }\n }\n if (edge.cardinality === \"multiple\") {\n if (!isSelfDirected && inverseEdge === undefined) {\n throw new Error(\n `Missing inverse edge in table \"${otherTableName}\" ` +\n `for edge \"${edge.name}\" in table \"${tableName}\"`,\n );\n }\n\n if (inverseEdge?.cardinality === \"single\") {\n if (inverseEdge.type === \"ref\") {\n throw new Error(\n `The edge \"${inverseEdge.name}\" in table \"${otherTableName}\" ` +\n `cannot be optional, as it must store the 1:many edge as a field. ` +\n `Check the its inverse edge \"${edge.name}\" in table \"${tableName}\".`,\n );\n }\n if (edge.type === \"ref\") {\n throw new Error(\n `The edge \"${inverseEdge.name}\" in table \"${otherTableName}\" ` +\n `cannot be singular, as the edge \"${edge.name}\" in table \"${tableName}\" did not ` +\n `specify the \\`ref\\` option.`,\n );\n }\n (edge as any).type = \"field\";\n (edge as any).ref = inverseEdge.field;\n }\n\n if (inverseEdge?.cardinality === \"multiple\" || isSelfDirected) {\n if (!isSelfDirected && edge?.type === \"field\") {\n throw new Error(\n `The edge \"${edge.name}\" in table \"${tableName}\" ` +\n `specified \\`ref\\`, but its inverse edge \"${inverseEdge.name}\" ` +\n `in table \"${otherTableName}\" is not the singular end of a 1:many edge.`,\n );\n }\n if (inverseEdge?.type === \"field\") {\n throw new Error(\n `The edge \"${inverseEdge.name}\" in table \"${otherTableName}\" ` +\n `specified \\`ref\\`, but its inverse edge \"${edge.name}\" ` +\n `in table \"${tableName}\" is not the singular end of a 1:many edge.`,\n );\n }\n\n const edgeTableName =\n edge.type === \"ref\" && edge.table !== undefined\n ? edge.table\n : inverseEdge === undefined\n ? `${tableName}_${edge.name}`\n : inverseEdge.name !== tableName\n ? `${tableName}_${inverseEdge.name}_to_${edge.name}`\n : `${inverseEdge.name}_to_${edge.name}`;\n\n const forwardId =\n edge.type === \"ref\" && edge.field !== undefined\n ? edge.field\n : inverseEdge === undefined\n ? \"aId\"\n : tableName === otherTableName\n ? inverseEdge.name + \"Id\"\n : tableName + \"Id\";\n const inverseId =\n isSelfDirected &&\n edge.type === \"ref\" &&\n edge.inverseField !== undefined\n ? edge.inverseField\n : inverseEdge === undefined\n ? \"bId\"\n : inverseEdge.type === \"ref\" && inverseEdge.field !== undefined\n ? inverseEdge.field\n : tableName === otherTableName\n ? edge.name + \"Id\"\n : otherTableName + \"Id\";\n // Add the table\n const edgeTable = defineEnt({\n [forwardId]: v.id(tableName),\n [inverseId]: v.id(otherTableName),\n })\n .index(forwardId, [forwardId])\n .index(inverseId, [inverseId])\n .index(edgeCompoundIndexNameRaw(forwardId, inverseId), [\n forwardId,\n inverseId,\n ]);\n const isSymmetric = inverseEdge === undefined;\n if (!isSymmetric) {\n edgeTable.index(edgeCompoundIndexNameRaw(inverseId, forwardId), [\n inverseId,\n forwardId,\n ]);\n }\n (schema as any)[edgeTableName] = edgeTable;\n (edge as any).type = \"ref\";\n (edge as any).table = edgeTableName;\n (edge as any).field = forwardId;\n (edge as any).ref = inverseId;\n (edge as any).symmetric = inverseEdge === undefined;\n if (inverseEdge !== undefined) {\n inverseEdge.type = \"ref\";\n (inverseEdge as any).table = edgeTableName;\n (inverseEdge as any).field = inverseId;\n (inverseEdge as any).ref = forwardId;\n (inverseEdge as any).symmetric = false;\n }\n }\n }\n }\n }\n return defineSchema(schema, options);\n}\n\nexport function edgeCompoundIndexName(\n edgeDefinition: EdgeConfig & { cardinality: \"multiple\"; type: \"ref\" },\n) {\n return edgeCompoundIndexNameRaw(edgeDefinition.field, edgeDefinition.ref);\n}\n\nfunction edgeCompoundIndexNameRaw(idA: string, idB: string) {\n return `${idA}_${idB}`;\n}\n\nfunction canBeInverseEdge(\n tableName: string,\n edge: EdgeConfigBeforeDefineSchema,\n isSelfDirected: boolean,\n) {\n return (candidate: EdgeConfigBeforeDefineSchema) => {\n if (candidate.to !== tableName) {\n return false;\n }\n // Simple: pick out explicit inverse edges\n if (isSelfDirected) {\n return (\n candidate.cardinality === \"multiple\" &&\n candidate.type === \"ref\" &&\n candidate.inverse === edge.name\n );\n }\n // If both ref and field are known, only consider matching edges (from the ref side)\n if (\n (edge.cardinality === \"single\" &&\n edge.type === \"ref\" &&\n edge.ref !== null) ||\n (edge.cardinality === \"multiple\" &&\n edge.type === \"field\" &&\n edge.ref !== true)\n ) {\n if (candidate.cardinality === \"single\" && candidate.type === \"field\") {\n return edge.ref === candidate.field;\n }\n }\n // If both ref and field are known, only consider matching edges (from the field side)\n if (\n edge.cardinality === \"single\" &&\n edge.type === \"field\" &&\n edge.field !== null\n ) {\n if (\n (candidate.cardinality === \"single\" &&\n candidate.type === \"ref\" &&\n candidate.ref !== null) ||\n (candidate.cardinality === \"multiple\" &&\n candidate.type === \"field\" &&\n candidate.ref !== true)\n ) {\n return edge.field === candidate.ref;\n }\n }\n\n // If table is known on both ends, only consider matching edges\n if (\n edge.cardinality === \"multiple\" &&\n edge.type === \"ref\" &&\n edge.table !== undefined\n ) {\n return (\n candidate.cardinality === \"multiple\" &&\n candidate.type === \"ref\" &&\n edge.table === candidate.table\n );\n }\n if (\n candidate.cardinality === \"multiple\" &&\n candidate.type === \"ref\" &&\n candidate.table !== undefined\n ) {\n return (\n edge.cardinality === \"multiple\" &&\n edge.type === \"ref\" &&\n edge.table === candidate.table\n );\n }\n return true;\n };\n}\n\nfunction edgeConfigsBeforeDefineSchema(table: EntDefinition) {\n return Object.values(\n (table as any).edgeConfigs as Record<string, EdgeConfigBeforeDefineSchema>,\n );\n}\n\nfunction deletionConfigFromEntDefinition(table: EntDefinition) {\n return (table as any).deletionConfig as DeletionConfig | undefined;\n}\n\nexport function defineEnt<DocumentSchema extends PropertyValidators>(\n documentSchema: DocumentSchema,\n): EntDefinition<ObjectValidator<DocumentSchema>> {\n return new EntDefinitionImpl(documentSchema) as any;\n}\n\nexport function defineEntFromTable<\n DocumentType extends GenericValidator = GenericValidator,\n // eslint-disable-next-line @typescript-eslint/ban-types\n Indexes extends GenericTableIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n SearchIndexes extends GenericTableSearchIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n VectorIndexes extends GenericTableVectorIndexes = {},\n>(\n definition: TableDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes\n >,\n): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes> {\n const validator: DocumentType = definition.validator;\n if (validator.kind !== \"object\") {\n throw new Error(\n \"Only tables with object definition are supported in Ents, not unions\",\n );\n }\n\n const entDefinition = defineEnt(validator.fields);\n // @ts-expect-error Private fields\n entDefinition.indexes = definition.indexes;\n // @ts-expect-error Private fields\n entDefinition.searchIndexes = definition.searchIndexes;\n // @ts-expect-error Private fields\n entDefinition.vectorIndexes = definition.vectorIndexes;\n return entDefinition as any;\n}\n\ntype DefineEntFromTables<\n T extends { [key: string]: TableDefinition<any, any, any, any> },\n> = {\n [K in keyof T]: T[K] extends TableDefinition<\n infer D,\n infer I,\n infer S,\n infer V\n >\n ? EntDefinition<D, I, S, V>\n : never;\n};\n\nexport function defineEntsFromTables<\n T extends { [key: string]: TableDefinition<any, any, any, any> },\n>(definitions: T): DefineEntFromTables<T> {\n const result: any = {};\n for (const key in definitions) {\n result[key] = defineEntFromTable(definitions[key]);\n }\n return result;\n}\n\ntype GenericEdges = Record<string, GenericEdgeConfig>;\n\nexport type GenericEdgeConfig = {\n name: string;\n to: string;\n cardinality: \"single\" | \"multiple\";\n type: \"field\" | \"ref\";\n optional?: boolean;\n};\n\ntype ExtractFieldPaths<T extends Validator<any, any, any>> =\n // Add in the system fields available in index definitions.\n // This should be everything except for `_id` because thats added to indexes\n // automatically.\n T[\"fieldPaths\"] | keyof SystemFields;\n\ntype ObjectFieldType<\n FieldName extends string,\n T extends Validator<any, any, any>,\n> = T[\"isOptional\"] extends \"optional\"\n ? { [key in FieldName]?: T[\"type\"] }\n : { [key in FieldName]: T[\"type\"] };\n\ntype AddField<\n V extends GenericValidator,\n FieldName extends string,\n P extends GenericValidator,\n> =\n // Note: We can't use the `AddField` type to add fields to a union type, but ents\n // do not support schemas with top level unions\n V extends VObject<\n infer TypeScriptType,\n infer Fields,\n infer IsOptional,\n infer FieldPaths\n >\n ? VObject<\n Expand<TypeScriptType & ObjectFieldType<FieldName, P>>,\n Expand<Fields & { FieldName: P }>,\n IsOptional,\n FieldPaths | FieldName\n >\n : V extends VAny\n ? VAny\n : never;\n\nexport interface EntDefinition<\n DocumentType extends Validator<any, any, any> = Validator<any, any, any>,\n // eslint-disable-next-line @typescript-eslint/ban-types\n Indexes extends GenericTableIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n SearchIndexes extends GenericTableSearchIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n VectorIndexes extends GenericTableVectorIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n Edges extends GenericEdges = {},\n> extends TableDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes> {\n /**\n * Define an index on this table.\n *\n * To learn about indexes, see [Defining Indexes](https://docs.convex.dev/using/indexes).\n *\n * @param name - The name of the index.\n * @param fields - The fields to index, in order. Must specify at least one\n * field.\n * @returns A {@link TableDefinition} with this index included.\n */\n index<\n IndexName extends string,\n FirstFieldPath extends ExtractFieldPaths<DocumentType>,\n RestFieldPaths extends ExtractFieldPaths<DocumentType>[],\n >(\n name: IndexName,\n fields: [FirstFieldPath, ...RestFieldPaths],\n ): EntDefinition<\n DocumentType,\n Expand<\n Indexes &\n Record<IndexName, [FirstFieldPath, ...RestFieldPaths, \"_creationTime\"]>\n >,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n\n /**\n * Define a search index on this table.\n *\n * To learn about search indexes, see [Search](https://docs.convex.dev/text-search).\n *\n * @param name - The name of the index.\n * @param indexConfig - The search index configuration object.\n * @returns A {@link TableDefinition} with this search index included.\n */\n searchIndex<\n IndexName extends string,\n SearchField extends ExtractFieldPaths<DocumentType>,\n FilterFields extends ExtractFieldPaths<DocumentType> = never,\n >(\n name: IndexName,\n indexConfig: Expand<SearchIndexConfig<SearchField, FilterFields>>,\n ): EntDefinition<\n DocumentType,\n Indexes,\n Expand<\n SearchIndexes &\n Record<\n IndexName,\n {\n searchField: SearchField;\n filterFields: FilterFields;\n }\n >\n >,\n VectorIndexes,\n Edges\n >;\n\n // /**\n // * Define a vector index on this table.\n // *\n // * To learn about vector indexes, see [Vector Search](https://docs.convex.dev/vector-search).\n // *\n // * @param name - The name of the index.\n // * @param indexConfig - The vector index configuration object.\n // * @returns A {@link TableDefinition} with this vector index included.\n // */\n vectorIndex<\n IndexName extends string,\n VectorField extends ExtractFieldPaths<DocumentType>,\n FilterFields extends ExtractFieldPaths<DocumentType> = never,\n >(\n name: IndexName,\n indexConfig: Expand<VectorIndexConfig<VectorField, FilterFields>>,\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n Expand<\n VectorIndexes &\n Record<\n IndexName,\n {\n vectorField: VectorField;\n dimensions: number;\n filterFields: FilterFields;\n }\n >\n >,\n Edges\n >;\n\n field<FieldName extends string, T extends GenericValidator>(\n field: FieldName,\n validator: T,\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n field<FieldName extends string, T extends Validator<any, any, any>>(\n field: FieldName,\n validator: T,\n options: { index: true },\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes & { [key in FieldName]: [FieldName, \"_creationTime\"] },\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n field<FieldName extends string, T extends Validator<any, any, any>>(\n field: FieldName,\n validator: T,\n options: { unique: true },\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes & { [key in FieldName]: [FieldName, \"_creationTime\"] },\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n field<FieldName extends string, T extends Validator<any, \"required\", any>>(\n field: FieldName,\n validator: T,\n options: { default: T[\"type\"] },\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n\n edge<EdgeName extends string>(\n edge: EdgeName,\n ): EntDefinition<\n AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${EdgeName}s`>>>,\n Indexes & { [key in `${EdgeName}Id`]: [`${EdgeName}Id`, \"_creationTime\"] },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"field\";\n cardinality: \"single\";\n optional: false;\n };\n }\n >;\n edge<EdgeName extends string, const FieldName extends string>(\n edge: EdgeName,\n options: { field: FieldName },\n ): EntDefinition<\n AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${EdgeName}s`>>>,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"field\";\n cardinality: \"single\";\n optional: false;\n };\n }\n >;\n edge<EdgeName extends string, const FieldName extends string>(\n edge: EdgeName,\n options: { field: FieldName; optional: true },\n ): EntDefinition<\n AddField<\n DocumentType,\n NoInfer<FieldName>,\n VOptional<VId<GenericId<`${EdgeName}s`>>>\n >,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"field\";\n cardinality: \"single\";\n optional: true;\n };\n }\n >;\n edge<\n EdgeName extends string,\n const FieldName extends string,\n const ToTable extends string,\n >(\n edge: EdgeName,\n options: { field: FieldName; to: ToTable },\n ): EntDefinition<\n AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${ToTable}`>>>,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: ToTable;\n type: \"field\";\n cardinality: \"single\";\n optional: false;\n };\n }\n >;\n edge<\n EdgeName extends string,\n const FieldName extends string,\n const ToTable extends string,\n >(\n edge: EdgeName,\n options: { field: FieldName; to: ToTable; optional: true },\n ): EntDefinition<\n AddField<\n DocumentType,\n NoInfer<FieldName>,\n VOptional<VId<GenericId<`${EdgeName}s`>>>\n >,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: ToTable;\n type: \"field\";\n cardinality: \"single\";\n optional: true;\n };\n }\n >;\n edge<EdgeName extends string>(\n edge: EdgeName,\n options: {\n optional: true;\n ref?: string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"ref\";\n cardinality: \"single\";\n };\n }\n >;\n edge<EdgeName extends string, const ToTable extends string>(\n edge: EdgeName,\n options: {\n optional: true;\n to: ToTable;\n ref?: string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: NoInfer<ToTable>;\n type: \"ref\";\n cardinality: \"single\";\n };\n }\n >;\n\n /**\n * Define many:1 edge to another table.\n * @param edge The name of the edge, also the name of the target table.\n * @param options.ref The name of the field that stores the many:1 edge\n * on the other table, or `true` to infer it.\n */\n edges<EdgesName extends string>(\n edge: EdgesName,\n options: {\n ref: true | string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: EdgesName;\n type: \"field\";\n cardinality: \"multiple\";\n };\n }\n >;\n /**\n * Define many:1 edge to another table.\n * @param edge The name of the edge.\n * @param options.to Name of the table the edge points to.\n * If it's the same as the table this edge is defined on, this edge is\n * a symmetric, self-directed many:many edge.\n * @param options.ref The name of the field that stores the many:1 edge\n * on the other table, or `true` to infer it.\n */\n edges<EdgesName extends string, TableName extends string>(\n edge: EdgesName,\n options: {\n to: TableName;\n ref: true | string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: NoInfer<TableName>;\n type: \"field\";\n cardinality: \"multiple\";\n };\n }\n >;\n\n /**\n * Define many:many edge to another table.\n * @param edge The name of the edge, also the name of the target table.\n * @param options.table Optional, name of the table to store the many:many edge in.\n * @param options.field Optional, name of the field to store the ID of the\n * this end of the many:many edge.\n */\n edges<EdgesName extends string>(\n edge: EdgesName,\n options?: {\n table?: string;\n field?: string;\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: EdgesName;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n }\n >;\n /**\n * Define many:many edge to another table.\n * @param edge The name of the edge.\n * @param options.to Name of the table the edge points to.\n * If it's the same as the table this edge is defined on, this edge is\n * a symmetric, self-directed many:many edge.\n * @param options.table Optional, name of the table to store the many:many edge in.\n * @param options.field Optional, name of the field to store the ID of the\n * of the source end of the forward many:many edge.\n * @param options.inverseField Optional, name of the field to store the ID\n * of the target end of the forward edge. Only allowed for symmetric,\n * self-directed many:many edges.\n */\n edges<EdgesName extends string, TableName extends string>(\n edge: EdgesName,\n options: {\n to: TableName;\n table?: string;\n field?: string;\n inverseField?: string;\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: NoInfer<TableName>;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n }\n >;\n /**\n * Define self-directed, assymetric, many:many edge.\n * @param edge The name of the edge.\n * @param options.to Name of the table the edge points to.\n * Must be the same as the table this edge is defined on.\n * @param options.inverse Name of the inverse edge.\n * @param options.table Optional, name of the table to store the many:many edge in.\n * @param options.field Optional, name of the field to store the ID of the\n * of the source end of the forward many:many edge.\n * @param options.inverseField Optional, name of the field to store the ID\n * of the target end of the forward many:many edge.\n */\n edges<\n EdgesName extends string,\n TableName extends string,\n InverseEdgesNames extends string,\n >(\n edge: EdgesName,\n options: {\n to: TableName;\n inverse: InverseEdgesNames;\n table?: string;\n field?: string;\n inverseField?: string;\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: NoInfer<TableName>;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n } & {\n [key in NoInfer<InverseEdgesNames>]: {\n name: NoInfer<InverseEdgesNames>;\n to: NoInfer<TableName>;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n }\n >;\n\n /**\n * Add the \"soft\" deletion behavior to this ent.\n *\n * When the ent is \"soft\" deleted, its `deletionTime` field is set to the\n * current time and it is not actually deleted.\n *\n * @param type `\"soft\"`\n */\n deletion(\n type: \"soft\",\n ): EntDefinition<\n AddField<DocumentType, \"deletionTime\", VOptional<VFloat64>>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n /**\n * Add the \"scheduled\" deletion behavior to this ent.\n *\n * The ent is first \"soft\" deleted and its hard deletion is scheduled\n * to run in a separate mutation.\n *\n * @param type `\"scheduled\"`\n * @param options.delayMs If the `delayMs` option is specified,\n * the hard deletion is scheduled to happen after the specified\n * time duration.\n */\n deletion(\n type: \"scheduled\",\n options?: {\n delayMs: number;\n },\n ): EntDefinition<\n AddField<DocumentType, \"deletionTime\", VOptional<VFloat64>>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n}\n\ntype NoInfer<T> = [T][T extends any ? 0 : never];\n\ntype FieldOptions = {\n index?: true;\n unique?: true;\n default?: any;\n};\n\ntype EdgeOptions = {\n optional?: true;\n field?: string;\n ref?: string;\n to?: string;\n};\n\ntype EdgesOptions = {\n to?: string;\n inverse?: string;\n ref?: string;\n table?: string;\n field?: string;\n inverseField?: string;\n deletion: \"soft\";\n};\n\nclass EntDefinitionImpl {\n validator: GenericValidator;\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n private indexes: Index[] = [];\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n private searchIndexes: SearchIndex[] = [];\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n private vectorIndexes: VectorIndex[] = [];\n\n private documentSchema: Record<string, Validator<any, any, any>>;\n\n private edgeConfigs: Record<string, EdgeConfigBeforeDefineSchema> = {};\n\n private fieldConfigs: Record<string, FieldConfig> = {};\n\n private defaults: Record<string, any> = {};\n\n private deletionConfig: DeletionConfig | undefined;\n\n constructor(documentSchema: Record<string, Validator<any, any, any>>) {\n this.documentSchema = documentSchema;\n this.validator = v.object(documentSchema);\n }\n\n index(name: any, fields: any) {\n this.indexes.push({ indexDescriptor: name, fields });\n return this;\n }\n\n searchIndex(name: any, indexConfig: any) {\n this.searchIndexes.push({\n indexDescriptor: name,\n searchField: indexConfig.searchField,\n filterFields: indexConfig.filterFields || [],\n });\n return this;\n }\n\n vectorIndex(name: any, indexConfig: any) {\n this.vectorIndexes.push({\n indexDescriptor: name,\n vectorField: indexConfig.vectorField,\n dimensions: indexConfig.dimensions,\n filterFields: indexConfig.filterFields || [],\n });\n return this;\n }\n\n /**\n * Export the contents of this definition.\n *\n * This is called internally by the Convex framework.\n * @internal\n */\n export() {\n return {\n indexes: this.indexes,\n searchIndexes: this.searchIndexes,\n vectorIndexes: this.vectorIndexes,\n documentType: (v.object(this.documentSchema) as any).json,\n };\n }\n\n field(name: string, validator: any, options?: FieldOptions): this {\n if (this.documentSchema[name] !== undefined) {\n // TODO: Store the fieldConfigs in an array so that we can\n // do the uniqueness check in defineEntSchema where we\n // know the table name.\n throw new Error(`Duplicate field \"${name}\"`);\n }\n const finalValidator =\n options?.default !== undefined ? v.optional(validator) : validator;\n this.documentSchema = { ...this.documentSchema, [name]: finalValidator };\n if (options?.unique === true || options?.index === true) {\n this.indexes.push({ indexDescriptor: name, fields: [name] });\n }\n if (options?.default !== undefined) {\n this.defaults[name] = options.default;\n }\n if (options?.unique === true) {\n this.fieldConfigs[name] = { name, unique: true };\n }\n return this;\n }\n\n edge(edgeName: string, options?: EdgeOptions): this {\n if (this.edgeConfigs[edgeName] !== undefined) {\n // TODO: Store the edgeConfigs in an array so that we can\n // do the uniqueness check in defineEntSchema where we\n // know the source table name.\n throw new Error(`Duplicate edge \"${edgeName}\"`);\n }\n const to = options?.to ?? edgeName + \"s\";\n if (options?.field !== undefined || options?.optional !== true) {\n const fieldName = options?.field ?? edgeName + \"Id\";\n this.documentSchema = {\n ...this.documentSchema,\n [fieldName]:\n options?.optional === true ? v.optional(v.id(to)) : v.id(to),\n };\n this.edgeConfigs[edgeName] = {\n name: edgeName,\n to,\n cardinality: \"single\",\n type: \"field\",\n field: fieldName,\n optional: options?.optional === true,\n };\n this.indexes.push({\n indexDescriptor: fieldName,\n fields: [fieldName],\n });\n return this;\n }\n if (options.optional === true) {\n this.edgeConfigs[edgeName] = {\n name: edgeName,\n to,\n cardinality: \"single\",\n type: \"ref\",\n ref: options.ref ?? null,\n };\n }\n return this;\n }\n\n edges(name: string, options?: EdgesOptions): this {\n const cardinality = \"multiple\";\n const to = options?.to ?? name;\n const ref = options?.ref;\n const table = options?.table;\n // TODO: Do this later when we have the table name,\n // or rework schema to use a builder pattern.\n if (ref !== undefined && table !== undefined) {\n throw new Error(\n `Cannot specify both \\`ref\\` and \\`table\\` for the same edge, ` +\n `as the former is for 1:many edges and the latter ` +\n `for many:many edges. Config: \\`${JSON.stringify(options)}\\``,\n );\n }\n const field = options?.field;\n const inverseField = options?.inverseField;\n // TODO: Do this later when we have the table name,\n // or rework schema to use a builder pattern.\n if (\n (field !== undefined || inverseField !== undefined) &&\n table === undefined\n ) {\n throw new Error(\n `Specify \\`table\\` if you're customizing the \\`field\\` or ` +\n `\\`inverseField\\` for a many:many edge. ` +\n `Config: \\`${JSON.stringify(options)}\\``,\n );\n }\n const inverseName = options?.inverse;\n const deletion = options?.deletion;\n this.edgeConfigs[name] =\n ref !== undefined\n ? { name, to, cardinality, type: \"field\", ref, deletion }\n : { name, to, cardinality, type: \"ref\", table, field, inverseField };\n if (inverseName !== undefined) {\n this.edgeConfigs[inverseName] = {\n name: inverseName,\n to,\n cardinality,\n type: \"ref\",\n inverse: name,\n table,\n };\n }\n return this;\n }\n\n deletion(type: \"soft\" | \"scheduled\", options?: { delayMs: number }): this {\n if (this.documentSchema.deletionTime !== undefined) {\n // TODO: Put the check where we know the table name.\n throw new Error(\n `Cannot enable \"${type}\" deletion because \"deletionTime\" field ` +\n `was already defined.`,\n );\n }\n if (this.deletionConfig !== undefined) {\n // TODO: Put the check where we know the table name.\n throw new Error(`Deletion behavior can only be specified once.`);\n }\n this.documentSchema = {\n ...this.documentSchema,\n deletionTime: v.optional(v.number()),\n };\n this.deletionConfig = { type, ...options };\n return this;\n }\n}\n\nexport type EdgeConfig = {\n name: string;\n to: string;\n} & (\n | ({\n cardinality: \"single\";\n } & (\n | {\n type: \"field\";\n field: string;\n unique: boolean;\n optional: boolean;\n }\n | {\n type: \"ref\";\n ref: string;\n deletion?: \"soft\";\n }\n ))\n | ({\n cardinality: \"multiple\";\n } & (\n | {\n type: \"field\";\n ref: string;\n deletion?: \"soft\";\n }\n | {\n type: \"ref\";\n table: string;\n field: string;\n ref: string;\n inverse: boolean;\n symmetric: boolean;\n }\n ))\n);\n\ntype EdgeConfigBeforeDefineSchema = {\n name: string;\n to: string;\n} & (\n | ({\n cardinality: \"single\";\n } & (\n | {\n type: \"field\";\n field: string;\n optional: boolean;\n }\n | {\n type: \"ref\";\n ref: null | string;\n deletion?: \"soft\";\n }\n ))\n | ({\n cardinality: \"multiple\";\n } & (\n | {\n type: \"field\";\n ref: true | string;\n deletion?: \"soft\";\n }\n | {\n type: \"ref\";\n table?: string;\n field?: string;\n inverseField?: string;\n inverse?: string;\n }\n ))\n);\n\nexport type FieldConfig = {\n name: string;\n unique: boolean;\n};\n\nexport type Expand<ObjectType extends Record<any, any>> =\n ObjectType extends Record<any, any>\n ? {\n [Key in keyof ObjectType]: ObjectType[Key];\n }\n : never;\nexport type SystemFields = {\n _creationTime: number;\n};\n\ntype ObjectValidator<Validators extends PropertyValidators> = VObject<\n // Compute the TypeScript type this validator refers to.\n ObjectType<Validators>,\n Validators\n>;\n\nexport type GenericEntsDataModel = GenericDataModel &\n Record<string, GenericEntModel>;\n\nexport type GenericEntModel = {\n edges: Record<string, GenericEdgeConfig>;\n};\n\nexport type DeletionConfig =\n | {\n type: \"soft\";\n }\n | {\n type: \"scheduled\";\n delayMs?: number;\n };\n\nexport type EntDataModelFromSchema<\n SchemaDef extends SchemaDefinition<any, boolean>,\n> = DataModelFromSchemaDefinition<SchemaDef> & {\n [TableName in keyof SchemaDef[\"tables\"] &\n string]: SchemaDef[\"tables\"][TableName] extends EntDefinition<\n any,\n any,\n any,\n any,\n infer Edges\n >\n ? {\n edges: Edges;\n }\n : never;\n};\n\nexport function getEntDefinitions<\n SchemaDef extends SchemaDefinition<any, boolean>,\n>(schema: SchemaDef): EntDataModelFromSchema<typeof schema> {\n const tables = schema.tables;\n return Object.entries(tables).reduce(\n (acc, [tableName, table]: [any, any]) => {\n acc[tableName] = {\n indexes: (\n table.indexes as {\n indexDescriptor: string;\n fields: string[];\n }[]\n ).reduce(\n (acc, { indexDescriptor, fields }) => {\n acc[indexDescriptor] = fields;\n return acc;\n },\n {} as Record<string, string[]>,\n ),\n defaults: table.defaults,\n edges: table.edgeConfigs,\n fields: table.fieldConfigs,\n deletionConfig: table.deletionConfig,\n };\n return acc;\n },\n {} as Record<string, any>,\n ) as any;\n}\n"],"mappings":"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","names":["edge","acc"]}
1
+ {"version":3,"sources":["../src/schema.ts"],"sourcesContent":["import {\n DataModelFromSchemaDefinition,\n DefineSchemaOptions,\n GenericDataModel,\n GenericTableIndexes,\n GenericTableSearchIndexes,\n GenericTableVectorIndexes,\n SchemaDefinition,\n SearchIndexConfig,\n TableDefinition,\n VectorIndexConfig,\n defineSchema,\n} from \"convex/server\";\nimport {\n GenericId,\n GenericValidator,\n ObjectType,\n PropertyValidators,\n VAny,\n VFloat64,\n VId,\n VObject,\n VOptional,\n Validator,\n v,\n} from \"convex/values\";\n\nexport function defineEntSchema<\n Schema extends Record<string, EntDefinition>,\n StrictTableNameTypes extends boolean = true,\n>(\n schema: Schema,\n options?: DefineSchemaOptions<StrictTableNameTypes>,\n): SchemaDefinition<Schema, StrictTableNameTypes> {\n // Set the properties of edges which requires knowing their inverses,\n // and add edge tables.\n const tableNames = Object.keys(schema);\n for (const tableName of tableNames) {\n const table = schema[tableName];\n for (const edge of edgeConfigsBeforeDefineSchema(table)) {\n if (\n // Skip inverse edges, we process their forward edges\n edge.cardinality === \"multiple\" &&\n edge.type === \"ref\" &&\n (edge.inverse !== undefined ||\n // symmetric is only set by defineEntSchema,\n // so we already processed the pair\n (edge as unknown as EdgeConfigMultipleRef).symmetric !== undefined)\n ) {\n continue;\n }\n\n const otherTableName = edge.to;\n if (otherTableName.startsWith(\"_\")) {\n if (edge.cardinality !== \"single\") {\n throw new Error(\n `Many:many edge \"${edge.name}\" in table \"${tableName}\" ` +\n `points to a system table \"${otherTableName}\", but only 1:1 ` +\n `edges can point to system tables`,\n );\n }\n if (edge.type !== \"field\") {\n throw new Error(\n `Edge \"${edge.name}\" in table \"${tableName}\" ` +\n `pointing to a system table \"${otherTableName}\" must store ` +\n `the edge by storing the system document ID. Remove ` +\n `the \\`ref\\` option.`,\n );\n }\n if (edge.deletion === \"soft\") {\n throw new Error(\n `Edge \"${edge.name}\" in table \"${tableName}\" ` +\n `pointing to a system table \"${otherTableName}\" cannot use ` +\n `soft deletion, because system documents cannot be soft deleted.`,\n );\n }\n // Nothing else to do, the edge is set up.\n continue;\n }\n const otherTable = schema[otherTableName];\n if (otherTable === undefined) {\n throw new Error(\n `Edge \"${edge.name}\" in table \"${tableName}\" ` +\n `points to an undefined table \"${otherTableName}\"`,\n );\n }\n\n const isSelfDirected = edge.to === tableName;\n\n const inverseEdgeCandidates = edgeConfigsBeforeDefineSchema(\n otherTable,\n ).filter(canBeInverseEdge(tableName, edge, isSelfDirected));\n if (inverseEdgeCandidates.length > 1) {\n throw new Error(\n `Edge \"${edge.name}\" in table \"${tableName}\" ` +\n `has too many potential inverse edges in table \"${otherTableName}\": ` +\n `${inverseEdgeCandidates\n .map((edge) => `\"${edge.name}\"`)\n .join(\", \")}`,\n );\n }\n const inverseEdge: EdgeConfigBeforeDefineSchema | undefined =\n inverseEdgeCandidates[0];\n\n if (\n edge.cardinality === \"single\" &&\n edge.type === \"field\" &&\n inverseEdge === undefined\n ) {\n throw new Error(\n `Missing inverse edge in table \"${otherTableName}\" ` +\n `for edge \"${edge.name}\" in table \"${tableName}\"`,\n );\n }\n\n // Default `ref` on the multiple end of the edge,\n if (edge.cardinality === \"single\" && edge.type === \"ref\") {\n if (inverseEdge === undefined) {\n throw new Error(\n `Missing inverse edge in table \"${otherTableName}\" ${\n edge.ref !== null ? `with field \"${edge.ref}\" ` : \"\"\n }for edge \"${edge.name}\" in table \"${tableName}\"`,\n );\n }\n if (\n inverseEdge.cardinality === \"single\" &&\n inverseEdge.type === \"ref\"\n ) {\n throw new Error(\n `Both edge \"${edge.name}\" in table \"${inverseEdge.to}\" and ` +\n `edge \"${inverseEdge.name}\" in table \"${edge.to}\" are marked ` +\n `as references, choose one to store the edge by removing ` +\n `the \\`ref\\` option.`,\n );\n }\n if (\n inverseEdge.cardinality !== \"single\" ||\n inverseEdge.type !== \"field\"\n ) {\n throw new Error(\n `Unexpected inverse edge type ${edge.name}, ${inverseEdge?.name}`,\n );\n }\n if (edge.ref === null) {\n (edge as EdgeConfigSingleRef).ref = inverseEdge.field;\n }\n // For now the the non-optional end is always unique\n (inverseEdge as EdgeConfigSingleField).unique = true;\n }\n if (\n edge.cardinality === \"single\" ||\n (edge.cardinality === \"multiple\" && edge.type === \"field\")\n ) {\n if (\n edge.deletion !== undefined &&\n deletionConfigFromEntDefinition(otherTable) === undefined\n ) {\n throw new Error(\n `Cannot specify soft deletion behavior for edge ` +\n `\"${edge.name}\" in table \"${tableName}\" ` +\n `because the target table \"${otherTableName}\" does not have ` +\n `a \"soft\" or \"scheduled\" deletion behavior ` +\n `configured.`,\n );\n }\n }\n if (edge.cardinality === \"multiple\") {\n if (!isSelfDirected && inverseEdge === undefined) {\n throw new Error(\n `Missing inverse edge in table \"${otherTableName}\" ` +\n `for edge \"${edge.name}\" in table \"${tableName}\"`,\n );\n }\n\n if (inverseEdge?.cardinality === \"single\") {\n if (inverseEdge.type === \"ref\") {\n throw new Error(\n `The edge \"${inverseEdge.name}\" in table \"${otherTableName}\" ` +\n `specified \\`ref\\`, but it must store the 1:many edge as a field. ` +\n `Check the its inverse edge \"${edge.name}\" in table \"${tableName}\".`,\n );\n }\n if (edge.type === \"ref\") {\n throw new Error(\n `The edge \"${inverseEdge.name}\" in table \"${otherTableName}\" ` +\n `cannot be singular, as the edge \"${edge.name}\" in table \"${tableName}\" did not ` +\n `specify the \\`ref\\` option.`,\n );\n }\n (edge as EdgeConfigMultipleField).type = \"field\";\n (edge as EdgeConfigMultipleField).ref = inverseEdge.field;\n }\n\n if (inverseEdge?.cardinality === \"multiple\" || isSelfDirected) {\n if (!isSelfDirected && edge?.type === \"field\") {\n throw new Error(\n `The edge \"${edge.name}\" in table \"${tableName}\" ` +\n `specified \\`ref\\`, but its inverse edge \"${inverseEdge.name}\" ` +\n `in table \"${otherTableName}\" is not the singular end of a 1:many edge.`,\n );\n }\n if (inverseEdge?.type === \"field\") {\n throw new Error(\n `The edge \"${inverseEdge.name}\" in table \"${otherTableName}\" ` +\n `specified \\`ref\\`, but its inverse edge \"${edge.name}\" ` +\n `in table \"${tableName}\" is not the singular end of a 1:many edge.`,\n );\n }\n\n const edgeTableName =\n edge.type === \"ref\" && edge.table !== undefined\n ? edge.table\n : inverseEdge === undefined\n ? `${tableName}_${edge.name}`\n : inverseEdge.name !== tableName\n ? `${tableName}_${inverseEdge.name}_to_${edge.name}`\n : `${inverseEdge.name}_to_${edge.name}`;\n\n const forwardId =\n edge.type === \"ref\" && edge.field !== undefined\n ? edge.field\n : inverseEdge === undefined\n ? \"aId\"\n : tableName === otherTableName\n ? inverseEdge.name + \"Id\"\n : tableName + \"Id\";\n const inverseId =\n isSelfDirected &&\n edge.type === \"ref\" &&\n edge.inverseField !== undefined\n ? edge.inverseField\n : inverseEdge === undefined\n ? \"bId\"\n : inverseEdge.type === \"ref\" && inverseEdge.field !== undefined\n ? inverseEdge.field\n : tableName === otherTableName\n ? edge.name + \"Id\"\n : otherTableName + \"Id\";\n // Add the table\n const edgeTable = defineEnt({\n [forwardId]: v.id(tableName),\n [inverseId]: v.id(otherTableName),\n })\n .index(forwardId, [forwardId])\n .index(inverseId, [inverseId])\n .index(edgeCompoundIndexNameRaw(forwardId, inverseId), [\n forwardId,\n inverseId,\n ]);\n const isSymmetric = inverseEdge === undefined;\n if (!isSymmetric) {\n edgeTable.index(edgeCompoundIndexNameRaw(inverseId, forwardId), [\n inverseId,\n forwardId,\n ]);\n }\n (schema as Record<string, EntDefinition>)[edgeTableName] = edgeTable;\n const edgeConfig = edge as unknown as EdgeConfigMultipleRef;\n edgeConfig.type = \"ref\";\n edgeConfig.table = edgeTableName;\n edgeConfig.field = forwardId;\n edgeConfig.ref = inverseId;\n edgeConfig.symmetric = inverseEdge === undefined;\n if (inverseEdge !== undefined) {\n inverseEdge.type = \"ref\";\n const inverseEdgeConfig =\n inverseEdge as unknown as EdgeConfigMultipleRef;\n inverseEdgeConfig.table = edgeTableName;\n inverseEdgeConfig.field = inverseId;\n inverseEdgeConfig.ref = forwardId;\n inverseEdgeConfig.symmetric = false;\n }\n }\n }\n }\n }\n return defineSchema(schema, options);\n}\n\nexport function edgeCompoundIndexName(\n edgeDefinition: EdgeConfig & { cardinality: \"multiple\"; type: \"ref\" },\n) {\n return edgeCompoundIndexNameRaw(edgeDefinition.field, edgeDefinition.ref);\n}\n\nfunction edgeCompoundIndexNameRaw(idA: string, idB: string) {\n return `${idA}_${idB}`;\n}\n\nfunction canBeInverseEdge(\n tableName: string,\n edge: EdgeConfigBeforeDefineSchema,\n isSelfDirected: boolean,\n) {\n return (candidate: EdgeConfigBeforeDefineSchema) => {\n if (candidate.to !== tableName) {\n return false;\n }\n // Simple: pick out explicit inverse edges\n if (isSelfDirected) {\n return (\n candidate.cardinality === \"multiple\" &&\n candidate.type === \"ref\" &&\n candidate.inverse === edge.name\n );\n }\n // If both ref and field are known, only consider matching edges (from the ref side)\n if (\n (edge.cardinality === \"single\" &&\n edge.type === \"ref\" &&\n edge.ref !== null) ||\n (edge.cardinality === \"multiple\" &&\n edge.type === \"field\" &&\n edge.ref !== true)\n ) {\n if (candidate.cardinality === \"single\" && candidate.type === \"field\") {\n return edge.ref === candidate.field;\n }\n }\n // If both ref and field are known, only consider matching edges (from the field side)\n if (\n edge.cardinality === \"single\" &&\n edge.type === \"field\" &&\n edge.field !== null\n ) {\n if (\n (candidate.cardinality === \"single\" &&\n candidate.type === \"ref\" &&\n candidate.ref !== null) ||\n (candidate.cardinality === \"multiple\" &&\n candidate.type === \"field\" &&\n candidate.ref !== true)\n ) {\n return edge.field === candidate.ref;\n }\n }\n\n // If table is known on both ends, only consider matching edges\n if (\n edge.cardinality === \"multiple\" &&\n edge.type === \"ref\" &&\n edge.table !== undefined\n ) {\n return (\n candidate.cardinality === \"multiple\" &&\n candidate.type === \"ref\" &&\n edge.table === candidate.table\n );\n }\n if (\n candidate.cardinality === \"multiple\" &&\n candidate.type === \"ref\" &&\n candidate.table !== undefined\n ) {\n return (\n edge.cardinality === \"multiple\" &&\n edge.type === \"ref\" &&\n edge.table === candidate.table\n );\n }\n return true;\n };\n}\n\nfunction edgeConfigsBeforeDefineSchema(table: EntDefinition) {\n return Object.values(\n (table as any).edgeConfigs as Record<string, EdgeConfigBeforeDefineSchema>,\n );\n}\n\nfunction deletionConfigFromEntDefinition(table: EntDefinition) {\n return (table as any).deletionConfig as DeletionConfig | undefined;\n}\n\nexport function defineEnt<DocumentSchema extends PropertyValidators>(\n documentSchema: DocumentSchema,\n): EntDefinition<ObjectValidator<DocumentSchema>> {\n return new EntDefinitionImpl(documentSchema) as any;\n}\n\nexport function defineEntFromTable<\n DocumentType extends GenericValidator = GenericValidator,\n // eslint-disable-next-line @typescript-eslint/ban-types\n Indexes extends GenericTableIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n SearchIndexes extends GenericTableSearchIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n VectorIndexes extends GenericTableVectorIndexes = {},\n>(\n definition: TableDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes\n >,\n): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes> {\n const validator: DocumentType = definition.validator;\n if (validator.kind !== \"object\") {\n throw new Error(\n \"Only tables with object definition are supported in Ents, not unions\",\n );\n }\n\n const entDefinition = defineEnt(validator.fields);\n // @ts-expect-error Private fields\n entDefinition.indexes = definition.indexes;\n // @ts-expect-error Private fields\n entDefinition.searchIndexes = definition.searchIndexes;\n // @ts-expect-error Private fields\n entDefinition.vectorIndexes = definition.vectorIndexes;\n return entDefinition as any;\n}\n\ntype DefineEntFromTables<\n T extends { [key: string]: TableDefinition<any, any, any, any> },\n> = {\n [K in keyof T]: T[K] extends TableDefinition<\n infer D,\n infer I,\n infer S,\n infer V\n >\n ? EntDefinition<D, I, S, V>\n : never;\n};\n\nexport function defineEntsFromTables<\n T extends { [key: string]: TableDefinition<any, any, any, any> },\n>(definitions: T): DefineEntFromTables<T> {\n const result: any = {};\n for (const key in definitions) {\n result[key] = defineEntFromTable(definitions[key]);\n }\n return result;\n}\n\ntype GenericEdges = Record<string, GenericEdgeConfig>;\n\nexport type GenericEdgeConfig = {\n name: string;\n to: string;\n cardinality: \"single\" | \"multiple\";\n type: \"field\" | \"ref\";\n optional?: boolean;\n};\n\ntype ExtractFieldPaths<T extends Validator<any, any, any>> =\n // Add in the system fields available in index definitions.\n // This should be everything except for `_id` because thats added to indexes\n // automatically.\n T[\"fieldPaths\"] | keyof SystemFields;\n\ntype ObjectFieldType<\n FieldName extends string,\n T extends Validator<any, any, any>,\n> = T[\"isOptional\"] extends \"optional\"\n ? { [key in FieldName]?: T[\"type\"] }\n : { [key in FieldName]: T[\"type\"] };\n\ntype AddField<\n V extends GenericValidator,\n FieldName extends string,\n P extends GenericValidator,\n> =\n // Note: We can't use the `AddField` type to add fields to a union type, but ents\n // do not support schemas with top level unions\n V extends VObject<\n infer TypeScriptType,\n infer Fields,\n infer IsOptional,\n infer FieldPaths\n >\n ? VObject<\n Expand<TypeScriptType & ObjectFieldType<FieldName, P>>,\n Expand<Fields & { FieldName: P }>,\n IsOptional,\n FieldPaths | FieldName\n >\n : V extends VAny\n ? VAny\n : never;\n\nexport interface EntDefinition<\n DocumentType extends Validator<any, any, any> = Validator<any, any, any>,\n // eslint-disable-next-line @typescript-eslint/ban-types\n Indexes extends GenericTableIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n SearchIndexes extends GenericTableSearchIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n VectorIndexes extends GenericTableVectorIndexes = {},\n // eslint-disable-next-line @typescript-eslint/ban-types\n Edges extends GenericEdges = {},\n> extends TableDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes> {\n /**\n * Define an index on this table.\n *\n * To learn about indexes, see [Defining Indexes](https://docs.convex.dev/using/indexes).\n *\n * @param name - The name of the index.\n * @param fields - The fields to index, in order. Must specify at least one\n * field.\n * @returns A {@link TableDefinition} with this index included.\n */\n index<\n IndexName extends string,\n FirstFieldPath extends ExtractFieldPaths<DocumentType>,\n RestFieldPaths extends ExtractFieldPaths<DocumentType>[],\n >(\n name: IndexName,\n fields: [FirstFieldPath, ...RestFieldPaths],\n ): EntDefinition<\n DocumentType,\n Expand<\n Indexes &\n Record<IndexName, [FirstFieldPath, ...RestFieldPaths, \"_creationTime\"]>\n >,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n\n /**\n * Define a search index on this table.\n *\n * To learn about search indexes, see [Search](https://docs.convex.dev/text-search).\n *\n * @param name - The name of the index.\n * @param indexConfig - The search index configuration object.\n * @returns A {@link TableDefinition} with this search index included.\n */\n searchIndex<\n IndexName extends string,\n SearchField extends ExtractFieldPaths<DocumentType>,\n FilterFields extends ExtractFieldPaths<DocumentType> = never,\n >(\n name: IndexName,\n indexConfig: Expand<SearchIndexConfig<SearchField, FilterFields>>,\n ): EntDefinition<\n DocumentType,\n Indexes,\n Expand<\n SearchIndexes &\n Record<\n IndexName,\n {\n searchField: SearchField;\n filterFields: FilterFields;\n }\n >\n >,\n VectorIndexes,\n Edges\n >;\n\n // /**\n // * Define a vector index on this table.\n // *\n // * To learn about vector indexes, see [Vector Search](https://docs.convex.dev/vector-search).\n // *\n // * @param name - The name of the index.\n // * @param indexConfig - The vector index configuration object.\n // * @returns A {@link TableDefinition} with this vector index included.\n // */\n vectorIndex<\n IndexName extends string,\n VectorField extends ExtractFieldPaths<DocumentType>,\n FilterFields extends ExtractFieldPaths<DocumentType> = never,\n >(\n name: IndexName,\n indexConfig: Expand<VectorIndexConfig<VectorField, FilterFields>>,\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n Expand<\n VectorIndexes &\n Record<\n IndexName,\n {\n vectorField: VectorField;\n dimensions: number;\n filterFields: FilterFields;\n }\n >\n >,\n Edges\n >;\n\n field<FieldName extends string, T extends GenericValidator>(\n field: FieldName,\n validator: T,\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n field<FieldName extends string, T extends Validator<any, any, any>>(\n field: FieldName,\n validator: T,\n options: { index: true },\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes & { [key in FieldName]: [FieldName, \"_creationTime\"] },\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n field<FieldName extends string, T extends Validator<any, any, any>>(\n field: FieldName,\n validator: T,\n options: { unique: true },\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes & { [key in FieldName]: [FieldName, \"_creationTime\"] },\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n field<FieldName extends string, T extends Validator<any, \"required\", any>>(\n field: FieldName,\n validator: T,\n options: { default: T[\"type\"] },\n ): EntDefinition<\n AddField<DocumentType, FieldName, T>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n\n edge<EdgeName extends string>(\n edge: EdgeName,\n options?: { deletion: \"hard\" | \"soft\" },\n ): EntDefinition<\n AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${EdgeName}s`>>>,\n Indexes & { [key in `${EdgeName}Id`]: [`${EdgeName}Id`, \"_creationTime\"] },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"field\";\n cardinality: \"single\";\n optional: false;\n };\n }\n >;\n edge<EdgeName extends string, const FieldName extends string>(\n edge: EdgeName,\n options: { field: FieldName; deletion?: \"hard\" | \"soft\" },\n ): EntDefinition<\n AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${EdgeName}s`>>>,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"field\";\n cardinality: \"single\";\n optional: false;\n };\n }\n >;\n edge<EdgeName extends string, const FieldName extends string>(\n edge: EdgeName,\n options: {\n field: FieldName;\n optional: true;\n deletion?: \"hard\" | \"soft\";\n },\n ): EntDefinition<\n AddField<\n DocumentType,\n NoInfer<FieldName>,\n VOptional<VId<GenericId<`${EdgeName}s`>>>\n >,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"field\";\n cardinality: \"single\";\n optional: true;\n };\n }\n >;\n edge<\n EdgeName extends string,\n const FieldName extends string,\n const ToTable extends string,\n >(\n edge: EdgeName,\n options: {\n field: FieldName;\n to: ToTable;\n deletion?: ToTable extends \"_storage\" | \"_scheduled_functions\"\n ? \"hard\"\n : \"hard\" | \"soft\";\n },\n ): EntDefinition<\n AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${ToTable}`>>>,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: ToTable;\n type: \"field\";\n cardinality: \"single\";\n optional: false;\n };\n }\n >;\n edge<EdgeName extends string, const ToTable extends string>(\n edge: EdgeName,\n options: {\n to: ToTable;\n deletion?: ToTable extends \"_storage\" | \"_scheduled_functions\"\n ? \"hard\"\n : \"hard\" | \"soft\";\n },\n ): EntDefinition<\n AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${ToTable}`>>>,\n Indexes & {\n [key in `${EdgeName}Id`]: [`${EdgeName}Id`, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: ToTable;\n type: \"field\";\n cardinality: \"single\";\n optional: false;\n };\n }\n >;\n edge<\n EdgeName extends string,\n const FieldName extends string,\n const ToTable extends string,\n >(\n edge: EdgeName,\n options: {\n field: FieldName;\n to: ToTable;\n optional: true;\n deletion?: ToTable extends \"_storage\" | \"_scheduled_functions\"\n ? \"hard\"\n : \"hard\" | \"soft\";\n },\n ): EntDefinition<\n AddField<\n DocumentType,\n NoInfer<FieldName>,\n VOptional<VId<GenericId<ToTable>>>\n >,\n Indexes & {\n [key in NoInfer<FieldName>]: [NoInfer<FieldName>, \"_creationTime\"];\n },\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: ToTable;\n type: \"field\";\n cardinality: \"single\";\n optional: true;\n };\n }\n >;\n edge<EdgeName extends string>(\n edge: EdgeName,\n options: {\n ref: true | string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: `${EdgeName}s`;\n type: \"ref\";\n cardinality: \"single\";\n };\n }\n >;\n edge<EdgeName extends string, const ToTable extends string>(\n edge: EdgeName,\n options: {\n to: ToTable;\n ref: true | string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgeName]: {\n name: EdgeName;\n to: NoInfer<ToTable>;\n type: \"ref\";\n cardinality: \"single\";\n };\n }\n >;\n\n /**\n * Define many:1 edge to another table.\n * @param edge The name of the edge, also the name of the target table.\n * @param options.ref The name of the field that stores the many:1 edge\n * on the other table, or `true` to infer it.\n */\n edges<EdgesName extends string>(\n edge: EdgesName,\n options: {\n ref: true | string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: EdgesName;\n type: \"field\";\n cardinality: \"multiple\";\n };\n }\n >;\n /**\n * Define many:1 edge to another table.\n * @param edge The name of the edge.\n * @param options.to Name of the table the edge points to.\n * If it's the same as the table this edge is defined on, this edge is\n * a symmetric, self-directed many:many edge.\n * @param options.ref The name of the field that stores the many:1 edge\n * on the other table, or `true` to infer it.\n */\n edges<EdgesName extends string, TableName extends string>(\n edge: EdgesName,\n options: {\n to: TableName;\n ref: true | string;\n deletion?: \"soft\";\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: NoInfer<TableName>;\n type: \"field\";\n cardinality: \"multiple\";\n };\n }\n >;\n\n /**\n * Define many:many edge to another table.\n * @param edge The name of the edge, also the name of the target table.\n * @param options.table Optional, name of the table to store the many:many edge in.\n * @param options.field Optional, name of the field to store the ID of the\n * this end of the many:many edge.\n */\n edges<EdgesName extends string>(\n edge: EdgesName,\n options?: {\n table?: string;\n field?: string;\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: EdgesName;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n }\n >;\n /**\n * Define many:many edge to another table.\n * @param edge The name of the edge.\n * @param options.to Name of the table the edge points to.\n * If it's the same as the table this edge is defined on, this edge is\n * a symmetric, self-directed many:many edge.\n * @param options.table Optional, name of the table to store the many:many edge in.\n * @param options.field Optional, name of the field to store the ID of the\n * of the source end of the forward many:many edge.\n * @param options.inverseField Optional, name of the field to store the ID\n * of the target end of the forward edge. Only allowed for symmetric,\n * self-directed many:many edges.\n */\n edges<EdgesName extends string, TableName extends string>(\n edge: EdgesName,\n options: {\n to: TableName;\n table?: string;\n field?: string;\n inverseField?: string;\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: NoInfer<TableName>;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n }\n >;\n /**\n * Define self-directed, assymetric, many:many edge.\n * @param edge The name of the edge.\n * @param options.to Name of the table the edge points to.\n * Must be the same as the table this edge is defined on.\n * @param options.inverse Name of the inverse edge.\n * @param options.table Optional, name of the table to store the many:many edge in.\n * @param options.field Optional, name of the field to store the ID of the\n * of the source end of the forward many:many edge.\n * @param options.inverseField Optional, name of the field to store the ID\n * of the target end of the forward many:many edge.\n */\n edges<\n EdgesName extends string,\n TableName extends string,\n InverseEdgesNames extends string,\n >(\n edge: EdgesName,\n options: {\n to: TableName;\n inverse: InverseEdgesNames;\n table?: string;\n field?: string;\n inverseField?: string;\n },\n ): EntDefinition<\n DocumentType,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges & {\n [key in EdgesName]: {\n name: EdgesName;\n to: NoInfer<TableName>;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n } & {\n [key in NoInfer<InverseEdgesNames>]: {\n name: NoInfer<InverseEdgesNames>;\n to: NoInfer<TableName>;\n type: \"ref\";\n cardinality: \"multiple\";\n };\n }\n >;\n\n /**\n * Add the \"soft\" deletion behavior to this ent.\n *\n * When the ent is \"soft\" deleted, its `deletionTime` field is set to the\n * current time and it is not actually deleted.\n *\n * @param type `\"soft\"`\n */\n deletion(\n type: \"soft\",\n ): EntDefinition<\n AddField<DocumentType, \"deletionTime\", VOptional<VFloat64>>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n /**\n * Add the \"scheduled\" deletion behavior to this ent.\n *\n * The ent is first \"soft\" deleted and its hard deletion is scheduled\n * to run in a separate mutation.\n *\n * @param type `\"scheduled\"`\n * @param options.delayMs If the `delayMs` option is specified,\n * the hard deletion is scheduled to happen after the specified\n * time duration.\n */\n deletion(\n type: \"scheduled\",\n options?: {\n delayMs: number;\n },\n ): EntDefinition<\n AddField<DocumentType, \"deletionTime\", VOptional<VFloat64>>,\n Indexes,\n SearchIndexes,\n VectorIndexes,\n Edges\n >;\n}\n\ntype NoInfer<T> = [T][T extends any ? 0 : never];\n\ntype FieldOptions = {\n index?: true;\n unique?: true;\n default?: any;\n};\n\ntype EdgeOptions = {\n optional?: true;\n field?: string;\n ref?: true | string;\n to?: string;\n deletion?: \"soft\" | \"hard\";\n};\n\ntype EdgesOptions = {\n to?: string;\n inverse?: string;\n ref?: string;\n table?: string;\n field?: string;\n inverseField?: string;\n deletion: \"soft\";\n};\n\nclass EntDefinitionImpl {\n validator: GenericValidator;\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n private indexes: Index[] = [];\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n private searchIndexes: SearchIndex[] = [];\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n private vectorIndexes: VectorIndex[] = [];\n\n private documentSchema: Record<string, Validator<any, any, any>>;\n\n private edgeConfigs: Record<string, EdgeConfigBeforeDefineSchema> = {};\n\n private fieldConfigs: Record<string, FieldConfig> = {};\n\n private defaults: Record<string, any> = {};\n\n private deletionConfig: DeletionConfig | undefined;\n\n constructor(documentSchema: Record<string, Validator<any, any, any>>) {\n this.documentSchema = documentSchema;\n this.validator = v.object(documentSchema);\n }\n\n index(name: any, fields: any) {\n this.indexes.push({ indexDescriptor: name, fields });\n return this;\n }\n\n searchIndex(name: any, indexConfig: any) {\n this.searchIndexes.push({\n indexDescriptor: name,\n searchField: indexConfig.searchField,\n filterFields: indexConfig.filterFields || [],\n });\n return this;\n }\n\n vectorIndex(name: any, indexConfig: any) {\n this.vectorIndexes.push({\n indexDescriptor: name,\n vectorField: indexConfig.vectorField,\n dimensions: indexConfig.dimensions,\n filterFields: indexConfig.filterFields || [],\n });\n return this;\n }\n\n /**\n * Export the contents of this definition.\n *\n * This is called internally by the Convex framework.\n * @internal\n */\n export() {\n return {\n indexes: this.indexes,\n searchIndexes: this.searchIndexes,\n vectorIndexes: this.vectorIndexes,\n documentType: (v.object(this.documentSchema) as any).json,\n };\n }\n\n field(name: string, validator: any, options?: FieldOptions): this {\n if (this.documentSchema[name] !== undefined) {\n // TODO: Store the fieldConfigs in an array so that we can\n // do the uniqueness check in defineEntSchema where we\n // know the table name.\n throw new Error(`Duplicate field \"${name}\"`);\n }\n const finalValidator =\n options?.default !== undefined ? v.optional(validator) : validator;\n this.documentSchema = { ...this.documentSchema, [name]: finalValidator };\n if (options?.unique === true || options?.index === true) {\n this.indexes.push({ indexDescriptor: name, fields: [name] });\n }\n if (options?.default !== undefined) {\n this.defaults[name] = options.default;\n }\n if (options?.unique === true) {\n this.fieldConfigs[name] = { name, unique: true };\n }\n return this;\n }\n\n edge(edgeName: string, options?: EdgeOptions): this {\n if (this.edgeConfigs[edgeName] !== undefined) {\n // TODO: Store the edgeConfigs in an array so that we can\n // do the uniqueness check in defineEntSchema where we\n // know the source table name.\n throw new Error(`Duplicate edge \"${edgeName}\"`);\n }\n const to = options?.to ?? edgeName + \"s\";\n if (options?.field !== undefined && options?.ref !== undefined) {\n throw new Error(\n `Cannot specify both \\`field\\` and \\`ref\\` for the same edge, ` +\n `choose one to be the reference and the other to store ` +\n `the foreign key.`,\n );\n }\n if (options?.field !== undefined || options?.ref === undefined) {\n const fieldName = options?.field ?? edgeName + \"Id\";\n this.documentSchema = {\n ...this.documentSchema,\n [fieldName]:\n options?.optional === true ? v.optional(v.id(to)) : v.id(to),\n };\n this.edgeConfigs[edgeName] = {\n name: edgeName,\n to,\n cardinality: \"single\",\n type: \"field\",\n field: fieldName,\n optional: options?.optional === true,\n deletion: options?.deletion,\n };\n this.indexes.push({\n indexDescriptor: fieldName,\n fields: [fieldName],\n });\n return this;\n }\n this.edgeConfigs[edgeName] = {\n name: edgeName,\n to,\n cardinality: \"single\",\n type: \"ref\",\n ref: options.ref === true ? null : options.ref,\n deletion: options.deletion as \"soft\" | undefined,\n };\n return this;\n }\n\n edges(name: string, options?: EdgesOptions): this {\n const cardinality = \"multiple\";\n const to = options?.to ?? name;\n const ref = options?.ref;\n const table = options?.table;\n // TODO: Do this later when we have the table name,\n // or rework schema to use a builder pattern.\n if (ref !== undefined && table !== undefined) {\n throw new Error(\n `Cannot specify both \\`ref\\` and \\`table\\` for the same edge, ` +\n `as the former is for 1:many edges and the latter ` +\n `for many:many edges. Config: \\`${JSON.stringify(options)}\\``,\n );\n }\n const field = options?.field;\n const inverseField = options?.inverseField;\n // TODO: Do this later when we have the table name,\n // or rework schema to use a builder pattern.\n if (\n (field !== undefined || inverseField !== undefined) &&\n table === undefined\n ) {\n throw new Error(\n `Specify \\`table\\` if you're customizing the \\`field\\` or ` +\n `\\`inverseField\\` for a many:many edge. ` +\n `Config: \\`${JSON.stringify(options)}\\``,\n );\n }\n const inverseName = options?.inverse;\n const deletion = options?.deletion;\n this.edgeConfigs[name] =\n ref !== undefined\n ? { name, to, cardinality, type: \"field\", ref, deletion }\n : { name, to, cardinality, type: \"ref\", table, field, inverseField };\n if (inverseName !== undefined) {\n this.edgeConfigs[inverseName] = {\n name: inverseName,\n to,\n cardinality,\n type: \"ref\",\n inverse: name,\n table,\n };\n }\n return this;\n }\n\n deletion(type: \"soft\" | \"scheduled\", options?: { delayMs: number }): this {\n if (this.documentSchema.deletionTime !== undefined) {\n // TODO: Put the check where we know the table name.\n throw new Error(\n `Cannot enable \"${type}\" deletion because \"deletionTime\" field ` +\n `was already defined.`,\n );\n }\n if (this.deletionConfig !== undefined) {\n // TODO: Put the check where we know the table name.\n throw new Error(`Deletion behavior can only be specified once.`);\n }\n this.documentSchema = {\n ...this.documentSchema,\n deletionTime: v.optional(v.number()),\n };\n this.deletionConfig = { type, ...options };\n return this;\n }\n}\n\nexport type EdgeConfig = {\n name: string;\n to: string;\n} & (\n | ({\n cardinality: \"single\";\n } & (\n | {\n type: \"field\";\n field: string;\n unique: boolean;\n optional: boolean;\n deletion?: \"soft\" | \"hard\";\n }\n | {\n type: \"ref\";\n ref: string;\n deletion?: \"soft\";\n }\n ))\n | ({\n cardinality: \"multiple\";\n } & (\n | {\n type: \"field\";\n ref: string;\n deletion?: \"soft\";\n }\n | {\n type: \"ref\";\n table: string;\n field: string;\n ref: string;\n inverse: boolean;\n symmetric: boolean;\n }\n ))\n);\n\ntype EdgeConfigSingleField = Extract<\n EdgeConfig,\n {\n type: \"field\";\n cardinality: \"single\";\n }\n>;\ntype EdgeConfigSingleRef = Extract<\n EdgeConfig,\n {\n type: \"ref\";\n cardinality: \"single\";\n }\n>;\ntype EdgeConfigMultipleField = Extract<\n EdgeConfig,\n {\n type: \"field\";\n cardinality: \"multiple\";\n }\n>;\ntype EdgeConfigMultipleRef = Extract<\n EdgeConfig,\n {\n type: \"ref\";\n cardinality: \"multiple\";\n }\n>;\n\ntype EdgeConfigBeforeDefineSchema = {\n name: string;\n to: string;\n} & (\n | ({\n cardinality: \"single\";\n } & (\n | {\n type: \"field\";\n field: string;\n optional: boolean;\n deletion?: \"soft\" | \"hard\";\n }\n | {\n type: \"ref\";\n ref: null | string;\n deletion?: \"soft\";\n }\n ))\n | ({\n cardinality: \"multiple\";\n } & (\n | {\n type: \"field\";\n ref: true | string;\n deletion?: \"soft\";\n }\n | {\n type: \"ref\";\n table?: string;\n field?: string;\n inverseField?: string;\n inverse?: string;\n }\n ))\n);\n\nexport type FieldConfig = {\n name: string;\n unique: boolean;\n};\n\nexport type Expand<ObjectType extends Record<any, any>> =\n ObjectType extends Record<any, any>\n ? {\n [Key in keyof ObjectType]: ObjectType[Key];\n }\n : never;\nexport type SystemFields = {\n _creationTime: number;\n};\n\ntype ObjectValidator<Validators extends PropertyValidators> = VObject<\n // Compute the TypeScript type this validator refers to.\n ObjectType<Validators>,\n Validators\n>;\n\nexport type GenericEntsDataModel = GenericDataModel &\n Record<string, GenericEntModel>;\n\nexport type GenericEntModel = {\n edges: Record<string, GenericEdgeConfig>;\n};\n\nexport type DeletionConfig =\n | {\n type: \"soft\";\n }\n | {\n type: \"scheduled\";\n delayMs?: number;\n };\n\nexport type EntDataModelFromSchema<\n SchemaDef extends SchemaDefinition<any, boolean>,\n> = DataModelFromSchemaDefinition<SchemaDef> & {\n [TableName in keyof SchemaDef[\"tables\"] &\n string]: SchemaDef[\"tables\"][TableName] extends EntDefinition<\n any,\n any,\n any,\n any,\n infer Edges\n >\n ? {\n edges: Edges;\n }\n : never;\n};\n\nexport function getEntDefinitions<\n SchemaDef extends SchemaDefinition<any, boolean>,\n>(schema: SchemaDef): EntDataModelFromSchema<typeof schema> {\n const tables = schema.tables;\n return Object.entries(tables).reduce(\n (acc, [tableName, table]: [any, any]) => {\n acc[tableName] = {\n indexes: (\n table.indexes as {\n indexDescriptor: string;\n fields: string[];\n }[]\n ).reduce(\n (acc, { indexDescriptor, fields }) => {\n acc[indexDescriptor] = fields;\n return acc;\n },\n {} as Record<string, string[]>,\n ),\n defaults: table.defaults,\n edges: table.edgeConfigs,\n fields: table.fieldConfigs,\n deletionConfig: table.deletionConfig,\n };\n return acc;\n },\n {} as Record<string, any>,\n ) as any;\n}\n"],"mappings":"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","names":["edge","acc"]}
package/dist/writer.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import 'convex/server';
2
2
  import 'convex/values';
3
- export { E as EdgeChanges, a as WithEdgeInserts, c as WithEdgePatches, b as WithEdges, W as WriterImplBase } from './index-kwzjMMHy.js';
3
+ export { E as EdgeChanges, a as WithEdgeInserts, c as WithEdgePatches, b as WithEdges, W as WriterImplBase, i as isSystemTable } from './index-surAwtky.js';
4
4
  import './schema.js';
5
5
  import './deletion.js';
6
6
  import './shared.js';
package/dist/writer.js CHANGED
@@ -20,7 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/writer.ts
21
21
  var writer_exports = {};
22
22
  __export(writer_exports, {
23
- WriterImplBase: () => WriterImplBase
23
+ WriterImplBase: () => WriterImplBase,
24
+ isSystemTable: () => isSystemTable
24
25
  });
25
26
  module.exports = __toCommonJS(writer_exports);
26
27
  var import_server2 = require("convex/server");
@@ -686,7 +687,7 @@ var PromiseEntOrNullImpl = class extends Promise {
686
687
  );
687
688
  }
688
689
  const otherDoc = await this.ctx.db.get(otherId);
689
- if (otherDoc === null) {
690
+ if (otherDoc === null && edgeDefinition.to !== "_scheduled_functions") {
690
691
  throw new Error(
691
692
  `Dangling reference for edge "${edgeDefinition.name}" in table "${this.table}" on document with ID "${id}": Could not find a document with ID "${otherId}" in table "${edgeDefinition.to}".`
692
693
  );
@@ -890,7 +891,7 @@ var PromiseEntWriterImpl = class extends PromiseEntOrNullImpl {
890
891
  if (edgeDefinition.type === "ref") {
891
892
  const oldDoc = await this.ctx.db.get(docId);
892
893
  if (oldDoc[key] !== void 0 && oldDoc[key] !== idOrIds) {
893
- throw new Error("Cannot set 1:1 edge from optional end.");
894
+ throw new Error("Cannot set 1:1 edge from ref end.");
894
895
  }
895
896
  }
896
897
  } else {
@@ -1056,7 +1057,7 @@ var WriterImplBase = class _WriterImplBase {
1056
1057
  const isDeletingSoftly = behavior !== "hard" && deletionConfig !== void 0 && (deletionConfig.type === "soft" || deletionConfig.type === "scheduled");
1057
1058
  if (behavior === "soft" && !isDeletingSoftly) {
1058
1059
  throw new Error(
1059
- `Cannot soft delete document with ID "${id}" in table "${this.table}" because it does not have an "allowSoft", "soft" or "scheduled" deletion behavior configured.`
1060
+ `Cannot soft delete document with ID "${id}" in table "${this.table}" because it does not have a "soft" or "scheduled" deletion behavior configured.`
1060
1061
  );
1061
1062
  }
1062
1063
  const edges = {};
@@ -1072,6 +1073,16 @@ var WriterImplBase = class _WriterImplBase {
1072
1073
  ).collect()).map((doc) => doc._id);
1073
1074
  edges[key] = { remove };
1074
1075
  }
1076
+ } else if (edgeDefinition.cardinality === "single") {
1077
+ if (edgeDefinition.deletion !== void 0 && (!isDeletingSoftly || edgeDefinition.deletion === "soft")) {
1078
+ const doc = await this.ctx.db.get(id);
1079
+ if (doc !== null) {
1080
+ const otherId = doc[edgeDefinition.field];
1081
+ edges[key] = {
1082
+ remove: otherId !== void 0 ? [otherId] : []
1083
+ };
1084
+ }
1085
+ }
1075
1086
  } else if (edgeDefinition.cardinality === "multiple") {
1076
1087
  if (!isDeletingSoftly) {
1077
1088
  const removeEdges = (await this.ctx.db.query(edgeDefinition.table).withIndex(
@@ -1115,12 +1126,26 @@ var WriterImplBase = class _WriterImplBase {
1115
1126
  }
1116
1127
  return id;
1117
1128
  }
1118
- async deletedIdIn(id, table, cascadingSoft) {
1129
+ async deleteIdIn(id, table, cascadingSoft) {
1119
1130
  await new _WriterImplBase(this.ctx, this.entDefinitions, table).deleteId(
1120
1131
  id,
1121
1132
  cascadingSoft ? "soft" : "hard"
1122
1133
  );
1123
1134
  }
1135
+ async deleteSystem(table, id) {
1136
+ switch (table) {
1137
+ case "_storage":
1138
+ await this.ctx.storage.delete(id);
1139
+ break;
1140
+ case "_scheduled_functions":
1141
+ await this.ctx.scheduler.cancel(id);
1142
+ break;
1143
+ default:
1144
+ throw new Error(
1145
+ `Cannot cascade deletion to unsupported system table "${table}".`
1146
+ );
1147
+ }
1148
+ }
1124
1149
  async writeEdges(docId, changes, deleteSoftly) {
1125
1150
  await Promise.all(
1126
1151
  Object.values(getEdgeDefinitions(this.entDefinitions, this.table)).map(
@@ -1133,7 +1158,7 @@ var WriterImplBase = class _WriterImplBase {
1133
1158
  if (idOrIds.remove !== void 0 && idOrIds.remove.length > 0) {
1134
1159
  await Promise.all(
1135
1160
  idOrIds.remove.map(
1136
- (id) => this.deletedIdIn(
1161
+ (id) => this.deleteIdIn(
1137
1162
  id,
1138
1163
  edgeDefinition.to,
1139
1164
  (deleteSoftly ?? false) && edgeDefinition.deletion === "soft"
@@ -1150,6 +1175,18 @@ var WriterImplBase = class _WriterImplBase {
1150
1175
  )
1151
1176
  );
1152
1177
  }
1178
+ } else if (edgeDefinition.cardinality === "single") {
1179
+ if (idOrIds.remove !== void 0 && idOrIds.remove.length > 0) {
1180
+ await Promise.all(
1181
+ idOrIds.remove.map(
1182
+ isSystemTable(edgeDefinition.to) ? (id) => this.deleteSystem(edgeDefinition.to, id) : (id) => this.deleteIdIn(
1183
+ id,
1184
+ edgeDefinition.to,
1185
+ (deleteSoftly ?? false) && edgeDefinition.deletion === "soft"
1186
+ )
1187
+ )
1188
+ );
1189
+ }
1153
1190
  } else if (edgeDefinition.cardinality === "multiple") {
1154
1191
  if ((idOrIds.removeEdges ?? []).length > 0) {
1155
1192
  await Promise.all(
@@ -1293,8 +1330,12 @@ var WriterImplBase = class _WriterImplBase {
1293
1330
  }
1294
1331
  }
1295
1332
  };
1333
+ function isSystemTable(table) {
1334
+ return table.startsWith("_");
1335
+ }
1296
1336
  // Annotate the CommonJS export names for ESM import in node:
1297
1337
  0 && (module.exports = {
1298
- WriterImplBase
1338
+ WriterImplBase,
1339
+ isSystemTable
1299
1340
  });
1300
1341
  //# sourceMappingURL=writer.js.map