import type { LixChange } from "../change/schema-definition.js"; import type { LixFile } from "../filesystem/file/schema.js"; import type { LixSchemaDefinition } from "../schema-definition/definition.js"; import type { QuerySync } from "./query-sync.js"; export type LixPlugin = { key: string; /** * The glob pattern that should invoke `detectChanges()`. * * @example * `**\/*.json` for all JSON files * `**\/*.inlang` for all inlang files */ detectChangesGlob?: string; /** * Detects changes between the `before` and `after` file update(s). * * `Before` is `undefined` if the file did not exist before ( * the file was created). * * `After` is always defined. Either the file was updated, or * deleted. If the file is deleted, lix own change control * will handle the deletion. Hence, `after` is always be defined. */ detectChanges?: ({ before, after, querySync, }: { before?: Omit & { data?: Uint8Array; }; after: Omit & { data: Uint8Array; }; /** * Build synchronous SQL queries. * * Detecting changes can require reading current state to preserve stable * entity ids, compare snapshots, or infer ordering without reparsing * files. * * Why is a sync possible and allowed? * * The engine can run in a separate thread/process. SQLite executes * queries synchronously and the engine cannot await async calls to the * host thread during `detectChanges()`. `querySync` provides a Kysely * builder whose `.execute()` runs synchronously inside the engine. * * @example * detectChanges: ({ after, querySync }) => { * const rows = querySync('state') * .where('file_id', '=', after.id) * .where('plugin_key', '=', 'plugin_md') * .select(['entity_id', 'schema_key', 'snapshot_content']) * .execute() * const latestById = new Map(rows.map(r => [r.entity_id, r])) * // ...use latestById to assign/reuse ids in emitted changes... * return [] * } */ querySync: QuerySync; }) => DetectedChange[]; applyChanges?: ({ file, changes, }: { /** * The file to which the changes should be applied. * * The `file.data` might be undefined if the file does not * exist at the time of applying the changes. This can * happen when merging a version that created a new file * that did not exist in the target version. Or, a file * has been deleted and should be restored at a later point. */ file: Omit & { data?: Uint8Array; }; changes: Array; }) => { fileData: Uint8Array; }; /** * UI components that are used to render the diff view. */ /** * Render the diff for this plugin as HTML. * * Hosts can insert the returned markup directly or scope it inside a * ShadowRoot. The HTML should rely on CSS custom properties (e.g. * `--lix-diff-*`) instead of hardcoded colors so environments can theme it. * * @example * const html = await plugin.renderDiff?.({ diffs }) * container.innerHTML = html ?? "" */ renderDiff?: (args: RenderDiffArgs) => Promise; }; /** * A detected change that lix ingests in to the database. * * - If the `snapshot` is `undefined`, the change is considered to be a deletion. * - The `schema` type can be narrowed by providing a change schema. * * @example * Type narrowing with a change schema: * * ``` * const FooV1Schema = { * key: "plugin-name-foo-v1", * type: "json", * schema: { * type: "object", * properties: { * name: { type: "string" }, * } * } * } as const satisfies ChangeSchema; * * const detectedChange: DetectedChange * * detectedChange.snapshot.name // string * ``` */ export type DetectedChange = { entity_id: string; schema: LixSchemaDefinition; /** * The change is considered a deletion if `snapshot_content` is `null`. */ snapshot_content: T | null; }; export type RenderDiffArgs = { diffs: Array & { before_snapshot_content: Record | null; after_snapshot_content: Record | null; }>; }; //# sourceMappingURL=lix-plugin.d.ts.map