import type { FromSchema, JSONSchema } from "json-schema-to-ts"; import type { JsonPointer } from "./json-pointer.js"; export type LixForeignKeyScopeColumn = "version_id" | "file_id"; /** * Foreign key constraint definition */ export type LixForeignKey = { /** * Local JSON-schema property names that participate in the FK. * Must have at least one property. */ properties: readonly JsonPointer[] | JsonPointer[]; /** * Where they point to. */ references: { /** * The x-lix-key of the referenced schema */ schemaKey: string; /** * Remote property names (must have same length as local properties) */ properties: readonly JsonPointer[] | JsonPointer[]; /** * Optional version of the referenced schema */ schemaVersion?: string; }; /** * Validation mode for this foreign key. * - "immediate" (default): validate referenced existence on insert/update; restrict deletion when referenced. * - "materialized": skip insert/update existence checks (reference is derived/materialized later); * still restrict deletion when referenced. */ mode?: "immediate" | "materialized"; /** * Implicit columns that scope the foreign key relationship. * When omitted, defaults to ["version_id", "file_id"] for state-backed schemas. * Provide an empty array to disable implicit scoping entirely. */ scope?: readonly LixForeignKeyScopeColumn[] | LixForeignKeyScopeColumn[]; }; export declare const LixSchemaDefinition: { readonly $schema: "http://json-schema.org/draft-07/schema#"; readonly title: "Lix Change Schema"; readonly description: "A JSON schema document that also includes custom x-key and x-version properties for identification and versioning."; readonly allOf: readonly [{ readonly $ref: "http://json-schema.org/draft-07/schema#"; }, { readonly type: "object"; readonly properties: { readonly "x-lix-unique": { readonly type: "array"; readonly items: { readonly type: "array"; readonly minItems: 1; readonly uniqueItems: true; readonly items: { readonly type: "string"; readonly format: "json-pointer"; readonly description: "JSON Pointer referencing a property"; }; }; }; readonly additionalProperties: { readonly type: "boolean"; readonly const: false; readonly description: "Objects describing Lix schemas must not allow arbitrary additional properties; set this explicitly to false."; }; readonly "x-lix-primary-key": { readonly type: "array"; readonly minItems: 1; readonly uniqueItems: true; readonly items: { readonly type: "string"; readonly format: "json-pointer"; readonly description: "JSON Pointer referencing a property that participates in the primary key."; }; }; readonly "x-lix-foreign-keys": { readonly type: "array"; readonly items: { readonly type: "object"; readonly required: readonly ["properties", "references"]; readonly properties: { readonly properties: { readonly type: "array"; readonly minItems: 1; readonly items: { readonly type: "string"; readonly format: "json-pointer"; readonly description: "JSON Pointer referencing the local field."; }; readonly uniqueItems: true; readonly description: "Local JSON-schema property names that participate in the FK"; }; readonly references: { readonly type: "object"; readonly required: readonly ["schemaKey", "properties"]; readonly properties: { readonly schemaKey: { readonly type: "string"; readonly description: "The x-lix-key of the referenced schema"; }; readonly properties: { readonly type: "array"; readonly minItems: 1; readonly items: { readonly type: "string"; readonly format: "json-pointer"; readonly description: "JSON Pointer referencing the remote field."; }; readonly uniqueItems: true; readonly description: "Remote property names (same length as local properties)"; }; readonly schemaVersion: { readonly type: "string"; readonly pattern: "^\\d+\\.\\d+$"; readonly description: "Optional version of the referenced schema"; }; }; }; readonly mode: { readonly type: "string"; readonly enum: readonly ["immediate", "materialized"]; readonly description: "Validation mode: immediate (default) or materialized (defer insert/update existence checks)"; }; readonly scope: { readonly type: "array"; readonly items: { readonly type: "string"; readonly enum: readonly ["version_id", "file_id"]; }; readonly uniqueItems: true; readonly description: "Implicit columns (version_id and/or file_id) used to scope the foreign key relationship"; }; }; }; }; readonly "x-lix-key": { readonly type: "string"; readonly pattern: "^[a-z][a-z0-9_]*$"; readonly description: "The schema identifier. Must be snake_case (lowercase, underscores) to safely embed in SQL identifiers."; readonly examples: readonly ["csv_plugin_cell"]; }; readonly "x-lix-immutable": { readonly type: "boolean"; readonly description: "When true, entities for this schema cannot be updated after creation."; }; readonly "x-lix-override-lixcols": { readonly type: "object"; readonly description: "Default metadata column values (such as lixcol_file_id). Does not affect JSON property defaults."; readonly additionalProperties: { readonly type: "string"; readonly format: "cel"; }; }; readonly "x-lix-entity-views": { readonly type: "array"; readonly description: "Restricts which SQL entity views (state/state_by_version/state_history) are generated. When omitted, all views are created."; readonly items: { readonly type: "string"; readonly enum: readonly ["state", "state_by_version", "state_history"]; }; readonly uniqueItems: true; }; readonly "x-lix-version": { readonly type: "string"; readonly description: "The version of the schema. Use the major version to signal breaking changes. Use the minor version to signal non-breaking changes."; readonly pattern: "^\\d+\\.\\d+$"; readonly examples: readonly ["1.0"]; }; readonly properties: { readonly type: "object"; readonly additionalProperties: { readonly allOf: readonly [{ readonly $ref: "http://json-schema.org/draft-07/schema#"; }, { readonly type: "object"; readonly properties: { readonly "x-lix-default": { readonly type: "string"; readonly format: "cel"; readonly description: "CEL expression evaluated to produce the default value when the property is omitted."; }; }; }]; }; }; }; readonly required: readonly ["x-lix-key", "x-lix-version", "additionalProperties"]; }]; }; /** * Extended property schema that includes Lix-specific extensions */ type LixPropertySchema = JSONSchema & { "x-lix-default"?: string; }; /** * LixSchema * * A superset of JSON Schema (draft-07) that includes Lix-specific metadata * and supports custom extensions. * * Custom extensions may be added with any x-* prefix. */ export type LixSchemaDefinition = JSONSchema & { /** * The key of the schema. * * The key is used to identify the schema. You must use a * unique key for each schema. * * @example * "csv_plugin_cell" */ "x-lix-key": string; /** * Marks the schema as immutable. Immutable entities may be inserted but cannot be updated. */ "x-lix-immutable"?: boolean; /** * The version of the schema. * * Use the major version to signal breaking changes. * Use the minor version to signal non-breaking changes. * * @example * "1.0" */ "x-lix-version": string; /** * Override metadata column values applied by entity-view rewrites (e.g. `lixcol_file_id`). * Does not provide defaults for JSON properties inside `properties`. Each entry is a CEL * expression evaluated in the same context as property defaults. * * @example * { * "x-lix-override-lixcols": { * "lixcol_file_id": "\"lix\"", * "lixcol_untracked": "0" * } * } */ "x-lix-override-lixcols"?: Record; /** * Restricts which entity views are generated. When omitted, all default * variants (state, state_by_version, state_history) are emitted. * * @example * { * "x-lix-entity-views": ["state"] // only generate the base view * } */ "x-lix-entity-views"?: ("state" | "state_by_version" | "state_history")[] | readonly ("state" | "state_by_version" | "state_history")[]; "x-lix-primary-key"?: JsonPointer[] | readonly JsonPointer[]; /** * Properties that must be unique per version. * * Not to be confused by `x-version` which is used for versioning the schema. * * * @example * { * "x-lix-unique": [ * // the id must be unique * ["/id"], * // the name and age must be unique as well * ["/name", "/age"], * ], * properties: { * id: { type: "string" }, * name: { type: "string" }, * age: { type: "number" }, * }, * } */ "x-lix-unique"?: JsonPointer[][] | readonly (readonly JsonPointer[])[]; /** * Foreign key constraints referencing other schemas. * * @example * [ * { * "properties": ["/author_id"], * "references": { * "schemaKey": "user_profile", * "properties": ["/id"] * } * }, * { * "properties": ["/entity_id", "/schema_key", "/file_id"], * "references": { * "schemaKey": "state", * "properties": ["/entity_id", "/schema_key", "/file_id"] * } * } * ] */ "x-lix-foreign-keys"?: LixForeignKey[] | readonly LixForeignKey[]; type: "object"; /** * Has to be false to know the schema beforehand. */ additionalProperties: false; properties?: { [key: string]: LixPropertySchema; }; }; /** * Marker type for database columns that are auto-generated. * * This type brands values as "generated" to enable special handling in insert/update * operations. Generated fields become optional in inserts since the database * provides default values. * * The type accepts T values directly for developer convenience while preserving * the generated marker for type transformations. * * @example * ```typescript * type Account = { * id: LixGenerated; // Auto-generated UUID * name: string; // User-provided * created_at: LixGenerated; // Auto-generated timestamp * }; * * // In inserts, generated fields are optional * const newAccount: LixInsertable = { * name: "John" // id and created_at are optional * }; * ``` */ export type LixGenerated = T & { readonly __lixGenerated?: true; }; /** * Check if a type has the LixGenerated brand */ type IsLixGenerated = T extends { readonly __lixGenerated?: true; } ? true : false; /** * Extract the base type from LixGenerated * Since LixGenerated = T & { brand }, we need to extract T */ type ExtractFromGenerated = T extends LixGenerated ? U : T; /** * Extract the select type from LixGenerated or return the type as-is */ type SelectType = ExtractFromGenerated; /** * Extract the insert type from LixGenerated or return the type as-is */ type InsertType = IsLixGenerated extends true ? ExtractFromGenerated | undefined : T; /** * Extract the update type from LixGenerated or return the type as-is */ type UpdateType = ExtractFromGenerated; /** * Evaluates to K if T can be null or undefined */ type IfNullable = undefined extends T ? K : null extends T ? K : never; /** * Evaluates to K if T can't be null or undefined */ type IfNotNullable = undefined extends T ? never : null extends T ? never : T extends never ? never : K; /** * Keys whose InsertType can be null or undefined (optional in inserts) */ type NullableInsertKeys = { [K in keyof T]: IfNullable, K>; }[keyof T]; /** * Keys whose InsertType can't be null or undefined (required in inserts) */ type NonNullableInsertKeys = { [K in keyof T]: IfNotNullable, K>; }[keyof T]; /** * Transform a type for insert operations. * * This type makes LixGenerated fields optional while keeping other required * fields mandatory. Use this when defining types for creating new entities. * * The database will automatically populate generated fields (like IDs, * timestamps) if not provided. * * @example * ```typescript * type Account = { * id: LixGenerated; * name: string; * email: string; * created_at: LixGenerated; * }; * * type NewAccount = LixInsertable; * // Result: { name: string; email: string; id?: string; created_at?: string; } * * const account: NewAccount = { * name: "John", * email: "john@example.com" * // id and created_at are optional * }; * ``` */ export type LixInsertable = { [K in NonNullableInsertKeys]: InsertType; } & { [K in NullableInsertKeys]?: InsertType; }; /** * Transform a type for update operations. * * This type makes all fields optional, allowing partial updates where you * only specify the fields you want to change. LixGenerated markers are * removed since you're providing explicit values. * * The database preserves existing values for any fields not included * in the update. * * @example * ```typescript * type Account = { * id: LixGenerated; * name: string; * email: string; * updated_at: LixGenerated; * }; * * type AccountUpdate = LixUpdateable; * // Result: { id?: string; name?: string; email?: string; updated_at?: string; } * * // Update only the email * const updates: AccountUpdate = { * email: "newemail@example.com" * // Other fields remain unchanged * }; * ``` */ export type LixUpdateable = { [K in keyof T]?: UpdateType; }; /** * Transform a type for select/query operations. * * This type unwraps all LixGenerated markers, giving you the actual engine * types that will be returned from database queries. All fields are required * and have their base types. * * Use this type when defining the shape of data returned from queries or * when passing entity data to UI components. * * @example * ```typescript * type Account = { * id: LixGenerated; * name: string; * email: string; * created_at: LixGenerated; * }; * * type AccountData = LixSelectable; * // Result: { id: string; name: string; email: string; created_at: string; } * * // Query results have this shape * const accounts: AccountData[] = await db * .selectFrom("account") * .selectAll() * .execute(); * * console.log(accounts[0].id); // string (not LixGenerated) * ``` */ export type LixSelectable = { [K in keyof T]: SelectType; }; type IsNever = [T] extends [never] ? true : false; type IsAny = 0 extends 1 & T ? true : false; /** * Transform object types with no properties from unknown to Record */ type TransformEmptyObject = IsAny extends true ? any : IsNever extends true ? never : T extends object ? keyof T extends never ? Record : T : T; /** * Check if a schema property is an empty object type (no properties defined) */ type IsEmptyObjectSchema

= P extends { type: "object"; } ? P extends { properties: any; } ? false : true : false; /** * Get the nullable part of a type based on schema */ type GetNullablePart

= P extends { nullable: true; } ? null : never; /** * Internal type that applies LixGenerated markers to properties that provide defaults. */ type PropertyHasDefault

= P extends { "x-lix-default": any; } ? true : P extends { default: any; } ? true : false; type ApplyLixGenerated = TSchema extends { properties: infer Props; } ? { [K in keyof FromSchema]: K extends keyof Props ? PropertyHasDefault extends true ? LixGenerated[K]>> : IsEmptyObjectSchema extends true ? Record | GetNullablePart : TransformEmptyObject[K]> : TransformEmptyObject[K]>; } : never; /** * Convert a LixSchemaDefinition to a TypeScript type. * * This type transformation: * 1. Converts JSON Schema properties to TypeScript types * 2. Wraps properties that declare defaults (`default` or `x-lix-default`) in LixGenerated markers * 3. Transforms `type: "object"` without properties to `Record` * * The resulting type can be used with LixInsertable, LixUpdateable, and * LixSelectable for database operations. * * @example * ```typescript * const AccountSchema = { * "x-lix-key": "account", * "x-lix-version": "1.0", * "x-lix-primary-key": ["/id"], * type: "object", * properties: { * id: { type: "string", default: "auto-generated" }, * name: { type: "string" }, * email: { type: "string" }, * metadata: { type: "object" }, // Becomes Record * created_at: { type: "string", "x-lix-default": "lix_now()" } * }, * required: ["id", "name", "email"], * additionalProperties: false * } as const satisfies LixSchemaDefinition; * * type Account = FromLixSchemaDefinition; * // Result: { * // id: LixGenerated; * // name: string; * // email: string; * // metadata: Record | undefined; * // created_at: LixGenerated | undefined; * // } * ``` */ export type FromLixSchemaDefinition = ApplyLixGenerated; export {}; //# sourceMappingURL=definition.d.ts.map