convex-ents
Version:
Relations, default values, unique fields, RLS for Convex
444 lines (441 loc) • 21.2 kB
TypeScript
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 };