import type { JSONValue, Awaitable } from "@canvas-js/utils"; export type PrimaryKeyType = "primary"; export type PrimitiveType = "integer" | "float" | "number" | "string" | "bytes" | "boolean" | "json"; export type NullablePrimitiveType = `${PrimitiveType}?`; export type ReferenceType = `@${string}`; export type NullableReferenceType = `@${string}?`; export type RelationType = `@${string}[]`; export type PropertyType = PrimaryKeyType | PrimitiveType | NullablePrimitiveType | ReferenceType | NullableReferenceType | RelationType; /** property name, or property names joined by slashes */ export type IndexInit = string; /** This should be an intersection type or conditional mapped type, but TS 5.6 doesn't let us mix them. * If you need a proper conditional type here, consider overriding ModelInit with your own type. */ export type ModelInit = ({ $indexes?: IndexInit[]; $primary?: string; } & Ext) | Record; export type ModelSchema = Record>; export type PrimitiveProperty = { name: string; kind: "primitive"; type: PrimitiveType; nullable: boolean; }; export type ReferenceProperty = { name: string; kind: "reference"; target: string; nullable: boolean; }; export type RelationProperty = { name: string; kind: "relation"; target: string; }; export type Property = PrimitiveProperty | ReferenceProperty | RelationProperty; export type Relation = { source: string; sourceProperty: string; target: string; indexed: boolean; }; export type Model = { name: string; primaryKey: string[]; properties: Property[]; indexes: string[][]; }; export type PrimaryKeyValue = number | string | Uint8Array; export type PrimitiveValue = number | string | Uint8Array | null | boolean; export type ReferenceValue = PrimaryKeyValue | PrimaryKeyValue[] | null; export type RelationValue = PrimaryKeyValue[] | PrimaryKeyValue[][]; export type PropertyValue = PrimitiveValue | ReferenceValue | RelationValue | JSONValue; export type ModelValue = { [key: string]: T; }; export type ModelValueWithIncludes = { [key: string]: T | ModelValueWithIncludes | ModelValueWithIncludes[]; }; export type WhereCondition = Record; export type NotExpression = { neq: PropertyValue | undefined; }; export type RangeExpression = { gt?: PrimitiveValue | ReferenceValue; gte?: PrimitiveValue | ReferenceValue; lt?: PrimitiveValue | ReferenceValue; lte?: PrimitiveValue | ReferenceValue; }; export type IncludeExpression = { [key: string]: IncludeExpression; }; export type QueryParams = { select?: Record; include?: IncludeExpression; where?: WhereCondition; orderBy?: Record; limit?: number; offset?: number; }; export type DerivePropertyType = T extends "primary" ? string : T extends "integer" | "float" | "number" ? number : T extends "integer?" | "float?" | "number?" ? number | null : T extends "string" ? string : T extends "string?" ? string | null : T extends "bytes" ? Uint8Array : T extends "bytes?" ? Uint8Array | null : T extends "boolean" ? boolean : T extends "boolean?" ? boolean | null : T extends "json" ? JSONValue : T extends `@${string}[]` ? HasIncludes extends true ? any : RelationValue : T extends `@${string}?` ? (HasIncludes extends true ? any : ReferenceValue) | null : T extends `@${string}` ? HasIncludes extends true ? any : ReferenceValue : never; export type DeriveModelTypes = { [K in keyof T]: { [P in keyof T[K] as Exclude]: T[K][P] extends PropertyType ? DerivePropertyType : never; }; }; export type DeriveModelType, Ext = {}, Includes extends IncludeExpression | undefined = {}> = { [P in keyof T as Exclude]: T[P] extends PropertyType ? DerivePropertyType : never; }; export type Contract = { models: T; actions: Record>; }; export type Effect = { model: string; operation: "set"; value: ModelValue; } | { model: string; operation: "delete"; key: PrimaryKeyValue | PrimaryKeyValue[]; }; export interface PropertyEncoder { encodePrimitiveValue(propertyName: string, type: PrimitiveType, nullable: boolean, value: PropertyValue): T; encodeReferenceValue(propertyName: string, target: PrimitiveProperty[], nullable: boolean, value: PropertyValue): T[]; } export interface PropertyDecoder { decodePrimitiveValue(propertyName: string, type: PrimitiveType, nullable: boolean, value: T): PrimitiveValue; decodeReferenceValue(propertyName: string, nullable: boolean, target: PrimitiveProperty[], values: T[]): ReferenceValue; } export interface PropertyAPI { columns: string[]; encode: (value: PropertyValue) => T[]; decode: (record: Record) => PropertyValue; } export interface DatabaseAPI { get = ModelValue>(modelName: string, key: PrimaryKeyValue | PrimaryKeyValue[]): Awaitable; getAll = ModelValue>(modelName: string): Awaitable; getMany = ModelValue>(modelName: string, key: PrimaryKeyValue[] | PrimaryKeyValue[][]): Awaitable<(T | null)[]>; iterate = ModelValue>(modelName: string, query?: QueryParams): AsyncIterable; query = ModelValue>(modelName: string, query?: QueryParams): Awaitable; count(modelName: string, where?: WhereCondition): Awaitable; clear(modelName: string): Awaitable; apply(effects: Effect[]): Awaitable; set = ModelValue>(modelName: string, value: T): Awaitable; delete(modelName: string, key: PrimaryKeyValue | PrimaryKeyValue[]): Awaitable; } export interface DatabaseUpgradeAPI extends DatabaseAPI { createModel(name: string, init: ModelInit): Awaitable; deleteModel(name: string): Awaitable; addProperty(modelName: string, propertyName: string, propertyType: PropertyType, defaultPropertyValue: PropertyValue): Awaitable; removeProperty(modelName: string, propertyName: string): Awaitable; addIndex(modelName: string, index: string): Awaitable; removeIndex(modelName: string, index: string): Awaitable; }