UNPKG

convex-ents

Version:

Relations, default values, unique fields, RLS for Convex

444 lines (441 loc) 21.2 kB
import { GenericDataModel, GenericTableIndexes, GenericTableSearchIndexes, GenericTableVectorIndexes, TableDefinition, IndexTiebreakerField, SearchIndexConfig, VectorIndexConfig, DefineSchemaOptions, SchemaDefinition, DataModelFromSchemaDefinition } from 'convex/server'; import { Validator, GenericValidator, VObject, VUnion, VAny, VId, GenericId, VOptional, VFloat64, ObjectType } from 'convex/values'; declare function defineEntSchema<Schema extends Record<string, EntDefinition>, StrictTableNameTypes extends boolean = true>(schema: Schema, options?: DefineSchemaOptions<StrictTableNameTypes>): SchemaDefinition<Schema, StrictTableNameTypes>; declare function edgeCompoundIndexName(edgeDefinition: EdgeConfig & { cardinality: "multiple"; type: "ref"; }): string; declare function defineEnt<DocumentSchema extends Validator<Record<string, any>, "required", any>>(documentSchema: DocumentSchema): EntDefinition<DocumentSchema>; declare function defineEnt<DocumentSchema extends Record<string, GenericValidator>>(documentSchema: DocumentSchema): EntDefinition<VObject<ObjectType<DocumentSchema>, DocumentSchema>>; declare function defineEntFromTable<DocumentType extends GenericValidator = GenericValidator, Indexes extends GenericTableIndexes = {}, SearchIndexes extends GenericTableSearchIndexes = {}, VectorIndexes extends GenericTableVectorIndexes = {}>(definition: TableDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes>): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes>; type DefineEntFromTables<T extends { [key: string]: TableDefinition<any, any, any, any>; }> = { [K in keyof T]: T[K] extends TableDefinition<infer D, infer I, infer S, infer V> ? EntDefinition<D, I, S, V> : never; }; declare function defineEntsFromTables<T extends { [key: string]: TableDefinition<any, any, any, any>; }>(definitions: T): DefineEntFromTables<T>; type GenericEdges = Record<string, GenericEdgeConfig>; type GenericEdgeConfig = { name: string; to: string; cardinality: "single" | "multiple"; type: "field" | "ref"; optional?: boolean; }; type ExtractFieldPaths<T extends Validator<any, any, any>> = T["fieldPaths"] | keyof SystemFields; type ObjectFieldType<FieldName extends string, T extends Validator<any, any, any>> = T["isOptional"] extends "optional" ? { [key in FieldName]?: T["type"]; } : { [key in FieldName]: T["type"]; }; type AddField<V extends GenericValidator, FieldName extends string, P extends GenericValidator> = V extends VObject<infer TypeScriptType, infer Fields, infer IsOptional, infer FieldPaths> ? VObject<Expand<TypeScriptType & ObjectFieldType<FieldName, P>>, Expand<Fields & { FieldName: P; }>, IsOptional, FieldPaths | FieldName> : V extends VUnion<infer TypeScriptType, infer Members, infer IsOptional, infer FieldPaths> ? VUnion<Expand<TypeScriptType & ObjectFieldType<FieldName, P>>, { [key in keyof Members]: AddField<Members[key], FieldName, P>; }, IsOptional, FieldPaths | FieldName> : V extends VAny ? VAny : never; interface EntDefinition<DocumentType extends Validator<any, any, any> = Validator<any, any, any>, Indexes extends GenericTableIndexes = {}, SearchIndexes extends GenericTableSearchIndexes = {}, VectorIndexes extends GenericTableVectorIndexes = {}, Edges extends GenericEdges = {}> extends TableDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes> { /** * Define an index on this table. * * To learn about indexes, see [Defining Indexes](https://docs.convex.dev/using/indexes). * * @param name - The name of the index. * @param indexConfig - The index configuration object. * @returns A {@link TableDefinition} with this index included. */ index<IndexName extends string, FirstFieldPath extends ExtractFieldPaths<DocumentType>, RestFieldPaths extends ExtractFieldPaths<DocumentType>[]>(name: IndexName, indexConfig: Expand<DbIndexConfig<FirstFieldPath, RestFieldPaths> & IndexOptions & { staged?: false; }>): TableDefinition<DocumentType, Expand<Indexes & Record<IndexName, [ FirstFieldPath, ...RestFieldPaths, IndexTiebreakerField ]>>, SearchIndexes, VectorIndexes>; /** * Define an index on this table. * * To learn about indexes, see [Defining Indexes](https://docs.convex.dev/using/indexes). * * @param name - The name of the index. * @param fields - The fields to index, in order. Must specify at least one * field. * @returns A {@link TableDefinition} with this index included. */ index<IndexName extends string, FirstFieldPath extends ExtractFieldPaths<DocumentType>, RestFieldPaths extends ExtractFieldPaths<DocumentType>[]>(name: IndexName, fields: [FirstFieldPath, ...RestFieldPaths]): EntDefinition<DocumentType, Expand<Indexes & Record<IndexName, [FirstFieldPath, ...RestFieldPaths, "_creationTime"]>>, SearchIndexes, VectorIndexes, Edges>; /** * Define a staged index on this table. * * For large tables, index backfill can be slow. Staging an index allows you * to push the schema and enable the index later. * * If `staged` is `true`, the index will be staged and will not be enabled * until the staged flag is removed. Staged indexes do not block push * completion. Staged indexes cannot be used in queries. * * To learn about indexes, see [Defining Indexes](https://docs.convex.dev/using/indexes). * * @param name - The name of the index. * @param indexConfig - The index configuration object. * @returns A {@link TableDefinition} with this index included. */ index<IndexName extends string, FirstFieldPath extends ExtractFieldPaths<DocumentType>, RestFieldPaths extends ExtractFieldPaths<DocumentType>[]>(name: IndexName, indexConfig: Expand<DbIndexConfig<FirstFieldPath, RestFieldPaths> & IndexOptions & { staged: true; }>): TableDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes>; /** * Define a search index on this table. * * To learn about search indexes, see [Search](https://docs.convex.dev/text-search). * * @param name - The name of the index. * @param indexConfig - The search index configuration object. * @returns A {@link TableDefinition} with this search index included. */ searchIndex<IndexName extends string, SearchField extends ExtractFieldPaths<DocumentType>, FilterFields extends ExtractFieldPaths<DocumentType> = never>(name: IndexName, indexConfig: Expand<SearchIndexConfig<SearchField, FilterFields>>): EntDefinition<DocumentType, Indexes, Expand<SearchIndexes & Record<IndexName, { searchField: SearchField; filterFields: FilterFields; }>>, VectorIndexes, Edges>; vectorIndex<IndexName extends string, VectorField extends ExtractFieldPaths<DocumentType>, FilterFields extends ExtractFieldPaths<DocumentType> = never>(name: IndexName, indexConfig: Expand<VectorIndexConfig<VectorField, FilterFields>>): EntDefinition<DocumentType, Indexes, SearchIndexes, Expand<VectorIndexes & Record<IndexName, { vectorField: VectorField; dimensions: number; filterFields: FilterFields; }>>, Edges>; field<FieldName extends string, T extends GenericValidator>(field: FieldName, validator: T): EntDefinition<AddField<DocumentType, FieldName, T>, Indexes, SearchIndexes, VectorIndexes, Edges>; field<FieldName extends string, T extends Validator<any, any, any>>(field: FieldName, validator: T, options: { index: true; }): EntDefinition<AddField<DocumentType, FieldName, T>, Indexes & { [key in FieldName]: [FieldName, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges>; field<FieldName extends string, T extends Validator<any, any, any>>(field: FieldName, validator: T, options: { unique: true; }): EntDefinition<AddField<DocumentType, FieldName, T>, Indexes & { [key in FieldName]: [FieldName, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges>; field<FieldName extends string, T extends Validator<any, "required", any>>(field: FieldName, validator: T, options: { default: T["type"]; }): EntDefinition<AddField<DocumentType, FieldName, T>, Indexes, SearchIndexes, VectorIndexes, Edges>; edge<EdgeName extends string>(edge: EdgeName, options?: { deletion: "hard" | "soft"; }): EntDefinition<AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${EdgeName}s`>>>, Indexes & { [key in `${EdgeName}Id`]: [`${EdgeName}Id`, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: `${EdgeName}s`; type: "field"; cardinality: "single"; optional: false; }; }>; edge<EdgeName extends string, const FieldName extends string>(edge: EdgeName, options: { field: FieldName; deletion?: "hard" | "soft"; }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${EdgeName}s`>>>, Indexes & { [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: `${EdgeName}s`; type: "field"; cardinality: "single"; optional: false; }; }>; edge<EdgeName extends string, const FieldName extends string>(edge: EdgeName, options: { field: FieldName; optional: true; deletion?: "hard" | "soft"; }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VOptional<VId<GenericId<`${EdgeName}s`>>>>, Indexes & { [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: `${EdgeName}s`; type: "field"; cardinality: "single"; optional: true; }; }>; edge<EdgeName extends string, const FieldName extends string, const ToTable extends string>(edge: EdgeName, options: { field: FieldName; to: ToTable; deletion?: ToTable extends "_storage" | "_scheduled_functions" ? "hard" : "hard" | "soft"; }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VId<GenericId<`${ToTable}`>>>, Indexes & { [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: ToTable; type: "field"; cardinality: "single"; optional: false; }; }>; edge<EdgeName extends string, const ToTable extends string>(edge: EdgeName, options: { to: ToTable; deletion?: ToTable extends "_storage" | "_scheduled_functions" ? "hard" : "hard" | "soft"; }): EntDefinition<AddField<DocumentType, `${EdgeName}Id`, VId<GenericId<`${ToTable}`>>>, Indexes & { [key in `${EdgeName}Id`]: [`${EdgeName}Id`, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: ToTable; type: "field"; cardinality: "single"; optional: false; }; }>; edge<EdgeName extends string, const FieldName extends string, const ToTable extends string>(edge: EdgeName, options: { field: FieldName; to: ToTable; optional: true; deletion?: ToTable extends "_storage" | "_scheduled_functions" ? "hard" : "hard" | "soft"; }): EntDefinition<AddField<DocumentType, NoInfer<FieldName>, VOptional<VId<GenericId<ToTable>>>>, Indexes & { [key in NoInfer<FieldName>]: [NoInfer<FieldName>, "_creationTime"]; }, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: ToTable; type: "field"; cardinality: "single"; optional: true; }; }>; edge<EdgeName extends string>(edge: EdgeName, options: { ref: true | string; deletion?: "soft"; }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: `${EdgeName}s`; type: "ref"; cardinality: "single"; }; }>; edge<EdgeName extends string, const ToTable extends string>(edge: EdgeName, options: { to: ToTable; ref: true | string; deletion?: "soft"; }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & { [key in EdgeName]: { name: EdgeName; to: NoInfer<ToTable>; type: "ref"; cardinality: "single"; }; }>; /** * Define many:1 edge to another table. * @param edge The name of the edge, also the name of the target table. * @param options.ref The name of the field that stores the many:1 edge * on the other table, or `true` to infer it. */ edges<EdgesName extends string>(edge: EdgesName, options: { ref: true | string; deletion?: "soft"; }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & { [key in EdgesName]: { name: EdgesName; to: EdgesName; type: "field"; cardinality: "multiple"; }; }>; /** * Define many:1 edge to another table. * @param edge The name of the edge. * @param options.to Name of the table the edge points to. * If it's the same as the table this edge is defined on, this edge is * a symmetric, self-directed many:many edge. * @param options.ref The name of the field that stores the many:1 edge * on the other table, or `true` to infer it. */ edges<EdgesName extends string, TableName extends string>(edge: EdgesName, options: { to: TableName; ref: true | string; deletion?: "soft"; }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & { [key in EdgesName]: { name: EdgesName; to: NoInfer<TableName>; type: "field"; cardinality: "multiple"; }; }>; /** * Define many:many edge to another table. * @param edge The name of the edge, also the name of the target table. * @param options.table Optional, name of the table to store the many:many edge in. * @param options.field Optional, name of the field to store the ID of the * this end of the many:many edge. */ edges<EdgesName extends string>(edge: EdgesName, options?: { table?: string; field?: string; }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & { [key in EdgesName]: { name: EdgesName; to: EdgesName; type: "ref"; cardinality: "multiple"; }; }>; /** * Define many:many edge to another table. * @param edge The name of the edge. * @param options.to Name of the table the edge points to. * If it's the same as the table this edge is defined on, this edge is * a symmetric, self-directed many:many edge. * @param options.table Optional, name of the table to store the many:many edge in. * @param options.field Optional, name of the field to store the ID of the * of the source end of the forward many:many edge. * @param options.inverseField Optional, name of the field to store the ID * of the target end of the forward edge. Only allowed for symmetric, * self-directed many:many edges. */ edges<EdgesName extends string, TableName extends string>(edge: EdgesName, options: { to: TableName; table?: string; field?: string; inverseField?: string; }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & { [key in EdgesName]: { name: EdgesName; to: NoInfer<TableName>; type: "ref"; cardinality: "multiple"; }; }>; /** * Define self-directed, assymetric, many:many edge. * @param edge The name of the edge. * @param options.to Name of the table the edge points to. * Must be the same as the table this edge is defined on. * @param options.inverse Name of the inverse edge. * @param options.table Optional, name of the table to store the many:many edge in. * @param options.field Optional, name of the field to store the ID of the * of the source end of the forward many:many edge. * @param options.inverseField Optional, name of the field to store the ID * of the target end of the forward many:many edge. */ edges<EdgesName extends string, TableName extends string, InverseEdgesNames extends string>(edge: EdgesName, options: { to: TableName; inverse: InverseEdgesNames; table?: string; field?: string; inverseField?: string; }): EntDefinition<DocumentType, Indexes, SearchIndexes, VectorIndexes, Edges & { [key in EdgesName]: { name: EdgesName; to: NoInfer<TableName>; type: "ref"; cardinality: "multiple"; }; } & { [key in NoInfer<InverseEdgesNames>]: { name: NoInfer<InverseEdgesNames>; to: NoInfer<TableName>; type: "ref"; cardinality: "multiple"; }; }>; /** * Add the "soft" deletion behavior to this ent. * * When the ent is "soft" deleted, its `deletionTime` field is set to the * current time and it is not actually deleted. * * @param type `"soft"` */ deletion(type: "soft"): EntDefinition<AddField<DocumentType, "deletionTime", VOptional<VFloat64>>, Indexes, SearchIndexes, VectorIndexes, Edges>; /** * Add the "scheduled" deletion behavior to this ent. * * The ent is first "soft" deleted and its hard deletion is scheduled * to run in a separate mutation. * * @param type `"scheduled"` * @param options.delayMs If the `delayMs` option is specified, * the hard deletion is scheduled to happen after the specified * time duration. */ deletion(type: "scheduled", options?: { delayMs: number; }): EntDefinition<AddField<DocumentType, "deletionTime", VOptional<VFloat64>>, Indexes, SearchIndexes, VectorIndexes, Edges>; } type NoInfer<T> = [T][T extends any ? 0 : never]; type EdgeConfig = { name: string; to: string; } & (({ cardinality: "single"; } & ({ type: "field"; field: string; unique: boolean; optional: boolean; deletion?: "soft" | "hard"; } | { type: "ref"; ref: string; deletion?: "soft"; })) | ({ cardinality: "multiple"; } & ({ type: "field"; ref: string; deletion?: "soft"; } | { type: "ref"; table: string; field: string; ref: string; inverse: boolean; symmetric: boolean; }))); type FieldConfig = { name: string; unique: boolean; }; type Expand<ObjectType extends Record<any, any>> = ObjectType extends Record<any, any> ? { [Key in keyof ObjectType]: ObjectType[Key]; } : never; type SystemFields = { _creationTime: number; }; interface IndexOptions { /** * Whether the index should be staged. * * For large tables, index backfill can be slow. Staging an index allows you * to push the schema and enable the index later. * * If `staged` is `true`, the index will be staged and will not be enabled * until the staged flag is removed. Staged indexes do not block push * completion. Staged indexes cannot be used in queries. */ staged?: boolean; } interface DbIndexConfig<FirstFieldPath extends string, RestFieldPaths extends string[]> { /** * The fields to index, in order. Must specify at least one field. */ fields: [FirstFieldPath, ...RestFieldPaths]; } type GenericEntsDataModel = GenericDataModel & Record<string, GenericEntModel>; type GenericEntModel = { edges: Record<string, GenericEdgeConfig>; }; type DeletionConfig = { type: "soft"; } | { type: "scheduled"; delayMs?: number; }; type EntDataModelFromSchema<SchemaDef extends SchemaDefinition<any, boolean>> = DataModelFromSchemaDefinition<SchemaDef> & { [TableName in keyof SchemaDef["tables"] & string]: SchemaDef["tables"][TableName] extends EntDefinition<any, any, any, any, infer Edges> ? { edges: Edges; } : never; }; declare function getEntDefinitions<SchemaDef extends SchemaDefinition<any, boolean>>(schema: SchemaDef): EntDataModelFromSchema<typeof schema>; export { type DeletionConfig, type EdgeConfig, type EntDataModelFromSchema, type EntDefinition, type Expand, type FieldConfig, type GenericEdgeConfig, type GenericEntModel, type GenericEntsDataModel, type SystemFields, defineEnt, defineEntFromTable, defineEntSchema, defineEntsFromTables, edgeCompoundIndexName, getEntDefinitions };