{"version":3,"sources":["../../../src/orm/index.ts","../../../src/errors/equippedError.ts","../../../src/instance/index.ts","../../../src/instance/hooks.ts","../../../src/instance/settings.ts","../../../src/orm/errors/introspection.ts","../../../src/orm/errors/migration.ts","../../../src/orm/errors/not-found.ts","../../../src/orm/errors/replay.ts","../../../src/orm/errors/validation.ts","../../../src/orm/fields.ts","../../../src/orm/filter.ts","../../../src/orm/orm-adapter.ts","../../../src/orm/event-log/executor.ts","../../../src/orm/event-log/schema.ts","../../../src/orm/schema.ts","../../../src/orm/event-log/registry.ts","../../../src/orm/event-log/walker.ts","../../../src/orm/event-log/event-log.ts","../../../src/orm/migrations/diff.ts","../../../src/orm/migrations/codegen.ts","../../../src/orm/migrations/apply.ts","../../../src/orm/migrations/pending.ts","../../../src/orm/migrations/validate.ts","../../../src/orm/migrations/migrator.ts","../../../src/orm/query-options.ts","../../../src/orm/relations.ts","../../../src/orm/repo/internals/computeds.ts","../../../src/orm/repo/internals/query-shape.ts","../../../src/orm/schema-validations.ts","../../../src/orm/updates.ts","../../../src/orm/repo/internals/executors.ts","../../../src/orm/repo/internals/preloads.ts","../../../src/orm/repo/builders.ts","../../../src/orm/repo/als.ts","../../../src/orm/repo/repo.ts"],"sourcesContent":["export * from './adapter'\nexport * from './adapters/base'\nexport { OrmAdapter, type AggregateSpec } from './orm-adapter'\nexport * from './errors'\nexport * from './event-log'\nexport * from './migrations'\nexport * from './fields'\nexport * from './filter'\nexport * from './query-options'\nexport * from './relations'\nexport * from './repo'\nexport * from './schema'\nexport * from './schema-validations'\nexport * from './updates'\n","export class EquippedError extends Error {\n\tconstructor(\n\t\tpublic readonly message: string,\n\t\tpublic readonly context: Record<string, unknown>,\n\t\tpublic readonly cause?: unknown,\n\t) {\n\t\tsuper(message, { cause })\n\t}\n}\n","import pino, { type Logger } from 'pino'\nimport { ulid } from 'ulid'\nimport { type Pipe, v } from 'valleyed'\n\nimport { EquippedError } from '../errors'\nimport { type ClassRef, type HookCb, type HookEvent, type HookOptions, type HookRecord, registerHook, runHooks } from './hooks'\nimport { instanceSettingsPipe, type Settings, type SettingsInput } from './settings'\n\nexport type { ClassRef, HookCb, HookEvent, HookOptions }\n\nexport class Instance {\n\tstatic #id: string | undefined\n\tstatic #instance: Instance | undefined\n\tstatic #hooks: Partial<Record<HookEvent, HookRecord[]>> = {}\n\treadonly settings: Readonly<Settings>\n\treadonly log: Logger<never>\n\n\tprivate constructor(settings: Settings) {\n\t\tInstance.#instance = this\n\t\tthis.settings = Object.freeze(settings)\n\t\tthis.log = pino<never>({\n\t\t\tlevel: this.settings.log.level,\n\t\t\tserializers: {\n\t\t\t\terr: pino.stdSerializers.err,\n\t\t\t\terror: pino.stdSerializers.err,\n\t\t\t\treq: pino.stdSerializers.req,\n\t\t\t\tres: pino.stdSerializers.res,\n\t\t\t},\n\t\t\tmixin: () => ({\n\t\t\t\tinstanceId: Instance.#id,\n\t\t\t}),\n\t\t})\n\t\tInstance.#registerOnExitHandler()\n\t}\n\n\talias(id: string) {\n\t\tif (Instance.#id !== undefined) return Instance.crash(new EquippedError('Instance already has an alias', {}))\n\t\tInstance.#id = id\n\t}\n\n\tget id() {\n\t\tif (Instance.#id === undefined) return Instance.crash(new EquippedError('Instance doesnt have an alias yet', {}))\n\t\treturn Instance.#id\n\t}\n\n\tgetScopedName(name: string, key = '.') {\n\t\treturn [this.settings.app.name, name].join(key)\n\t}\n\n\tasync start() {\n\t\ttry {\n\t\t\tawait runHooks(Instance.#hooks['setup'] ?? [])\n\t\t\tawait runHooks(Instance.#hooks['start'] ?? [])\n\t\t} catch (error) {\n\t\t\tInstance.crash(new EquippedError(`Error starting instance`, {}, error))\n\t\t}\n\t}\n\n\tstatic envs<E extends object>(envsPipe: Pipe<unknown, E>): E {\n\t\tconst envValidity = v.validate(envsPipe, process.env)\n\t\tif (!envValidity.valid) {\n\t\t\tInstance.crash(\n\t\t\t\tnew EquippedError(`Environment variables are not valid\\n${envValidity.error.toString()}`, {\n\t\t\t\t\tmessages: envValidity.error.messages,\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t\treturn envValidity.value\n\t}\n\n\tstatic create(settings: SettingsInput) {\n\t\tif (Instance.#instance) return Instance.crash(new EquippedError('Instance has been initialized already', {}))\n\t\tconst settingsValidity = v.validate(instanceSettingsPipe(), settings)\n\t\tif (!settingsValidity.valid) {\n\t\t\tInstance.crash(\n\t\t\t\tnew EquippedError(`Settings are not valid\\n${settingsValidity.error.toString()}`, {\n\t\t\t\t\tmessages: settingsValidity.error.messages,\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\t\treturn new Instance(settingsValidity.value)\n\t}\n\n\tstatic get() {\n\t\tif (!Instance.#instance)\n\t\t\treturn Instance.crash(\n\t\t\t\tnew EquippedError('Has not been initialized. Make sure an instance has been created before you get an instance', {}),\n\t\t\t)\n\t\treturn Instance.#instance\n\t}\n\n\tstatic maybeGet() {\n\t\treturn Instance.#instance\n\t}\n\n\tstatic on(event: HookEvent, cb: HookCb, options?: HookOptions) {\n\t\tInstance.#hooks[event] ??= []\n\t\tconst record: HookRecord = { cb, class: options?.class, after: options?.after ?? [] }\n\t\tregisterHook(Instance.#hooks[event], record)\n\t}\n\n\tstatic #registerOnExitHandler() {\n\t\tconst signals = {\n\t\t\tSIGHUP: 1,\n\t\t\tSIGINT: 2,\n\t\t\tSIGTERM: 15,\n\t\t}\n\n\t\tObject.entries(signals).forEach(([signal, code]) => {\n\t\t\tprocess.on(signal, async () => {\n\t\t\t\tawait runHooks(Instance.#hooks['close'] ?? [], () => {}, true)\n\t\t\t\tprocess.exit(128 + code)\n\t\t\t})\n\t\t})\n\t}\n\n\tstatic resolveBeforeCrash<T>(cb: () => Promise<T>) {\n\t\tconst value = cb()\n\t\tInstance.on('close', async () => await value)\n\t\treturn value\n\t}\n\n\tstatic crash(error: EquippedError): never {\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error(error)\n\t\tprocess.exit(1)\n\t}\n\n\tstatic createId(opts?: { prefix?: string; time?: Date }) {\n\t\treturn `${opts?.prefix ?? ''}${ulid(opts?.time?.getTime())}`\n\t}\n}\n","export type HookEvent = 'setup' | 'start' | 'close'\nexport type HookCb = Promise<unknown | void> | (() => void | unknown | Promise<void | unknown>)\n\nexport type ClassRef = Function & { prototype: unknown; name: string }\nexport type HookOptions = {\n\tclass?: ClassRef\n\tafter?: ClassRef[]\n}\nexport type HookRecord = { cb: HookCb; class?: ClassRef; after: ClassRef[] }\n\nexport function registerHook(hooks: HookRecord[], record: HookRecord): void {\n\tfor (const dep of record.after) {\n\t\tif (!hooks.some((h) => h.class === dep)) {\n\t\t\tconst depName = dep.name || 'unknown'\n\t\t\tconst ownerName = record.class?.name || 'anonymous'\n\t\t\tthrow new Error(`Missing dependency: ${ownerName} declares after: [${depName}], but ${depName} is not registered`)\n\t\t}\n\t}\n\n\tif (record.class) {\n\t\tconst tentative = [...hooks, record]\n\t\tdetectCycle(tentative)\n\t}\n\n\thooks.push(record)\n}\n\nfunction detectCycle(hooks: HookRecord[]): void {\n\tconst classToAfter = new Map<ClassRef, ClassRef[]>()\n\tfor (const h of hooks) {\n\t\tif (!h.class) continue\n\t\tconst existing = classToAfter.get(h.class)\n\t\tif (existing) {\n\t\t\tfor (const dep of h.after) {\n\t\t\t\tif (!existing.includes(dep)) existing.push(dep)\n\t\t\t}\n\t\t} else {\n\t\t\tclassToAfter.set(h.class, [...h.after])\n\t\t}\n\t}\n\n\tconst visited = new Set<ClassRef>()\n\tconst stack = new Set<ClassRef>()\n\n\tfunction visit(cls: ClassRef, path: ClassRef[]): void {\n\t\tif (stack.has(cls)) {\n\t\t\tconst cycleStart = path.indexOf(cls)\n\t\t\tconst cycle = [...path.slice(cycleStart), cls]\n\t\t\tconst names = cycle.map((c) => c.name || 'unknown')\n\t\t\tthrow new Error(`Cycle detected: ${names.join(' → ')}`)\n\t\t}\n\t\tif (visited.has(cls)) return\n\t\tstack.add(cls)\n\t\tpath.push(cls)\n\t\tfor (const dep of classToAfter.get(cls) ?? []) {\n\t\t\tvisit(dep, path)\n\t\t}\n\t\tpath.pop()\n\t\tstack.delete(cls)\n\t\tvisited.add(cls)\n\t}\n\n\tfor (const cls of classToAfter.keys()) {\n\t\tvisit(cls, [])\n\t}\n}\n\nexport type HookLayer = HookRecord[]\n\nexport function resolveHookDAG(hooks: HookRecord[], invert: boolean = false): HookLayer[] {\n\tif (hooks.length === 0) return []\n\n\tconst classToHooks = new Map<ClassRef, HookRecord[]>()\n\tconst anonymous: HookRecord[] = []\n\n\tfor (const h of hooks) {\n\t\tif (h.class) {\n\t\t\tconst list = classToHooks.get(h.class)\n\t\t\tif (list) list.push(h)\n\t\t\telse classToHooks.set(h.class, [h])\n\t\t} else {\n\t\t\tanonymous.push(h)\n\t\t}\n\t}\n\n\tconst classes = [...classToHooks.keys()]\n\n\tconst inDegree = new Map<ClassRef, number>()\n\tconst graph = new Map<ClassRef, ClassRef[]>()\n\n\tfor (const cls of classes) {\n\t\tinDegree.set(cls, 0)\n\t\tgraph.set(cls, [])\n\t}\n\n\tfor (const h of hooks) {\n\t\tif (!h.class) continue\n\t\tfor (const dep of h.after) {\n\t\t\tconst [from, to] = invert ? [h.class, dep] : [dep, h.class]\n\t\t\tconst edges = graph.get(from)!\n\t\t\tif (!edges.includes(to)) {\n\t\t\t\tedges.push(to)\n\t\t\t\tinDegree.set(to, inDegree.get(to)! + 1)\n\t\t\t}\n\t\t}\n\t}\n\n\tconst layers: HookLayer[] = []\n\tlet queue = classes.filter((c) => inDegree.get(c) === 0)\n\n\twhile (queue.length > 0) {\n\t\tconst layer: HookRecord[] = []\n\t\tconst next: ClassRef[] = []\n\n\t\tfor (const cls of queue) {\n\t\t\tlayer.push(...(classToHooks.get(cls) ?? []))\n\t\t}\n\t\tlayers.push(layer)\n\n\t\tfor (const cls of queue) {\n\t\t\tfor (const neighbor of graph.get(cls) ?? []) {\n\t\t\t\tconst d = inDegree.get(neighbor)! - 1\n\t\t\t\tinDegree.set(neighbor, d)\n\t\t\t\tif (d === 0) next.push(neighbor)\n\t\t\t}\n\t\t}\n\n\t\tqueue = next\n\t}\n\n\tif (anonymous.length > 0) {\n\t\tconst anonWithDeps = anonymous.filter((h) => h.after.length > 0)\n\t\tconst anonWithoutDeps = anonymous.filter((h) => h.after.length === 0)\n\n\t\tif (anonWithDeps.length > 0) {\n\t\t\tlet maxLayer = -1\n\t\t\tfor (const h of anonWithDeps) {\n\t\t\t\tfor (const dep of h.after) {\n\t\t\t\t\tconst depLayer = layers.findIndex((l) => l.some((r) => r.class === dep))\n\t\t\t\t\tif (depLayer > maxLayer) maxLayer = depLayer\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst insertAt = maxLayer + 1\n\t\t\tif (insertAt < layers.length) {\n\t\t\t\tlayers[insertAt].push(...anonWithDeps)\n\t\t\t} else {\n\t\t\t\tlayers.push(anonWithDeps)\n\t\t\t}\n\t\t}\n\n\t\tif (anonWithoutDeps.length > 0) {\n\t\t\tif (layers.length > 0) {\n\t\t\t\tlayers[layers.length - 1].push(...anonWithoutDeps)\n\t\t\t} else {\n\t\t\t\tlayers.push(anonWithoutDeps)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn layers\n}\n\nexport async function runHooks(\n\thooks: HookRecord[],\n\tonError: (error: Error) => void = (error) => {\n\t\tthrow error\n\t},\n\tinvert: boolean = false,\n) {\n\tconst layers = resolveHookDAG(hooks, invert)\n\tfor (const layer of layers)\n\t\tawait Promise.all(\n\t\t\tlayer.map(async (h) => {\n\t\t\t\ttry {\n\t\t\t\t\tif (typeof h.cb === 'function') return await h.cb()\n\t\t\t\t\treturn await h.cb\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn onError(error instanceof Error ? error : new Error(`${error}`))\n\t\t\t\t}\n\t\t\t}),\n\t\t)\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\n\tclass A {}\n\tclass B {}\n\tclass C {}\n\tclass D {}\n\n\tconst noop = () => {}\n\n\tdescribe('registerHook', () => {\n\t\ttest('registers a hook with no dependencies', () => {\n\t\t\tconst hooks: HookRecord[] = []\n\t\t\tregisterHook(hooks, { cb: noop, class: A, after: [] })\n\t\t\texpect(hooks).toHaveLength(1)\n\t\t})\n\n\t\ttest('throws on missing dependency', () => {\n\t\t\tconst hooks: HookRecord[] = []\n\t\t\texpect(() => registerHook(hooks, { cb: noop, class: B, after: [A] })).toThrow(/Missing dependency.*B.*A/)\n\t\t})\n\n\t\ttest('throws on cycle: A→B→A', () => {\n\t\t\tconst hooks: HookRecord[] = []\n\t\t\tregisterHook(hooks, { cb: noop, class: A, after: [] })\n\t\t\tregisterHook(hooks, { cb: noop, class: B, after: [A] })\n\t\t\texpect(() => registerHook(hooks, { cb: noop, class: A, after: [B] })).toThrow(/Cycle detected/)\n\t\t})\n\n\t\ttest('throws on transitive cycle: A→B→C→A', () => {\n\t\t\tconst hooks: HookRecord[] = []\n\t\t\tregisterHook(hooks, { cb: noop, class: A, after: [] })\n\t\t\tregisterHook(hooks, { cb: noop, class: B, after: [A] })\n\t\t\tregisterHook(hooks, { cb: noop, class: C, after: [B] })\n\t\t\texpect(() => registerHook(hooks, { cb: noop, class: A, after: [C] })).toThrow(/Cycle detected/)\n\t\t})\n\n\t\ttest('allows anonymous hooks with dependencies', () => {\n\t\t\tconst hooks: HookRecord[] = []\n\t\t\tregisterHook(hooks, { cb: noop, class: A, after: [] })\n\t\t\tregisterHook(hooks, { cb: noop, after: [A] })\n\t\t\texpect(hooks).toHaveLength(2)\n\t\t})\n\n\t\ttest('anonymous hook throws on missing dependency', () => {\n\t\t\tconst hooks: HookRecord[] = []\n\t\t\texpect(() => registerHook(hooks, { cb: noop, after: [A] })).toThrow(/Missing dependency.*anonymous.*A/)\n\t\t})\n\t})\n\n\tdescribe('resolveHookDAG', () => {\n\t\ttest('linear chain: A → B → C', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t\t{ cb: noop, class: C, after: [B] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks)\n\t\t\texpect(layers).toHaveLength(3)\n\t\t\texpect(layers[0].every((h) => h.class === A)).toBe(true)\n\t\t\texpect(layers[1].every((h) => h.class === B)).toBe(true)\n\t\t\texpect(layers[2].every((h) => h.class === C)).toBe(true)\n\t\t})\n\n\t\ttest('fan-out: A → B, A → C (B and C are parallel)', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t\t{ cb: noop, class: C, after: [A] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks)\n\t\t\texpect(layers).toHaveLength(2)\n\t\t\texpect(layers[0].every((h) => h.class === A)).toBe(true)\n\t\t\tconst secondClasses = layers[1].map((h) => h.class)\n\t\t\texpect(secondClasses).toContain(B)\n\t\t\texpect(secondClasses).toContain(C)\n\t\t})\n\n\t\ttest('fan-in: B → D, C → D', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: B, after: [] },\n\t\t\t\t{ cb: noop, class: C, after: [] },\n\t\t\t\t{ cb: noop, class: D, after: [B, C] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks)\n\t\t\texpect(layers).toHaveLength(2)\n\t\t\tconst firstClasses = layers[0].map((h) => h.class)\n\t\t\texpect(firstClasses).toContain(B)\n\t\t\texpect(firstClasses).toContain(C)\n\t\t\texpect(layers[1].every((h) => h.class === D)).toBe(true)\n\t\t})\n\n\t\ttest('diamond: A → B, A → C, B → D, C → D', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t\t{ cb: noop, class: C, after: [A] },\n\t\t\t\t{ cb: noop, class: D, after: [B, C] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks)\n\t\t\texpect(layers).toHaveLength(3)\n\t\t\texpect(layers[0][0].class).toBe(A)\n\t\t\tconst midClasses = layers[1].map((h) => h.class)\n\t\t\texpect(midClasses).toContain(B)\n\t\t\texpect(midClasses).toContain(C)\n\t\t\texpect(layers[2][0].class).toBe(D)\n\t\t})\n\n\t\ttest('close-event inversion reverses the graph', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t\t{ cb: noop, class: C, after: [B] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks, true)\n\t\t\texpect(layers).toHaveLength(3)\n\t\t\texpect(layers[0].every((h) => h.class === C)).toBe(true)\n\t\t\texpect(layers[1].every((h) => h.class === B)).toBe(true)\n\t\t\texpect(layers[2].every((h) => h.class === A)).toBe(true)\n\t\t})\n\n\t\ttest('close inversion: fan-out becomes fan-in', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t\t{ cb: noop, class: C, after: [A] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks, true)\n\t\t\texpect(layers).toHaveLength(2)\n\t\t\tconst firstClasses = layers[0].map((h) => h.class)\n\t\t\texpect(firstClasses).toContain(B)\n\t\t\texpect(firstClasses).toContain(C)\n\t\t\texpect(layers[1].every((h) => h.class === A)).toBe(true)\n\t\t})\n\n\t\ttest('multiple hooks per class are all in the same layer', () => {\n\t\t\tconst cb1 = () => {}\n\t\t\tconst cb2 = () => {}\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: cb1, class: A, after: [] },\n\t\t\t\t{ cb: cb2, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks)\n\t\t\texpect(layers).toHaveLength(2)\n\t\t\texpect(layers[0]).toHaveLength(2)\n\t\t\texpect(layers[0].every((h) => h.class === A)).toBe(true)\n\t\t})\n\n\t\ttest('anonymous hooks without deps run at deepest level', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t\t{ cb: noop, after: [] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks)\n\t\t\texpect(layers).toHaveLength(2)\n\t\t\tconst lastLayer = layers[layers.length - 1]\n\t\t\texpect(lastLayer.some((h) => h.class === undefined)).toBe(true)\n\t\t})\n\n\t\ttest('anonymous hooks with deps run after their dependencies', () => {\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: noop, class: A, after: [] },\n\t\t\t\t{ cb: noop, class: B, after: [A] },\n\t\t\t\t{ cb: noop, class: C, after: [B] },\n\t\t\t\t{ cb: noop, after: [A] },\n\t\t\t]\n\t\t\tconst layers = resolveHookDAG(hooks)\n\t\t\tconst anonLayer = layers.findIndex((l) => l.some((h) => h.class === undefined))\n\t\t\tconst aLayer = layers.findIndex((l) => l.some((h) => h.class === A))\n\t\t\texpect(anonLayer).toBeGreaterThan(aLayer)\n\t\t})\n\n\t\ttest('empty hooks returns empty layers', () => {\n\t\t\texpect(resolveHookDAG([])).toEqual([])\n\t\t})\n\t})\n\n\tdescribe('runHooks', () => {\n\t\ttest('same-depth siblings observably overlap', async () => {\n\t\t\tconst log: string[] = []\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{\n\t\t\t\t\tcb: async () => {\n\t\t\t\t\t\tlog.push('B-start')\n\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 20))\n\t\t\t\t\t\tlog.push('B-end')\n\t\t\t\t\t},\n\t\t\t\t\tclass: B,\n\t\t\t\t\tafter: [],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcb: async () => {\n\t\t\t\t\t\tlog.push('C-start')\n\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 20))\n\t\t\t\t\t\tlog.push('C-end')\n\t\t\t\t\t},\n\t\t\t\t\tclass: C,\n\t\t\t\t\tafter: [],\n\t\t\t\t},\n\t\t\t]\n\t\t\tawait runHooks(hooks)\n\t\t\texpect(log[0]).toBe('B-start')\n\t\t\texpect(log[1]).toBe('C-start')\n\t\t})\n\n\t\ttest('different depths run sequentially', async () => {\n\t\t\tconst log: string[] = []\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{\n\t\t\t\t\tcb: async () => {\n\t\t\t\t\t\tlog.push('A')\n\t\t\t\t\t},\n\t\t\t\t\tclass: A,\n\t\t\t\t\tafter: [],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tcb: async () => {\n\t\t\t\t\t\tlog.push('B')\n\t\t\t\t\t},\n\t\t\t\t\tclass: B,\n\t\t\t\t\tafter: [A],\n\t\t\t\t},\n\t\t\t]\n\t\t\tawait runHooks(hooks)\n\t\t\texpect(log).toEqual(['A', 'B'])\n\t\t})\n\n\t\ttest('close inversion runs hooks in reverse dependency order', async () => {\n\t\t\tconst log: string[] = []\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{ cb: async () => log.push('A'), class: A, after: [] },\n\t\t\t\t{ cb: async () => log.push('B'), class: B, after: [A] },\n\t\t\t\t{ cb: async () => log.push('C'), class: C, after: [B] },\n\t\t\t]\n\t\t\tawait runHooks(hooks, undefined, true)\n\t\t\texpect(log).toEqual(['C', 'B', 'A'])\n\t\t})\n\n\t\ttest('errors are routed to onError handler', async () => {\n\t\t\tconst errors: Error[] = []\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{\n\t\t\t\t\tcb: () => {\n\t\t\t\t\t\tthrow new Error('boom')\n\t\t\t\t\t},\n\t\t\t\t\tclass: A,\n\t\t\t\t\tafter: [],\n\t\t\t\t},\n\t\t\t]\n\t\t\tawait runHooks(hooks, (e) => {\n\t\t\t\terrors.push(e)\n\t\t\t})\n\t\t\texpect(errors).toHaveLength(1)\n\t\t\texpect(errors[0].message).toBe('boom')\n\t\t})\n\n\t\ttest('raw promise callbacks are awaited', async () => {\n\t\t\tconst log: string[] = []\n\t\t\tconst hooks: HookRecord[] = [\n\t\t\t\t{\n\t\t\t\t\tcb: Promise.resolve().then(() => log.push('resolved')),\n\t\t\t\t\tclass: A,\n\t\t\t\t\tafter: [],\n\t\t\t\t},\n\t\t\t]\n\t\t\tawait runHooks(hooks)\n\t\t\texpect(log).toContain('resolved')\n\t\t})\n\t})\n}\n","import { type ConditionalObjectKeys, type PipeInput, type PipeOutput, v } from 'valleyed'\n\nexport const instanceSettingsPipe = () =>\n\tv.object({\n\t\tapp: v.object({\n\t\t\tname: v.string(),\n\t\t}),\n\t\tlog: v.defaults(\n\t\t\tv.object({\n\t\t\t\tlevel: v.defaults(v.in(['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent'] as const), 'info'),\n\t\t\t}),\n\t\t\t{},\n\t\t),\n\t\tutils: v.defaults(\n\t\t\tv.object({\n\t\t\t\thashSaltRounds: v.defaults(v.number(), 10),\n\t\t\t\tpaginationDefaultLimit: v.defaults(v.number(), 100),\n\t\t\t\tmaxFileUploadSizeInMb: v.defaults(v.number(), 500),\n\t\t\t}),\n\t\t\t{},\n\t\t),\n\t})\n\nexport type Settings = PipeOutput<ReturnType<typeof instanceSettingsPipe>>\nexport type SettingsInput = ConditionalObjectKeys<PipeInput<ReturnType<typeof instanceSettingsPipe>>>\n","import { EquippedError } from '../../errors'\n\nexport class OrmIntrospectionError extends EquippedError {\n\treadonly adapter: string\n\treadonly table: string\n\n\tconstructor(opts: { adapter: string; table: string; cause: unknown }) {\n\t\tsuper(`Introspection failed for table '${opts.table}' on adapter '${opts.adapter}'`, { adapter: opts.adapter, table: opts.table }, opts.cause)\n\t\tthis.adapter = opts.adapter\n\t\tthis.table = opts.table\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { EquippedError } = await import('../../errors')\n\n\tdescribe('OrmIntrospectionError', () => {\n\t\ttest('stores adapter, table, and cause on the instance', () => {\n\t\t\tconst cause = new Error('unknown column type')\n\t\t\tconst err = new OrmIntrospectionError({ adapter: 'postgres', table: 'users', cause })\n\t\t\texpect(err.adapter).toBe('postgres')\n\t\t\texpect(err.table).toBe('users')\n\t\t\texpect(err.cause).toBe(cause)\n\t\t})\n\n\t\ttest('message includes adapter and table', () => {\n\t\t\tconst err = new OrmIntrospectionError({ adapter: 'pg', table: 'posts', cause: 'bad type' })\n\t\t\texpect(err.message).toBe(\"Introspection failed for table 'posts' on adapter 'pg'\")\n\t\t})\n\n\t\ttest('instanceof EquippedError is true', () => {\n\t\t\tconst err = new OrmIntrospectionError({ adapter: 'x', table: 'y', cause: null })\n\t\t\texpect(err).toBeInstanceOf(OrmIntrospectionError)\n\t\t\texpect(err).toBeInstanceOf(EquippedError)\n\t\t})\n\n\t\ttest('context carries adapter and table', () => {\n\t\t\tconst err = new OrmIntrospectionError({ adapter: 'mem', table: 'items', cause: 'fail' })\n\t\t\texpect((err.context as any).adapter).toBe('mem')\n\t\t\texpect((err.context as any).table).toBe('items')\n\t\t})\n\n\t\ttest('discriminates from sibling error classes', async () => {\n\t\t\tconst { OrmMigrationError } = await import('./migration')\n\t\t\tconst { OrmValidationError } = await import('./validation')\n\t\t\tconst err = new OrmIntrospectionError({ adapter: 'x', table: 'y', cause: null })\n\t\t\texpect(err).not.toBeInstanceOf(OrmMigrationError)\n\t\t\texpect(err).not.toBeInstanceOf(OrmValidationError)\n\t\t})\n\t})\n}\n","import { EquippedError } from '../../errors'\n\nexport type OrmMigrationPhase = 'lock' | 'load' | 'session' | 'user' | 'record'\n\nexport class OrmMigrationError extends EquippedError {\n\treadonly id: string\n\treadonly phase: OrmMigrationPhase\n\n\tconstructor(opts: { id: string; phase: OrmMigrationPhase; cause: unknown }) {\n\t\tsuper(`Migration failed at phase '${opts.phase}' for migration '${opts.id}'`, { id: opts.id, phase: opts.phase }, opts.cause)\n\t\tthis.id = opts.id\n\t\tthis.phase = opts.phase\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { EquippedError } = await import('../../errors')\n\n\tdescribe('OrmMigrationError', () => {\n\t\ttest('stores id, phase, and cause on the instance', () => {\n\t\t\tconst cause = new Error('connection lost')\n\t\t\tconst err = new OrmMigrationError({ id: '0001-add-users', phase: 'user', cause })\n\t\t\texpect(err.id).toBe('0001-add-users')\n\t\t\texpect(err.phase).toBe('user')\n\t\t\texpect(err.cause).toBe(cause)\n\t\t})\n\n\t\ttest('message includes phase and migration id', () => {\n\t\t\tconst err = new OrmMigrationError({ id: '0002-add-index', phase: 'session', cause: 'boom' })\n\t\t\texpect(err.message).toBe(\"Migration failed at phase 'session' for migration '0002-add-index'\")\n\t\t})\n\n\t\ttest('instanceof EquippedError is true', () => {\n\t\t\tconst err = new OrmMigrationError({ id: 'x', phase: 'lock', cause: null })\n\t\t\texpect(err).toBeInstanceOf(OrmMigrationError)\n\t\t\texpect(err).toBeInstanceOf(EquippedError)\n\t\t})\n\n\t\ttest('discriminates from sibling error classes', async () => {\n\t\t\tconst { OrmValidationError } = await import('./validation')\n\t\t\tconst { OrmNotFoundError } = await import('./not-found')\n\t\t\tconst { OrmReplayError } = await import('./replay')\n\t\t\tconst err = new OrmMigrationError({ id: 'x', phase: 'load', cause: null })\n\t\t\texpect(err).not.toBeInstanceOf(OrmValidationError)\n\t\t\texpect(err).not.toBeInstanceOf(OrmNotFoundError)\n\t\t\texpect(err).not.toBeInstanceOf(OrmReplayError)\n\t\t})\n\n\t\ttest('context carries id and phase', () => {\n\t\t\tconst err = new OrmMigrationError({ id: '0003-drop-table', phase: 'record', cause: 'fail' })\n\t\t\texpect((err.context as any).id).toBe('0003-drop-table')\n\t\t\texpect((err.context as any).phase).toBe('record')\n\t\t})\n\n\t\ttest.each(['lock', 'load', 'session', 'user', 'record'] as const)('phase %s works', (phase) => {\n\t\t\tconst err = new OrmMigrationError({ id: 'test', phase, cause: null })\n\t\t\texpect(err.phase).toBe(phase)\n\t\t})\n\t})\n}\n","import { EquippedError } from '../../errors'\nimport { Filter, FilterGroup } from '../filter'\n\nexport type OrmNotFoundOperation = 'findOne' | 'updateOne' | 'deleteOne'\n\nfunction renderFilter(group: FilterGroup): string {\n\tif (\n\t\tgroup.op === 'and'\n\t\t&& group.children.length === 1\n\t\t&& group.children[0] instanceof Filter\n\t\t&& group.children[0].op === 'eq'\n\t) {\n\t\tconst f = group.children[0]\n\t\treturn `${f.field}=${f.value}`\n\t}\n\treturn renderFilterTree(group)\n}\n\nfunction renderFilterTree(node: FilterGroup | Filter): string {\n\tif (node instanceof Filter) {\n\t\treturn `${node.field} ${node.op} ${JSON.stringify(node.value)}`\n\t}\n\tconst parts = node.children.map((c) => renderFilterTree(c))\n\tif (parts.length === 1) return parts[0]\n\treturn `(${parts.join(` ${node.op} `)})`\n}\n\nexport class OrmNotFoundError extends EquippedError {\n\treadonly schema: string\n\treadonly operation: OrmNotFoundOperation\n\treadonly where: FilterGroup\n\n\tconstructor(opts: {\n\t\tschema: string\n\t\toperation: OrmNotFoundOperation\n\t\twhere: FilterGroup\n\t\tmessage?: string\n\t}) {\n\t\tconst msg = opts.message ?? `${opts.schema}.${opts.operation}: no row matched ${renderFilter(opts.where)}`\n\t\tsuper(msg, {\n\t\t\tschema: opts.schema,\n\t\t\toperation: opts.operation,\n\t\t\twhere: opts.where,\n\t\t})\n\t\tthis.schema = opts.schema\n\t\tthis.operation = opts.operation\n\t\tthis.where = opts.where\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { OrmValidationError } = await import('./validation')\n\n\tdescribe('OrmNotFoundError', () => {\n\t\tdescribe('carrier construction', () => {\n\t\t\ttest('stores schema, operation, and where on the instance', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'u1')\n\t\t\t\tconst err = new OrmNotFoundError({ schema: 'users', operation: 'findOne', where })\n\t\t\t\texpect(err.schema).toBe('users')\n\t\t\t\texpect(err.operation).toBe('findOne')\n\t\t\t\texpect(err.where).toBe(where)\n\t\t\t})\n\n\t\t\ttest('accepts all three operation literals', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'u1')\n\t\t\t\tfor (const op of ['findOne', 'updateOne', 'deleteOne'] as const) {\n\t\t\t\t\tconst err = new OrmNotFoundError({ schema: 'users', operation: op, where })\n\t\t\t\t\texpect(err.operation).toBe(op)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tdescribe('default message generation', () => {\n\t\t\ttest('PK-keyed filter renders as id=<pk>', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'abc-123')\n\t\t\t\tconst err = new OrmNotFoundError({ schema: 'users', operation: 'findOne', where })\n\t\t\t\texpect(err.message).toBe('users.findOne: no row matched id=abc-123')\n\t\t\t})\n\n\t\t\ttest('filter-based rendering shows the filter tree', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('email', 'a@b.com').gt('age', 18)\n\t\t\t\tconst err = new OrmNotFoundError({ schema: 'users', operation: 'updateOne', where })\n\t\t\t\texpect(err.message).toBe('users.updateOne: no row matched (email eq \"a@b.com\" and age gt 18)')\n\t\t\t})\n\n\t\t\ttest('single non-eq filter renders without parens', () => {\n\t\t\t\tconst where = FilterGroup.create().gt('age', 18)\n\t\t\t\tconst err = new OrmNotFoundError({ schema: 'users', operation: 'deleteOne', where })\n\t\t\t\texpect(err.message).toBe('users.deleteOne: no row matched age gt 18')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('custom message override', () => {\n\t\t\ttest('custom message replaces the default', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'u1')\n\t\t\t\tconst err = new OrmNotFoundError({\n\t\t\t\t\tschema: 'users',\n\t\t\t\t\toperation: 'findOne',\n\t\t\t\t\twhere,\n\t\t\t\t\tmessage: 'user must exist',\n\t\t\t\t})\n\t\t\t\texpect(err.message).toBe('user must exist')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('instanceof discrimination', () => {\n\t\t\ttest('instanceof OrmNotFoundError is true', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'u1')\n\t\t\t\tconst err = new OrmNotFoundError({ schema: 'users', operation: 'findOne', where })\n\t\t\t\texpect(err).toBeInstanceOf(OrmNotFoundError)\n\t\t\t})\n\n\t\t\ttest('instanceof EquippedError is true', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'u1')\n\t\t\t\tconst err = new OrmNotFoundError({ schema: 'users', operation: 'findOne', where })\n\t\t\t\texpect(err).toBeInstanceOf(EquippedError)\n\t\t\t})\n\n\t\t\ttest('instanceof OrmNotFoundError discriminates from OrmValidationError', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'u1')\n\t\t\t\tconst notFound = new OrmNotFoundError({ schema: 'users', operation: 'findOne', where })\n\t\t\t\tconst validation = new OrmValidationError('validation', 'users', 'createOne', [])\n\t\t\t\texpect(notFound).not.toBeInstanceOf(OrmValidationError)\n\t\t\t\texpect(validation).not.toBeInstanceOf(OrmNotFoundError)\n\t\t\t})\n\n\t\t\ttest('both are instanceof EquippedError', () => {\n\t\t\t\tconst where = FilterGroup.create().eq('id', 'u1')\n\t\t\t\tconst notFound = new OrmNotFoundError({ schema: 'users', operation: 'findOne', where })\n\t\t\t\tconst validation = new OrmValidationError('validation', 'users', 'createOne', [])\n\t\t\t\texpect(notFound).toBeInstanceOf(EquippedError)\n\t\t\t\texpect(validation).toBeInstanceOf(EquippedError)\n\t\t\t})\n\t\t})\n\t})\n}\n","import { EquippedError } from '../../errors'\n\nexport class OrmReplayError extends EquippedError {\n\treadonly key: string\n\treadonly eventName: string\n\n\tconstructor(opts: { key: string; name: string; cause: unknown }) {\n\t\tsuper(`EventLog replay failed on event ${opts.name} (key=${opts.key})`, { key: opts.key, name: opts.name }, opts.cause)\n\t\tthis.key = opts.key\n\t\tthis.eventName = opts.name\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { EquippedError } = await import('../../errors')\n\tconst { OrmValidationError } = await import('./validation')\n\tconst { OrmNotFoundError } = await import('./not-found')\n\n\tdescribe('OrmReplayError', () => {\n\t\ttest('stores key, eventName, and cause on the instance', () => {\n\t\t\tconst cause = new Error('handler blew up')\n\t\t\tconst err = new OrmReplayError({ key: 'evt-123', name: 'user.signup', cause })\n\t\t\texpect(err.key).toBe('evt-123')\n\t\t\texpect(err.eventName).toBe('user.signup')\n\t\t\texpect(err.cause).toBe(cause)\n\t\t})\n\n\t\ttest('message includes handler name and key', () => {\n\t\t\tconst err = new OrmReplayError({ key: 'evt-456', name: 'order.placed', cause: 'boom' })\n\t\t\texpect(err.message).toBe('EventLog replay failed on event order.placed (key=evt-456)')\n\t\t})\n\n\t\ttest('instanceof EquippedError is true', () => {\n\t\t\tconst err = new OrmReplayError({ key: 'k', name: 'n', cause: null })\n\t\t\texpect(err).toBeInstanceOf(OrmReplayError)\n\t\t\texpect(err).toBeInstanceOf(EquippedError)\n\t\t})\n\n\t\ttest('discriminates from OrmValidationError and OrmNotFoundError', () => {\n\t\t\tconst err = new OrmReplayError({ key: 'k', name: 'n', cause: null })\n\t\t\texpect(err).not.toBeInstanceOf(OrmValidationError)\n\t\t\texpect(err).not.toBeInstanceOf(OrmNotFoundError)\n\t\t})\n\n\t\ttest('context carries key and name', () => {\n\t\t\tconst err = new OrmReplayError({ key: 'evt-789', name: 'payment.charged', cause: 'fail' })\n\t\t\texpect((err.context as any).key).toBe('evt-789')\n\t\t\texpect((err.context as any).name).toBe('payment.charged')\n\t\t})\n\t})\n}\n","import { EquippedError } from '../../errors'\n\nexport type OrmValidationErrorKind = 'validation' | 'conflicting-ops' | 'empty-group' | 'undeclared-op' | 'upsert-filter-incompatible' | 'aggregate' | 'query-shape' | 'changes'\n\nexport type OrmValidationFailure = {\n\topIndex?: number\n\trowIndex?: number\n\tfield?: string\n\talias?: string\n\toption?: 'limit' | 'offset' | 'page' | 'batchSize'\n\tmigrationId?: string\n\tchangeIndex?: number\n\tcause: unknown\n}\n\nexport class OrmValidationError extends EquippedError {\n\tconstructor(\n\t\treadonly kind: OrmValidationErrorKind,\n\t\treadonly schema: string,\n\t\treadonly operation: string,\n\t\treadonly failures: OrmValidationFailure[],\n\t) {\n\t\tsuper(`ORM validation error (${kind}) on ${schema}.${operation}`, {\n\t\t\tkind,\n\t\t\tschema,\n\t\t\toperation,\n\t\t\tfailures,\n\t\t})\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { EquippedError } = await import('../../errors')\n\n\tdescribe('OrmValidationError', () => {\n\t\ttest('constructs with aggregate kind', () => {\n\t\t\tconst err = new OrmValidationError('aggregate', 'orders', 'aggregate', [])\n\t\t\texpect(err.kind).toBe('aggregate')\n\t\t\texpect(err.operation).toBe('aggregate')\n\t\t\texpect(err.message).toBe('ORM validation error (aggregate) on orders.aggregate')\n\t\t\texpect(err).toBeInstanceOf(OrmValidationError)\n\t\t\texpect(err).toBeInstanceOf(EquippedError)\n\t\t})\n\n\t\ttest('failure carries alias through to context', () => {\n\t\t\tconst failures: OrmValidationFailure[] = [\n\t\t\t\t{ alias: 'total_price', cause: 'alias collision' },\n\t\t\t]\n\t\t\tconst err = new OrmValidationError('aggregate', 'orders', 'aggregate', failures)\n\t\t\texpect(err.failures[0].alias).toBe('total_price')\n\t\t\texpect((err.context as any).failures[0].alias).toBe('total_price')\n\t\t})\n\n\t\ttest('failure without alias still works', () => {\n\t\t\tconst failures: OrmValidationFailure[] = [\n\t\t\t\t{ field: 'amount', cause: 'invalid field' },\n\t\t\t]\n\t\t\tconst err = new OrmValidationError('validation', 'orders', 'createOne', failures)\n\t\t\texpect(err.failures[0].alias).toBeUndefined()\n\t\t\texpect(err.failures[0].field).toBe('amount')\n\t\t})\n\n\t\ttest.each([\n\t\t\t'validation',\n\t\t\t'conflicting-ops',\n\t\t\t'empty-group',\n\t\t\t'undeclared-op',\n\t\t\t'upsert-filter-incompatible',\n\t\t\t'query-shape',\n\t\t\t'changes',\n\t\t] as const)('kind %s still works', (kind) => {\n\t\t\tconst err = new OrmValidationError(kind, 'users', 'createOne', [])\n\t\t\texpect(err.kind).toBe(kind)\n\t\t})\n\t})\n}\n","import { v, type Pipe, type PipeOutput } from 'valleyed'\n\nexport class Field<T = unknown, Name extends string = string, S = unknown> {\n\tdeclare readonly __valueType?: T\n\tdeclare readonly __schema?: S\n\treadonly name: Name\n\treadonly path: readonly string[]\n\n\tconstructor(name: Name, path?: readonly string[]) {\n\t\tthis.name = name\n\t\tthis.path = path ?? [name]\n\t}\n}\n\nexport type AnyField = Field<unknown, string, any>\n\nexport function toFieldName(field: string | AnyField): string {\n\tif (field instanceof Field) return field.path.join('.')\n\treturn field\n}\n\nexport class SchemaField<\n\tName extends string = string,\n\tP extends Pipe<any, any> = Pipe<any, any>,\n\tHasOnCreate extends boolean = false,\n> extends Field<PipeOutput<P>, Name> {\n\tdeclare readonly __hasOnCreate?: HasOnCreate\n\treadonly pipe: P\n\treadonly onCreate?: () => PipeOutput<P>\n\treadonly onUpdate?: () => PipeOutput<P>\n\n\tconstructor(name: Name, pipe: P, opts?: { onCreate?: () => PipeOutput<P>; onUpdate?: () => PipeOutput<P> }) {\n\t\tsuper(name)\n\t\tthis.pipe = pipe\n\t\tthis.onCreate = opts?.onCreate\n\t\tthis.onUpdate = opts?.onUpdate\n\t}\n}\n\nexport type AnySchemaField = SchemaField<string, Pipe<any, any>, boolean>\n\nexport class ComputedField<\n\tName extends string = string,\n\tP extends Pipe<any, any> = Pipe<any, any>,\n\tDeps extends readonly string[] = readonly string[],\n> {\n\tconstructor(\n\t\treadonly name: Name,\n\t\treadonly pipe: P,\n\t\treadonly deps: Deps,\n\t\treadonly compute: (data: Record<Deps[number], unknown>) => PipeOutput<P>,\n\t) {}\n}\n\nexport type AnyComputedField = ComputedField<string, Pipe<any, any>, readonly string[]>\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\n\tdescribe('Fields', () => {\n\t\tdescribe('Field', () => {\n\t\t\ttest('stores name and defaults path to [name]', () => {\n\t\t\t\tconst f = new Field('email')\n\t\t\t\texpect(f.name).toBe('email')\n\t\t\t\texpect(f.path).toEqual(['email'])\n\t\t\t})\n\n\t\t\ttest('stores provided custom path', () => {\n\t\t\t\tconst f = new Field('profile', ['profile', 'displayName'])\n\t\t\t\texpect(f.name).toBe('profile')\n\t\t\t\texpect(f.path).toEqual(['profile', 'displayName'])\n\t\t\t})\n\t\t})\n\n\t\tdescribe('toFieldName()', () => {\n\t\t\ttest('returns string input unchanged', () => {\n\t\t\t\texpect(toFieldName('createdAt')).toBe('createdAt')\n\t\t\t})\n\n\t\t\ttest('converts Field path into dot notation', () => {\n\t\t\t\tconst f = new Field('profile', ['profile', 'displayName'])\n\t\t\t\texpect(toFieldName(f)).toBe('profile.displayName')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('SchemaField', () => {\n\t\t\ttest('extends Field and stores pipe', () => {\n\t\t\t\tconst mockPipe = v.string()\n\t\t\t\tconst f = new SchemaField('email', mockPipe)\n\t\t\t\texpect(f).toBeInstanceOf(Field)\n\t\t\t\texpect(f.name).toBe('email')\n\t\t\t\texpect(f.path).toEqual(['email'])\n\t\t\t\texpect(f.pipe).toBe(mockPipe)\n\t\t\t})\n\n\t\t\ttest('stores lifecycle hooks when provided', () => {\n\t\t\t\tconst mockPipe = v.number()\n\t\t\t\tconst f = new SchemaField('updatedAt', mockPipe, {\n\t\t\t\t\tonCreate: () => 1000,\n\t\t\t\t\tonUpdate: () => 2000,\n\t\t\t\t})\n\t\t\t\texpect(f.onCreate?.()).toBe(1000)\n\t\t\t\texpect(f.onUpdate?.()).toBe(2000)\n\t\t\t})\n\n\t\t\ttest('keeps lifecycle hooks undefined when omitted', () => {\n\t\t\t\tconst mockPipe = v.string()\n\t\t\t\tconst f = new SchemaField('name', mockPipe)\n\t\t\t\texpect(f.onCreate).toBeUndefined()\n\t\t\t\texpect(f.onUpdate).toBeUndefined()\n\t\t\t})\n\t\t})\n\t})\n}\n","import type { FilterOpName } from './adapter'\nimport { EquippedError } from '../errors'\nimport { OrmValidationError, type OrmValidationFailure } from './errors'\nimport { toFieldName, type AnyField, type Field } from './fields'\nimport type { AggregateSpec } from './orm-adapter'\nimport type { AnySchema } from './schema'\n\nexport class Filter {\n\treadonly field: string\n\tconstructor(\n\t\tfield: string | AnyField,\n\t\treadonly op: FilterOpName,\n\t\treadonly value: unknown,\n\t) {\n\t\tthis.field = toFieldName(field)\n\t}\n}\n\nexport type FilterChild = Filter | FilterGroup\n\nexport type FilterFactory = (q: FilterGroup) => FilterGroup\n\nexport type FilterGroupOp = 'and' | 'or'\n\nexport class FilterGroup {\n\treadonly children: readonly FilterChild[]\n\n\tprivate constructor(\n\t\treadonly op: FilterGroupOp = 'and',\n\t\tchildren?: readonly FilterChild[],\n\t) {\n\t\tthis.children = children ?? []\n\t}\n\n\t#withChild(child: FilterChild): FilterGroup {\n\t\treturn new FilterGroup(this.op, [...this.children, child])\n\t}\n\n\teq<T>(field: string | Field<T>, value: T): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'eq', value))\n\t}\n\n\tne<T>(field: string | Field<T>, value: T): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'ne', value))\n\t}\n\n\tgt<T>(field: string | Field<T>, value: T): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'gt', value))\n\t}\n\n\tgte<T>(field: string | Field<T>, value: T): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'gte', value))\n\t}\n\n\tlt<T>(field: string | Field<T>, value: T): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'lt', value))\n\t}\n\n\tlte<T>(field: string | Field<T>, value: T): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'lte', value))\n\t}\n\n\tin<T>(field: string | Field<T>, value: T[]): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'in', value))\n\t}\n\n\tnotIn<T>(field: string | Field<T>, value: T[]): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'notIn', value))\n\t}\n\n\tlike(field: string | Field<string>, value: string): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'like', value))\n\t}\n\n\texists(field: string | Field<unknown>): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'exists', true))\n\t}\n\n\tnotExists(field: string | Field<unknown>): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'notExists', true))\n\t}\n\n\tcontains<T>(field: string | Field<T>, value: T[]): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'contains', value))\n\t}\n\n\tnotContains<T>(field: string | Field<T>, value: T[]): FilterGroup {\n\t\treturn this.#withChild(new Filter(field, 'notContains', value))\n\t}\n\n\tand(facFns: FilterFactory[]): FilterGroup {\n\t\tif (facFns.length === 0) throw new EquippedError('and() requires at least one filter factory', { op: 'and' })\n\t\tconst group = new FilterGroup('and', facFns.map((fn) => fn(FilterGroup.create())))\n\t\treturn this.#withChild(group)\n\t}\n\n\tor(facFns: FilterFactory[]): FilterGroup {\n\t\tif (facFns.length === 0) throw new EquippedError('or() requires at least one filter factory', { op: 'or' })\n\t\tconst group = new FilterGroup('or', facFns.map((fn) => fn(FilterGroup.create())))\n\t\treturn this.#withChild(group)\n\t}\n\n\tclone(): FilterGroup {\n\t\treturn new FilterGroup(\n\t\t\tthis.op,\n\t\t\tthis.children.map((c) => {\n\t\t\t\tif (c instanceof Filter) return new Filter(c.field, c.op, structuredClone(c.value))\n\t\t\t\treturn c.clone()\n\t\t\t}),\n\t\t)\n\t}\n\n\tstatic create(): FilterGroup {\n\t\treturn new FilterGroup()\n\t}\n}\n\n\nexport type GatedFilterGroup<DeclaredOps extends readonly FilterOpName[]> = {\n\t[K in FilterOpName]: K extends DeclaredOps[number] ? FilterGroup[K] : never\n} & Pick<FilterGroup, 'and' | 'or' | 'clone' | 'children' | 'op'>\n\nexport type GatedFilterFactory<DeclaredOps extends readonly FilterOpName[]> = (\n\tq: GatedFilterGroup<DeclaredOps>,\n) => GatedFilterGroup<DeclaredOps>\n\nexport function assertNormalisedAggregate(schema: AnySchema, adapter: { aggregateOps: readonly string[] }, spec: AggregateSpec): void {\n\tconst failures: OrmValidationFailure[] = []\n\n\tif (spec.aggregates.length === 0) {\n\t\tfailures.push({ cause: 'At least one aggregator step is required' })\n\t}\n\n\tconst seenAliases = new Set<string>()\n\tfor (const agg of spec.aggregates) {\n\t\tif (seenAliases.has(agg.alias)) {\n\t\t\tfailures.push({ alias: agg.alias, cause: `Duplicate alias \"${agg.alias}\"` })\n\t\t}\n\t\tseenAliases.add(agg.alias)\n\n\t\tif (!adapter.aggregateOps.includes(agg.fn)) {\n\t\t\tfailures.push({ alias: agg.alias, cause: `Undeclared aggregate op \"${agg.fn}\"` })\n\t\t}\n\t}\n\n\tconst allFields = schema.fields as Record<string, unknown>\n\tconst fieldNames = new Set(Object.keys(allFields))\n\tfor (const field of spec.groupBy) {\n\t\tif (!fieldNames.has(field)) {\n\t\t\tfailures.push({ field, cause: `Unknown groupBy field \"${field}\" on schema \"${schema.name}\"` })\n\t\t}\n\t\tif (seenAliases.has(field)) {\n\t\t\tfailures.push({ alias: field, cause: `Alias \"${field}\" collides with groupBy field name` })\n\t\t}\n\t}\n\n\tif (failures.length > 0) {\n\t\tthrow new OrmValidationError('aggregate', schema.name, 'aggregate', failures)\n\t}\n\n\tif (spec.where) {\n\t\tassertNormalisedFilter(schema, spec.where)\n\t}\n\n\tif (spec.having) {\n\t\tconst validHavingFields = new Set([...seenAliases, ...spec.groupBy])\n\t\tconst havingErrors: OrmValidationFailure[] = []\n\n\t\tfunction walkHaving(node: FilterChild): void {\n\t\t\tif (node instanceof Filter) {\n\t\t\t\tif (!validHavingFields.has(node.field)) {\n\t\t\t\t\thavingErrors.push({ field: node.field, cause: `Unknown having field \"${node.field}\" — must be an aggregator alias or groupBy field` })\n\t\t\t\t}\n\t\t\t} else if (node instanceof FilterGroup) {\n\t\t\t\tfor (const child of node.children) walkHaving(child)\n\t\t\t}\n\t\t}\n\n\t\tfor (const child of spec.having.children) walkHaving(child)\n\n\t\tif (havingErrors.length > 0) {\n\t\t\tthrow new OrmValidationError('aggregate', schema.name, 'aggregate', havingErrors)\n\t\t}\n\t}\n}\n\nexport function assertNormalisedFilter(schema: AnySchema, group: FilterGroup): void {\n\tconst allFields = schema.fields as Record<string, unknown>\n\tconst fieldNames = new Set(Object.keys(allFields))\n\tconst errors: Array<{ field: string; cause: string }> = []\n\n\tfunction walk(node: FilterChild): void {\n\t\tif (node instanceof Filter) {\n\t\t\tif (!fieldNames.has(node.field)) {\n\t\t\t\terrors.push({ field: node.field, cause: `Unknown field \"${node.field}\" on schema \"${schema.name}\"` })\n\t\t\t}\n\t\t} else if (node instanceof FilterGroup) {\n\t\t\tfor (const child of node.children) walk(child)\n\t\t}\n\t}\n\n\tfor (const child of group.children) walk(child)\n\n\tif (errors.length > 0) {\n\t\tthrow new OrmValidationError(\n\t\t\t'validation',\n\t\t\tschema.name,\n\t\t\t'filter',\n\t\t\terrors.map((e) => ({ field: e.field, cause: e.cause })),\n\t\t)\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { Schema } = await import('./schema')\n\n\tconst UserSchema = Schema.from('users')\n\t\t.pk('id', v.string(), () => 'u1')\n\t\t.field('email', v.string())\n\t\t.field('age', v.number())\n\t\t.field('name', v.string())\n\t\t.field('tags', v.array(v.string()))\n\t\t.build()\n\n\tdescribe('FilterGroup', () => {\n\t\tdescribe('filter-op methods', () => {\n\t\t\ttest('eq adds a filter clause with op eq', () => {\n\t\t\t\tconst g = FilterGroup.create().eq(UserSchema.fields.age, 25)\n\t\t\t\texpect(g.children).toHaveLength(1)\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.field).toBe('age')\n\t\t\t\texpect(f.op).toBe('eq')\n\t\t\t\texpect(f.value).toBe(25)\n\t\t\t})\n\n\t\t\ttest('ne adds a filter clause with op ne', () => {\n\t\t\t\tconst g = FilterGroup.create().ne('name', 'Bob')\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.op).toBe('ne')\n\t\t\t\texpect(f.value).toBe('Bob')\n\t\t\t})\n\n\t\t\ttest('gt/gte/lt/lte produce correct ops', () => {\n\t\t\t\tconst g = FilterGroup.create()\n\t\t\t\t\t.gt(UserSchema.fields.age, 10)\n\t\t\t\t\t.gte(UserSchema.fields.age, 20)\n\t\t\t\t\t.lt(UserSchema.fields.age, 30)\n\t\t\t\t\t.lte(UserSchema.fields.age, 40)\n\t\t\t\texpect(g.children.map((c) => (c as Filter).op)).toEqual(['gt', 'gte', 'lt', 'lte'])\n\t\t\t})\n\n\t\t\ttest('in adds a filter clause with array value', () => {\n\t\t\t\tconst g = FilterGroup.create().in(UserSchema.fields.age, [1, 2, 3])\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.op).toBe('in')\n\t\t\t\texpect(f.value).toEqual([1, 2, 3])\n\t\t\t})\n\n\t\t\ttest('notIn adds a filter clause with op notIn', () => {\n\t\t\t\tconst g = FilterGroup.create().notIn(UserSchema.fields.age, [4, 5])\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.op).toBe('notIn')\n\t\t\t\texpect(f.value).toEqual([4, 5])\n\t\t\t})\n\n\t\t\ttest('like adds a filter clause with op like', () => {\n\t\t\t\tconst g = FilterGroup.create().like(UserSchema.fields.email, 'alice')\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.op).toBe('like')\n\t\t\t\texpect(f.value).toBe('alice')\n\t\t\t})\n\n\t\t\ttest('exists adds a filter clause with op exists', () => {\n\t\t\t\tconst g = FilterGroup.create().exists(UserSchema.fields.name)\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.op).toBe('exists')\n\t\t\t})\n\n\t\t\ttest('notExists is its own op, not a boolean form of exists', () => {\n\t\t\t\tconst g = FilterGroup.create().notExists(UserSchema.fields.name)\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.op).toBe('notExists')\n\t\t\t\texpect(f.op).not.toBe('exists')\n\t\t\t})\n\n\t\t\ttest('contains/notContains produce correct ops', () => {\n\t\t\t\tconst g = FilterGroup.create()\n\t\t\t\t\t.contains('tags', ['a'])\n\t\t\t\t\t.notContains('tags', ['b'])\n\t\t\t\texpect((g.children[0] as Filter).op).toBe('contains')\n\t\t\t\texpect((g.children[1] as Filter).op).toBe('notContains')\n\t\t\t})\n\n\t\t\ttest('all 13 filter ops produce matching Filter.op values (name-parity)', () => {\n\t\t\t\tconst g = FilterGroup.create()\n\t\t\t\t\t.eq('f', 1)\n\t\t\t\t\t.ne('f', 1)\n\t\t\t\t\t.gt('f', 1)\n\t\t\t\t\t.gte('f', 1)\n\t\t\t\t\t.lt('f', 1)\n\t\t\t\t\t.lte('f', 1)\n\t\t\t\t\t.in('f', [1])\n\t\t\t\t\t.notIn('f', [1])\n\t\t\t\t\t.like('f', 'x')\n\t\t\t\t\t.exists('f')\n\t\t\t\t\t.notExists('f')\n\t\t\t\t\t.contains('f', [1])\n\t\t\t\t\t.notContains('f', [1])\n\t\t\t\tconst ops = g.children.map((c) => (c as Filter).op)\n\t\t\t\texpect(ops).toEqual([\n\t\t\t\t\t'eq', 'ne', 'gt', 'gte', 'lt', 'lte',\n\t\t\t\t\t'in', 'notIn', 'like', 'exists', 'notExists',\n\t\t\t\t\t'contains', 'notContains',\n\t\t\t\t])\n\t\t\t})\n\t\t})\n\n\t\tdescribe('raw-string field overload', () => {\n\t\t\ttest('accepts raw string field name', () => {\n\t\t\t\tconst g = FilterGroup.create().eq('age', 18)\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.field).toBe('age')\n\t\t\t})\n\n\t\t\ttest('accepts typed Field ref and extracts field name', () => {\n\t\t\t\tconst g = FilterGroup.create().eq(UserSchema.fields.age, 18)\n\t\t\t\tconst f = g.children[0] as Filter\n\t\t\t\texpect(f.field).toBe('age')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('structural combinators', () => {\n\t\t\ttest('and() creates a nested and-group', () => {\n\t\t\t\tconst g = FilterGroup.create().and([\n\t\t\t\t\t(q) => q.eq('age', 10),\n\t\t\t\t\t(q) => q.eq('name', 'Alice'),\n\t\t\t\t])\n\t\t\t\texpect(g.children).toHaveLength(1)\n\t\t\t\tconst nested = g.children[0] as FilterGroup\n\t\t\t\texpect(nested.op).toBe('and')\n\t\t\t\texpect(nested.children).toHaveLength(2)\n\t\t\t})\n\n\t\t\ttest('or() creates a nested or-group', () => {\n\t\t\t\tconst g = FilterGroup.create().or([\n\t\t\t\t\t(q) => q.eq('name', 'Alice'),\n\t\t\t\t\t(q) => q.eq('name', 'Bob'),\n\t\t\t\t])\n\t\t\t\tconst nested = g.children[0] as FilterGroup\n\t\t\t\texpect(nested.op).toBe('or')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('empty-combinator rejection', () => {\n\t\t\ttest('and([]) throws at builder time', () => {\n\t\t\t\texpect(() => FilterGroup.create().and([])).toThrow()\n\t\t\t})\n\n\t\t\ttest('or([]) throws at builder time', () => {\n\t\t\t\texpect(() => FilterGroup.create().or([])).toThrow()\n\t\t\t})\n\n\t\t\ttest('thrown error has stack pointing at offending call', () => {\n\t\t\t\ttry {\n\t\t\t\t\tFilterGroup.create().and([])\n\t\t\t\t\texpect.unreachable()\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect((e as Error).stack).toContain('filter.ts')\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tdescribe('clone()', () => {\n\t\t\ttest('deep-clones the tree', () => {\n\t\t\t\tconst original = FilterGroup.create()\n\t\t\t\t\t.eq('name', 'Alice')\n\t\t\t\t\t.and([(q) => q.gt('age', 20)])\n\t\t\t\tconst cloned = original.clone()\n\n\t\t\t\texpect(cloned.children).toHaveLength(2)\n\t\t\t\texpect(cloned).not.toBe(original)\n\t\t\t\texpect(cloned.children[0]).not.toBe(original.children[0])\n\t\t\t})\n\n\t\t\ttest('structuredClone of values — mutations on clone do not leak', () => {\n\t\t\t\tconst arr = [1, 2, 3]\n\t\t\t\tconst original = FilterGroup.create().in('ids', arr)\n\t\t\t\tconst cloned = original.clone()\n\n\t\t\t\tconst clonedValue = (cloned.children[0] as Filter).value as number[]\n\t\t\t\tclonedValue.push(4)\n\n\t\t\t\texpect((original.children[0] as Filter).value).toEqual([1, 2, 3])\n\t\t\t\texpect(clonedValue).toEqual([1, 2, 3, 4])\n\t\t\t})\n\t\t})\n\t})\n\n\tdescribe('clone-on-step: fan-out independence', () => {\n\t\ttest('.eq() returns a new FilterGroup, not the same instance', () => {\n\t\t\tconst base = FilterGroup.create()\n\t\t\tconst a = base.eq('name', 'Alice')\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\n\t\ttest('fan-out from shared base does not pollute either branch', () => {\n\t\t\tconst base = FilterGroup.create().eq('name', 'Alice')\n\t\t\tconst branchA = base.gt('age', 20)\n\t\t\tconst branchB = base.lt('age', 40)\n\n\t\t\texpect(branchA.children).toHaveLength(2)\n\t\t\texpect(branchB.children).toHaveLength(2)\n\t\t\texpect(base.children).toHaveLength(1)\n\t\t\texpect((branchA.children[1] as Filter).op).toBe('gt')\n\t\t\texpect((branchB.children[1] as Filter).op).toBe('lt')\n\t\t})\n\n\t\ttest('.and() returns a new FilterGroup', () => {\n\t\t\tconst base = FilterGroup.create().eq('name', 'Alice')\n\t\t\tconst withAnd = base.and([(q) => q.gt('age', 20)])\n\t\t\texpect(withAnd).not.toBe(base)\n\t\t\texpect(base.children).toHaveLength(1)\n\t\t\texpect(withAnd.children).toHaveLength(2)\n\t\t})\n\n\t\ttest('.or() returns a new FilterGroup', () => {\n\t\t\tconst base = FilterGroup.create().eq('name', 'Alice')\n\t\t\tconst withOr = base.or([(q) => q.gt('age', 20)])\n\t\t\texpect(withOr).not.toBe(base)\n\t\t\texpect(base.children).toHaveLength(1)\n\t\t\texpect(withOr.children).toHaveLength(2)\n\t\t})\n\t})\n\n\tdescribe('assertNormalisedFilter', () => {\n\t\ttest('passes for valid filter referencing known fields', () => {\n\t\t\tconst g = FilterGroup.create().eq(UserSchema.fields.age, 25)\n\t\t\texpect(() => assertNormalisedFilter(UserSchema, g)).not.toThrow()\n\t\t})\n\n\t\ttest('passes for raw string field that exists on schema', () => {\n\t\t\tconst g = FilterGroup.create().eq('email', 'test@test.com')\n\t\t\texpect(() => assertNormalisedFilter(UserSchema, g)).not.toThrow()\n\t\t})\n\n\t\ttest('rejects unknown field name with OrmValidationError', () => {\n\t\t\tconst g = FilterGroup.create().eq('unknownField', 42)\n\t\t\texpect(() => assertNormalisedFilter(UserSchema, g)).toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('rejected error has kind validation', () => {\n\t\t\tconst g = FilterGroup.create().eq('badField', 42)\n\t\t\ttry {\n\t\t\t\tassertNormalisedFilter(UserSchema, g)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\texpect((e as OrmValidationError).kind).toBe('validation')\n\t\t\t\texpect((e as OrmValidationError).failures).toHaveLength(1)\n\t\t\t\texpect((e as OrmValidationError).failures[0].field).toBe('badField')\n\t\t\t}\n\t\t})\n\n\t\ttest('preserves logical field names verbatim for Field refs', () => {\n\t\t\tconst g = FilterGroup.create().eq(UserSchema.fields.email, 'a@b.com')\n\t\t\tconst f = g.children[0] as Filter\n\t\t\texpect(f.field).toBe('email')\n\t\t\tassertNormalisedFilter(UserSchema, g)\n\t\t})\n\n\t\ttest('preserves logical field names verbatim for raw-string overloads', () => {\n\t\t\tconst g = FilterGroup.create().eq('age', 18)\n\t\t\tconst f = g.children[0] as Filter\n\t\t\texpect(f.field).toBe('age')\n\t\t\tassertNormalisedFilter(UserSchema, g)\n\t\t})\n\n\t\ttest('walks nested groups to detect unknown fields', () => {\n\t\t\tconst g = FilterGroup.create().and([\n\t\t\t\t(q) => q.eq('name', 'Alice'),\n\t\t\t\t(q) => q.or([(inner) => inner.eq('nonexistent', 'val')]),\n\t\t\t])\n\t\t\texpect(() => assertNormalisedFilter(UserSchema, g)).toThrow(OrmValidationError)\n\t\t})\n\t})\n\n\tdescribe('assertNormalisedAggregate', () => {\n\t\tconst adapter = { aggregateOps: ['count'] as readonly string[] }\n\n\t\ttest('passes for valid count aggregate', () => {\n\t\t\tconst spec = { aggregates: [{ fn: 'count' as const, alias: 'total' }], groupBy: [] }\n\t\t\texpect(() => assertNormalisedAggregate(UserSchema, adapter, spec)).not.toThrow()\n\t\t})\n\n\t\ttest('rejects empty aggregator list', () => {\n\t\t\tconst spec = { aggregates: [], groupBy: [] }\n\t\t\texpect(() => assertNormalisedAggregate(UserSchema, adapter, spec)).toThrow(OrmValidationError)\n\t\t\ttry {\n\t\t\t\tassertNormalisedAggregate(UserSchema, adapter, spec)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect((e as OrmValidationError).kind).toBe('aggregate')\n\t\t\t}\n\t\t})\n\n\t\ttest('rejects undeclared aggregate op', () => {\n\t\t\tconst spec = { aggregates: [{ fn: 'sum' as const, alias: 'total', field: 'age' }], groupBy: [] }\n\t\t\texpect(() => assertNormalisedAggregate(UserSchema, adapter, spec)).toThrow(OrmValidationError)\n\t\t\ttry {\n\t\t\t\tassertNormalisedAggregate(UserSchema, adapter, spec)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('aggregate')\n\t\t\t\texpect(err.failures[0].alias).toBe('total')\n\t\t\t\texpect(err.failures[0].cause).toContain('Undeclared')\n\t\t\t}\n\t\t})\n\n\t\ttest('rejects duplicate aliases', () => {\n\t\t\tconst spec = {\n\t\t\t\taggregates: [\n\t\t\t\t\t{ fn: 'count' as const, alias: 'total' },\n\t\t\t\t\t{ fn: 'count' as const, alias: 'total' },\n\t\t\t\t],\n\t\t\t\tgroupBy: [],\n\t\t\t}\n\t\t\texpect(() => assertNormalisedAggregate(UserSchema, adapter, spec)).toThrow(OrmValidationError)\n\t\t\ttry {\n\t\t\t\tassertNormalisedAggregate(UserSchema, adapter, spec)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.failures.some((f) => f.alias === 'total')).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('validates where filter against schema', () => {\n\t\t\tconst spec = {\n\t\t\t\taggregates: [{ fn: 'count' as const, alias: 'total' }],\n\t\t\t\tgroupBy: [],\n\t\t\t\twhere: FilterGroup.create().eq('unknownField', 42),\n\t\t\t}\n\t\t\texpect(() => assertNormalisedAggregate(UserSchema, adapter, spec)).toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('collects multiple failures', () => {\n\t\t\tconst spec = {\n\t\t\t\taggregates: [\n\t\t\t\t\t{ fn: 'sum' as const, alias: 'x', field: 'age' },\n\t\t\t\t\t{ fn: 'avg' as const, alias: 'x', field: 'age' },\n\t\t\t\t],\n\t\t\t\tgroupBy: [],\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tassertNormalisedAggregate(UserSchema, adapter, spec)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.failures.length).toBeGreaterThanOrEqual(2)\n\t\t\t}\n\t\t})\n\n\t\ttest('rejects unknown groupBy field', () => {\n\t\t\tconst allOps = { aggregateOps: ['count', 'sum'] as readonly string[] }\n\t\t\tconst spec = {\n\t\t\t\taggregates: [{ fn: 'count' as const, alias: 'total' }],\n\t\t\t\tgroupBy: ['nonexistent'],\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tassertNormalisedAggregate(UserSchema, allOps, spec)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('aggregate')\n\t\t\t\texpect(err.failures[0].cause).toContain('groupBy')\n\t\t\t\texpect(err.failures[0].field).toBe('nonexistent')\n\t\t\t}\n\t\t})\n\n\t\ttest('rejects alias colliding with groupBy field name', () => {\n\t\t\tconst allOps = { aggregateOps: ['count'] as readonly string[] }\n\t\t\tconst spec = {\n\t\t\t\taggregates: [{ fn: 'count' as const, alias: 'name' }],\n\t\t\t\tgroupBy: ['name'],\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tassertNormalisedAggregate(UserSchema, allOps, spec)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('aggregate')\n\t\t\t\texpect(err.failures.some((f) => String(f.cause).includes('collides'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('rejects unknown having field', () => {\n\t\t\tconst allOps = { aggregateOps: ['count'] as readonly string[] }\n\t\t\tconst spec = {\n\t\t\t\taggregates: [{ fn: 'count' as const, alias: 'total' }],\n\t\t\t\tgroupBy: ['name'],\n\t\t\t\thaving: FilterGroup.create().gt('nonexistent', 0),\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tassertNormalisedAggregate(UserSchema, allOps, spec)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('aggregate')\n\t\t\t\texpect(err.failures[0].cause).toContain('having')\n\t\t\t}\n\t\t})\n\n\t\ttest('having accepts alias and groupBy field names', () => {\n\t\t\tconst allOps = { aggregateOps: ['count'] as readonly string[] }\n\t\t\tconst spec = {\n\t\t\t\taggregates: [{ fn: 'count' as const, alias: 'total' }],\n\t\t\t\tgroupBy: ['name'],\n\t\t\t\thaving: FilterGroup.create().gt('total', 0).eq('name', 'Alice'),\n\t\t\t}\n\t\t\texpect(() => assertNormalisedAggregate(UserSchema, allOps, spec)).not.toThrow()\n\t\t})\n\n\t\ttest('passes valid groupBy with known schema fields', () => {\n\t\t\tconst allOps = { aggregateOps: ['count'] as readonly string[] }\n\t\t\tconst spec = {\n\t\t\t\taggregates: [{ fn: 'count' as const, alias: 'total' }],\n\t\t\t\tgroupBy: ['name', 'age'],\n\t\t\t}\n\t\t\texpect(() => assertNormalisedAggregate(UserSchema, allOps, spec)).not.toThrow()\n\t\t})\n\t})\n}\n","import type { Pipe } from 'valleyed'\n\nimport { EquippedError } from '../errors'\nimport { Instance, type ClassRef } from '../instance'\nimport type { AggregateOpName, FieldTypeName, FilterOpName, UpdateOpName } from './adapter'\nimport type { OrmUse } from './adapters/base'\nimport { FilterGroup } from './filter'\nimport type { DiscoveredSchema } from './migrations/introspection-types'\nimport type { AddFieldChange, AddForeignKeyChange, AddIndexChange, CreateTableChange, DropFieldChange, DropForeignKeyChange, DropIndexChange, DropTableChange, ModifyFieldChange, RenameFieldChange, RenameTableChange } from './migrations/types'\nimport type { IterationQueryOptions, QueryOptions } from './query-options'\nimport type { AnySchema } from './schema'\nimport type { AnyUpdateOp } from './updates'\n\nexport type AggregateSpec = {\n\twhere?: FilterGroup\n\taggregates: ReadonlyArray<{\n\t\tfn: AggregateOpName\n\t\tfield?: string\n\t\talias: string\n\t}>\n\tgroupBy: readonly string[]\n\thaving?: FilterGroup\n}\n\nexport abstract class OrmAdapter {\n\treadonly queryableOps: readonly FilterOpName[] = []\n\treadonly updateOps: readonly UpdateOpName[] = []\n\treadonly aggregateOps: readonly AggregateOpName[] = []\n\treadonly supportedFieldTypes: readonly FieldTypeName[] = []\n\n\tabstract readonly schemaConfigPipe: Pipe<any, any>\n\n\tconnect?(): Promise<void>\n\tdisconnect?(): Promise<void>\n\tfindByPk?(schema: AnySchema, config: unknown, pk: unknown): Promise<Record<string, unknown> | null>\n\tcreateMany?(schema: AnySchema, config: unknown, data: Record<string, unknown>[]): Promise<Record<string, unknown>[]>\n\tupdateByPk?(schema: AnySchema, config: unknown, pk: unknown, ops: AnyUpdateOp[]): Promise<Record<string, unknown> | null>\n\tdeleteByPk?(schema: AnySchema, config: unknown, pk: unknown): Promise<Record<string, unknown> | null>\n\traw?(schema: AnySchema, config: unknown, ...args: any[]): Promise<any>\n\tfindMany?(schema: AnySchema, config: unknown, filter: FilterGroup, options?: QueryOptions): Promise<Record<string, unknown>[]>\n\tcount?(schema: AnySchema, config: unknown, filter: FilterGroup): Promise<number>\n\titerateMany?(schema: AnySchema, config: unknown, filter: FilterGroup, options?: IterationQueryOptions): AsyncGenerator<Record<string, unknown>, void, void>\n\tupdateMany?(schema: AnySchema, config: unknown, filter: FilterGroup, data: Record<string, unknown>): Promise<Record<string, unknown>[]>\n\tdeleteMany?(schema: AnySchema, config: unknown, filter: FilterGroup): Promise<Record<string, unknown>[]>\n\tupsertOne?(\n\t\tschema: AnySchema,\n\t\tconfig: unknown,\n\t\tfilter: FilterGroup,\n\t\tcreate: Record<string, unknown>,\n\t\tops: AnyUpdateOp[],\n\t): Promise<Record<string, unknown>>\n\taggregate?(schema: AnySchema, config: unknown, spec: AggregateSpec): Promise<Array<Record<string, unknown>>>\n\tsession?<T>(fn: () => Promise<T>): Promise<T>\n\n\tloadMigrations?(): Promise<{ id: string; appliedAt: number }[]>\n\trecordMigration?(id: string, appliedAt: number): Promise<void>\n\tacquireMigrationLock?<T>(fn: () => Promise<T>): Promise<T>\n\tapplyCreateTable?(change: CreateTableChange<any>): Promise<void>\n\tapplyDropTable?(change: DropTableChange): Promise<void>\n\tapplyAddField?(change: AddFieldChange<any>): Promise<void>\n\tapplyDropField?(change: DropFieldChange): Promise<void>\n\tapplyModifyField?(change: ModifyFieldChange<any>): Promise<void>\n\tapplyRenameTable?(change: RenameTableChange): Promise<void>\n\tapplyRenameField?(change: RenameFieldChange): Promise<void>\n\tapplyAddIndex?(change: AddIndexChange): Promise<void>\n\tapplyDropIndex?(change: DropIndexChange): Promise<void>\n\tapplyAddForeignKey?(change: AddForeignKeyChange): Promise<void>\n\tapplyDropForeignKey?(change: DropForeignKeyChange): Promise<void>\n\tintrospect?(): Promise<DiscoveredSchema[]>\n\n\tprotected onFatalError(err: unknown): never {\n\t\tconst wrapped = err instanceof EquippedError ? err : new EquippedError('OrmAdapter fatal error', {}, err)\n\t\tInstance.crash(wrapped)\n\t}\n\n\tconstructor() {\n\t\tconst self = this as any\n\t\tif (typeof self.connect === 'function') {\n\t\t\tInstance.on('start', () => self.connect(), { class: this.constructor as ClassRef })\n\t\t}\n\t\tif (typeof self.disconnect === 'function') {\n\t\t\tInstance.on('close', () => self.disconnect(), { class: this.constructor as ClassRef })\n\t\t}\n\t}\n\n\tuse(schema: AnySchema, config: unknown): OrmUse {\n\t\tconst self = this as any\n\t\tconst emptyIterator = async function* (): AsyncGenerator<Record<string, unknown>, void, void> {}\n\t\tconst use: OrmUse = {\n\t\t\tfindMany: (filter, opts) => self.findMany?.(schema, config, filter, opts) ?? Promise.resolve([]),\n\t\t\titerateMany: (filter, opts) => self.iterateMany?.(schema, config, filter, opts) ?? emptyIterator(),\n\t\t\tfindOne: async (filter) => {\n\t\t\t\tconst rows = await use.findMany(filter, { limit: 1 })\n\t\t\t\treturn rows[0] ?? null\n\t\t\t},\n\t\t\tcount: (filter) => self.count?.(schema, config, filter) ?? Promise.reject(new Error('count not implemented')),\n\t\t\tcreateOne: async (d) => {\n\t\t\t\tconst rows = await use.createMany([d])\n\t\t\t\treturn rows[0]\n\t\t\t},\n\t\t\tcreateMany: (d) => self.createMany?.(schema, config, d) ?? Promise.resolve([]),\n\t\t\tupdateMany: (filter, d) => self.updateMany?.(schema, config, filter, d) ?? Promise.resolve([]),\n\t\t\tupdateOne: async (filter, d) => {\n\t\t\t\tconst match = await use.findOne(filter)\n\t\t\t\tif (!match) return null\n\t\t\t\tconst pk = schema.pkField.name\n\t\t\t\tconst pkFilter = FilterGroup.create().eq(pk, match[pk])\n\t\t\t\tconst rows = await use.updateMany(pkFilter, d)\n\t\t\t\treturn rows[0] ?? null\n\t\t\t},\n\t\t\tupsertOne: (filter, create, ops) =>\n\t\t\t\tself.upsertOne?.(schema, config, filter, create, ops) ?? Promise.reject(new Error('upsertOne not implemented')),\n\t\t\tdeleteOne: async (filter) => {\n\t\t\t\tconst row = await use.findOne(filter)\n\t\t\t\tif (!row) return null\n\t\t\t\tconst pk = schema.pkField.name\n\t\t\t\tif (self.deleteByPk) {\n\t\t\t\t\tawait self.deleteByPk(schema, config, row[pk])\n\t\t\t\t} else if (self.deleteMany) {\n\t\t\t\t\tconst pkFilter = FilterGroup.create().eq(pk, row[pk])\n\t\t\t\t\tawait self.deleteMany(schema, config, pkFilter)\n\t\t\t\t}\n\t\t\t\treturn row\n\t\t\t},\n\t\t\tdeleteMany: (filter) => self.deleteMany?.(schema, config, filter) ?? Promise.resolve([]),\n\t\t\traw: (...args: any[]) => self.raw?.(schema, config, ...args) ?? Promise.reject(new Error('raw not implemented')),\n\t\t\taggregate: (spec) => self.aggregate?.(schema, config, spec) ?? Promise.reject(new Error('aggregate not implemented')),\n\t\t\taggregateOps: self.aggregateOps ?? [],\n\t\t}\n\t\treturn use\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, vi } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\n\tdescribe('OrmAdapter', () => {\n\t\ttest('subclass with connect/disconnect auto-registers Instance hooks', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tconst onSpy = vi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tclass TestAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync connect() {}\n\t\t\t\tasync disconnect() {}\n\t\t\t}\n\n\t\t\tnew (TestAdapter as any)()\n\n\t\t\texpect(onSpy).toHaveBeenCalledWith('start', expect.any(Function), expect.objectContaining({ class: TestAdapter }))\n\t\t\texpect(onSpy).toHaveBeenCalledWith('close', expect.any(Function), expect.objectContaining({ class: TestAdapter }))\n\n\t\t\tonSpy.mockRestore()\n\t\t})\n\n\t\ttest('subclass without connect/disconnect does not register hooks', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tconst onSpy = vi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tclass MinimalAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t}\n\n\t\t\tnew (MinimalAdapter as any)()\n\t\t\texpect(onSpy).not.toHaveBeenCalled()\n\n\t\t\tonSpy.mockRestore()\n\t\t})\n\n\t\ttest('capability declarations default to empty arrays', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tclass DefaultAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t}\n\t\t\tconst adapter = new (DefaultAdapter as any)() as DefaultAdapter\n\t\t\texpect(adapter.queryableOps).toEqual([])\n\t\t\texpect(adapter.updateOps).toEqual([])\n\t\t\texpect(adapter.aggregateOps).toEqual([])\n\t\t\texpect(adapter.supportedFieldTypes).toEqual([])\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('onFatalError wraps non-EquippedError and calls Instance.crash', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\t\t\tconst crashSpy = vi.spyOn(Inst, 'crash').mockImplementation((() => {\n\t\t\t\tthrow new Error('crash')\n\t\t\t}) as any)\n\n\t\t\tclass FatalAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\ttriggerFatal() {\n\t\t\t\t\tthis.onFatalError(new Error('something broke'))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adapter = new (FatalAdapter as any)() as FatalAdapter\n\t\t\texpect(() => adapter.triggerFatal()).toThrow('crash')\n\t\t\texpect(crashSpy).toHaveBeenCalled()\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('onFatalError passes through EquippedError directly', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\t\t\tlet crashedWith: unknown\n\t\t\tvi.spyOn(Inst, 'crash').mockImplementation(((err: any) => {\n\t\t\t\tcrashedWith = err\n\t\t\t\tthrow err\n\t\t\t}) as any)\n\n\t\t\tclass FatalAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\ttriggerFatal() {\n\t\t\t\t\tthis.onFatalError(new EquippedError('equipped error', { detail: 'test' }))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adapter = new (FatalAdapter as any)() as FatalAdapter\n\t\t\texpect(() => adapter.triggerFatal()).toThrow()\n\t\t\texpect(crashedWith).toBeInstanceOf(EquippedError)\n\t\t\texpect((crashedWith as EquippedError).message).toBe('equipped error')\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('use() creates OrmUse bridge that delegates to flat methods', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tconst { Schema } = await import('./schema')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tclass BridgeAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\n\t\t\t\tasync findMany(_s: AnySchema, _c: unknown, _f: any, _o?: any) {\n\t\t\t\t\treturn [{ id: 'found' }]\n\t\t\t\t}\n\t\t\t\tasync count(_s: AnySchema, _c: unknown, _f: any) {\n\t\t\t\t\treturn 1\n\t\t\t\t}\n\t\t\t\tasync *iterateMany(_s: AnySchema, _c: unknown, _f: any, _o?: any) {\n\t\t\t\t\tyield { id: 'iterated' }\n\t\t\t\t}\n\t\t\t\tasync createMany(_s: AnySchema, _c: unknown, data: Record<string, unknown>[]) {\n\t\t\t\t\treturn data\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst TestSchema = Schema.from('bridge_test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.build()\n\t\t\tconst adapter = new (BridgeAdapter as any)() as BridgeAdapter\n\t\t\tconst ormUse = adapter.use(TestSchema, { table: 'bridge_test' })\n\n\t\t\tconst rows = await ormUse.findMany(FilterGroup.create())\n\t\t\texpect(rows).toEqual([{ id: 'found' }])\n\t\t\tawait expect(ormUse.count(FilterGroup.create())).resolves.toBe(1)\n\n\t\t\tconst iterated: Record<string, unknown>[] = []\n\t\t\tfor await (const row of ormUse.iterateMany(FilterGroup.create())) iterated.push(row)\n\t\t\texpect(iterated).toEqual([{ id: 'iterated' }])\n\n\t\t\tconst created = await ormUse.createOne({ id: 'new' })\n\t\t\texpect(created).toEqual({ id: 'new' })\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('use() bridge delegates updateMany to flat method', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tconst { Schema } = await import('./schema')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tclass UpdateAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly updateOps = ['set'] as const\n\n\t\t\t\tasync findMany(_s: AnySchema, _c: unknown, _f: any) {\n\t\t\t\t\treturn [{ id: 'u1', name: 'old' }]\n\t\t\t\t}\n\t\t\t\tasync updateMany(_s: AnySchema, _c: unknown, _f: any, data: Record<string, unknown>) {\n\t\t\t\t\treturn [{ id: 'u1', ...data }]\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst TestSchema = Schema.from('upd_test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst adapter = new (UpdateAdapter as any)() as UpdateAdapter\n\t\t\tconst ormUse = adapter.use(TestSchema, { table: 'upd_test' })\n\n\t\t\tconst updated = await ormUse.updateOne(FilterGroup.create().eq('id', 'u1'), { name: 'new' })\n\t\t\texpect(updated).not.toBeNull()\n\t\t\texpect(updated!.name).toBe('new')\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('use().iterateMany does not fall back to findMany when adapter omits iterateMany', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tconst { Schema } = await import('./schema')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tlet findManyCalls = 0\n\t\t\tclass FindOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync findMany() {\n\t\t\t\t\tfindManyCalls += 1\n\t\t\t\t\treturn [{ id: 'found' }]\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst TestSchema = Schema.from('iter_no_fallback')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.build()\n\t\t\tconst adapter = new (FindOnlyAdapter as any)() as FindOnlyAdapter\n\t\t\tconst rows: Record<string, unknown>[] = []\n\t\t\tfor await (const row of adapter.use(TestSchema, { table: 'iter_no_fallback' }).iterateMany(FilterGroup.create())) rows.push(row)\n\n\t\t\texpect(rows).toEqual([])\n\t\t\texpect(findManyCalls).toBe(0)\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('use() bridge delegates deleteByPk via deleteOne', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tconst { Schema } = await import('./schema')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tlet deletedPk: unknown = null\n\t\t\tclass DeleteAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\n\t\t\t\tasync findMany(_s: AnySchema, _c: unknown, _f: any) {\n\t\t\t\t\treturn [{ id: 'd1' }]\n\t\t\t\t}\n\t\t\t\tasync deleteByPk(_s: AnySchema, _c: unknown, pk: unknown) {\n\t\t\t\t\tdeletedPk = pk\n\t\t\t\t\treturn { id: String(pk) }\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst TestSchema = Schema.from('del_test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.build()\n\t\t\tconst adapter = new (DeleteAdapter as any)() as DeleteAdapter\n\t\t\tconst ormUse = adapter.use(TestSchema, { table: 'del_test' })\n\n\t\t\tconst deleted = await ormUse.deleteOne(FilterGroup.create().eq('id', 'd1'))\n\t\t\texpect(deleted).toEqual({ id: 'd1' })\n\t\t\texpect(deletedPk).toBe('d1')\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('use() bridge delegates session to flat method', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tlet sessionRan = false\n\t\t\tclass SessionAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\tasync session<T>(fn: () => Promise<T>): Promise<T> {\n\t\t\t\t\tsessionRan = true\n\t\t\t\t\treturn fn()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adapter = new (SessionAdapter as any)() as SessionAdapter\n\t\t\tconst result = await adapter.session!(async () => 42)\n\t\t\texpect(result).toBe(42)\n\t\t\texpect(sessionRan).toBe(true)\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\n\t\ttest('type-level: subclass method override enforces canonical signature', async () => {\n\t\t\tconst { Instance: Inst } = await import('../instance')\n\t\t\tvi.spyOn(Inst, 'on').mockImplementation(() => {})\n\n\t\t\tclass CorrectAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\tasync findByPk(_s: AnySchema, _c: unknown, _pk: unknown) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tasync createMany(_s: AnySchema, _c: unknown, _d: Record<string, unknown>[]) {\n\t\t\t\t\treturn []\n\t\t\t\t}\n\t\t\t\tasync raw(_s: AnySchema, _c: unknown, ..._args: any[]) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adapter = new (CorrectAdapter as any)() as CorrectAdapter\n\t\t\texpect(adapter.findByPk).toBeTypeOf('function')\n\t\t\texpect(adapter.createMany).toBeTypeOf('function')\n\t\t\texpect(adapter.raw).toBeTypeOf('function')\n\n\t\t\tvi.restoreAllMocks()\n\t\t})\n\t})\n}\n","import { ulid } from 'ulid'\nimport { v } from 'valleyed'\n\nimport { OrmValidationError } from '../errors'\nimport type { EventContext, HandlerDef } from './registry'\nimport { EventLogSchema } from './schema'\nimport type { Repo } from '../repo/repo'\n\nexport async function fire<R>(\n\trepo: Repo<any>,\n\tname: string,\n\tdef: HandlerDef,\n\tpayload: unknown,\n\tctx?: { by?: string; at?: Date },\n): Promise<R> {\n\tconst validated = v.validate(def.pipe, payload)\n\tif (!validated.valid) {\n\t\tthrow new OrmValidationError('validation', 'event_log', 'fire', [\n\t\t\t{ cause: validated.error },\n\t\t])\n\t}\n\n\treturn repo.session(async () => {\n\t\tconst at = ctx?.at ?? new Date()\n\t\tconst ts = at.getTime()\n\t\tconst key = ulid(ts)\n\t\tconst by = ctx?.by ?? null\n\n\t\tawait repo.on(EventLogSchema).one().create({\n\t\t\tkey,\n\t\t\tname,\n\t\t\tts,\n\t\t\tbody: validated.value,\n\t\t\tby,\n\t\t})\n\n\t\tconst evCtx: EventContext = {\n\t\t\tkey,\n\t\t\tname,\n\t\t\tts,\n\t\t\tbody: validated.value,\n\t\t\tby,\n\t\t\tat,\n\t\t\tfirstRun: true,\n\t\t}\n\n\t\treturn await def.handle(validated.value, evCtx) as R\n\t})\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { Repo } = await import('../repo/repo')\n\tconst { OrmValidationError } = await import('../errors')\n\ttype EventContext = import('./registry').EventContext\n\tconst { EventLogSchema } = await import('./schema')\n\n\tfunction makeRepo() {\n\t\tconst adapter = InMemoryAdapter.create({})\n\t\treturn new Repo({\n\t\t\tadapter,\n\t\t\tresolve: (s) => {\n\t\t\t\tif (s === EventLogSchema) return { table: 'events' }\n\t\t\t\treturn { table: s.name }\n\t\t\t},\n\t\t})\n\t}\n\n\tdescribe('FireExecutor', () => {\n\t\ttest('payload validation rejects → throws OrmValidationError, no row persisted', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ email: v.string() }),\n\t\t\t\thandle: async () => 'ok',\n\t\t\t}\n\t\t\tawait expect(fire(repo, 'test', def, { email: 123 })).rejects.toThrow(OrmValidationError)\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(rows).toHaveLength(0)\n\t\t})\n\n\t\ttest('happy path persists row and runs handler', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tlet called = false\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ email: v.string() }),\n\t\t\t\thandle: async (payload: { email: string }) => {\n\t\t\t\t\tcalled = true\n\t\t\t\t\treturn `created-${payload.email}`\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tconst result = await fire(repo, 'user.signup', def, { email: 'a@b.com' }, { by: 'admin' })\n\n\t\t\texpect(result).toBe('created-a@b.com')\n\t\t\texpect(called).toBe(true)\n\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(rows).toHaveLength(1)\n\t\t\texpect(rows[0].name).toBe('user.signup')\n\t\t\texpect(rows[0].body).toEqual({ email: 'a@b.com' })\n\t\t\texpect(rows[0].by).toBe('admin')\n\t\t\texpect(typeof rows[0].ts).toBe('number')\n\t\t\texpect(typeof rows[0].key).toBe('string')\n\t\t})\n\n\t\ttest('handler receives EventContext with firstRun: true', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tlet captured: EventContext | undefined\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ x: v.number() }),\n\t\t\t\thandle: async (_payload: unknown, ctx: EventContext) => {\n\t\t\t\t\tcaptured = ctx\n\t\t\t\t},\n\t\t\t}\n\t\t\tconst at = new Date('2025-01-15T12:00:00Z')\n\n\t\t\tawait fire(repo, 'test.event', def, { x: 42 }, { by: 'user-1', at })\n\n\t\t\texpect(captured).toBeDefined()\n\t\t\texpect(captured!.firstRun).toBe(true)\n\t\t\texpect(captured!.name).toBe('test.event')\n\t\t\texpect(captured!.by).toBe('user-1')\n\t\t\texpect(captured!.at).toEqual(at)\n\t\t\texpect(captured!.ts).toBe(at.getTime())\n\t\t\texpect(captured!.body).toEqual({ x: 42 })\n\t\t\texpect(typeof captured!.key).toBe('string')\n\t\t})\n\n\t\ttest('handler throws → no EventLogSchema row remains', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ val: v.string() }),\n\t\t\t\thandle: async () => {\n\t\t\t\t\tthrow new Error('handler blew up')\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tawait expect(fire(repo, 'boom', def, { val: 'x' })).rejects.toThrow('handler blew up')\n\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(rows).toHaveLength(0)\n\t\t})\n\n\t\ttest('by defaults to null when not provided', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst def = {\n\t\t\t\tpipe: v.string(),\n\t\t\t\thandle: async () => {},\n\t\t\t}\n\n\t\t\tawait fire(repo, 'test', def, 'hello')\n\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(rows).toHaveLength(1)\n\t\t\texpect(rows[0].by).toBeNull()\n\t\t})\n\t})\n}\n","import { ulid } from 'ulid'\nimport { v } from 'valleyed'\n\nimport { Schema } from '../schema'\n\nexport const EventLogSchema = Schema.from('event_log')\n\t.pk('key', v.string(), () => ulid())\n\t.field('name', v.string())\n\t.field('ts', v.number())\n\t.field('body', v.any())\n\t.field('by', v.nullable(v.string()))\n\t.build()\n","import { v, type Pipe, type PipeOutput } from 'valleyed'\n\nimport { ComputedField, SchemaField, type AnyComputedField, type AnySchemaField } from './fields'\nimport type { Prettify } from './utils'\n\ntype AnyPrimaryKeyField = SchemaField<string, Pipe<any, any>, true>\n\nexport type AnySchema = Schema<string, AnyPrimaryKeyField, Record<string, AnySchemaField>, Record<string, AnyComputedField>>\n\nexport type SchemaComputedDefs<S extends AnySchema> = S extends Schema<any, any, any, infer C> ? C : Record<string, AnyComputedField>\n\nexport type SchemaPersistedOutput<S extends AnySchema> = Prettify<{\n\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] extends SchemaField<any, infer P, any> ? PipeOutput<P> : never\n}>\n\nexport type SchemaComputedOutput<S extends AnySchema> = Prettify<{\n\t[K in keyof SchemaComputedDefs<S>]: SchemaComputedDefs<S>[K] extends ComputedField<any, infer P, any> ? PipeOutput<P> : never\n}>\n\nexport type SchemaOutput<S extends AnySchema> = Prettify<{\n\t[K in keyof SchemaPersistedOutput<S> | keyof SchemaComputedOutput<S>]: K extends keyof SchemaPersistedOutput<S>\n\t\t? SchemaPersistedOutput<S>[K]\n\t\t: K extends keyof SchemaComputedOutput<S>\n\t\t\t? SchemaComputedOutput<S>[K]\n\t\t\t: never\n}>\n\nexport type SchemaFields<S extends AnySchema> =\n\tS extends Schema<any, infer PKField, infer F, any>\n\t\t? PKField extends AnySchemaField\n\t\t\t? Prettify<Record<PKField['name'], PKField> & F>\n\t\t\t: F\n\t\t: never\n\nexport type SchemaTaggedFields<S extends AnySchema> = {\n\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] & { readonly __schema?: S }\n}\n\nexport class SchemaBuilder<\n\tN extends string,\n\tPKField extends AnyPrimaryKeyField | never = never,\n\tF extends Record<string, AnySchemaField> = {},\n\tC extends Record<string, AnyComputedField> = {},\n> {\n\t#name: N\n\t#pkField: PKField | null\n\t#fieldDefs: F\n\t#computedDefs: C\n\n\tconstructor(name: N, pkField?: PKField | null, fieldDefs?: F, computedDefs?: C) {\n\t\tthis.#name = name\n\t\tthis.#pkField = pkField ?? null\n\t\tthis.#fieldDefs = fieldDefs ?? ({} as F)\n\t\tthis.#computedDefs = computedDefs ?? ({} as C)\n\t}\n\n\tget name() {\n\t\treturn this.#name\n\t}\n\n\tpk<K extends string, P extends Pipe<any, any>>(\n\t\tname: [PKField] extends [never] ? K : never,\n\t\tpipe: P,\n\t\tgenerate: () => PipeOutput<P>,\n\t): SchemaBuilder<N, SchemaField<K, P, true>, F> {\n\t\treturn new SchemaBuilder<N, SchemaField<K, P, true>, F>(\n\t\t\tthis.#name,\n\t\t\tnew SchemaField(name, pipe, { onCreate: generate }),\n\t\t\t{ ...this.#fieldDefs } as F,\n\t\t\tundefined,\n\t\t)\n\t}\n\n\tfield<\n\t\tK extends string,\n\t\tP extends Pipe<any, any>,\n\t\tO extends { onCreate?: () => PipeOutput<P>; onUpdate?: () => PipeOutput<P> } | undefined = undefined,\n\t>(\n\t\tname: K extends keyof F ? never : K,\n\t\tpipe: P,\n\t\topts?: O,\n\t): SchemaBuilder<\n\t\tN,\n\t\tPKField,\n\t\t{\n\t\t\t[Key in keyof F | K]: Key extends K\n\t\t\t\t? SchemaField<K, P, [O] extends [{ onCreate: () => any }] ? true : false>\n\t\t\t\t: Key extends keyof F\n\t\t\t\t\t? F[Key]\n\t\t\t\t\t: never\n\t\t}\n\t> {\n\t\tconst nextFields = { ...this.#fieldDefs, [name]: new SchemaField(name, pipe, opts) }\n\t\treturn new SchemaBuilder(this.#name, this.#pkField, nextFields, { ...this.#computedDefs }) as any\n\t}\n\n\tcomputed<\n\t\tK extends string,\n\t\tDeps extends readonly (keyof SchemaPersistedOutput<Schema<N, PKField, F, C>> & string)[],\n\t\tP extends Pipe<any, any>,\n\t>(\n\t\tname: K extends keyof SchemaPersistedOutput<Schema<N, PKField, F, C>> | keyof C ? never : K,\n\t\tdeps: Deps,\n\t\tpipe: P,\n\t\tcompute: (data: Pick<SchemaPersistedOutput<Schema<N, PKField, F, C>>, Deps[number]>) => PipeOutput<P>,\n\t): SchemaBuilder<\n\t\tN,\n\t\tPKField,\n\t\tF,\n\t\t{\n\t\t\t[Key in keyof C | K]: Key extends K ? ComputedField<K, P, Deps> : Key extends keyof C ? C[Key] : never\n\t\t}\n\t> {\n\t\tconst nextComputed = { ...this.#computedDefs, [name]: new ComputedField(name as K, pipe, deps, compute as any) }\n\t\treturn new SchemaBuilder(this.#name, this.#pkField, { ...this.#fieldDefs }, nextComputed) as any\n\t}\n\n\tbuild(this: [PKField] extends [never] ? never : SchemaBuilder<N, PKField, F, C>): Schema<N, PKField, F, C> {\n\t\tconst self = this as unknown as SchemaBuilder<N, PKField, F, C>\n\t\treturn new Schema<N, PKField, F, C>(self.#name, self.#pkField, self.#fieldDefs, self.#computedDefs)\n\t}\n}\n\nexport class Schema<\n\tN extends string,\n\tPKField extends AnyPrimaryKeyField | never = never,\n\tF extends Record<string, AnySchemaField> = {},\n\tC extends Record<string, AnyComputedField> = {},\n> {\n\t#name: N\n\t#pkField: PKField | null\n\t#fieldDefs: F\n\t#computedDefs: C\n\n\tconstructor(name: N, pkField: PKField | null, fieldDefs: F, computedDefs: C) {\n\t\tthis.#name = name\n\t\tthis.#pkField = pkField\n\t\tthis.#fieldDefs = fieldDefs\n\t\tthis.#computedDefs = computedDefs\n\t\tif (this.#pkField) {\n\t\t\tObject.defineProperty(this.#pkField, '__schema', { value: this, enumerable: false, configurable: true })\n\t\t}\n\t\tfor (const field of Object.values(this.#fieldDefs)) {\n\t\t\tObject.defineProperty(field, '__schema', { value: this, enumerable: false, configurable: true })\n\t\t}\n\t}\n\n\tget name() {\n\t\treturn this.#name\n\t}\n\n\tget pkField() {\n\t\tif (!this.#pkField) throw new Error(`Schema \"${this.#name}\" does not have a primary key defined`)\n\t\treturn this.#pkField\n\t}\n\n\tget fieldDefs(): F {\n\t\treturn this.#fieldDefs\n\t}\n\n\tget computedDefs(): C {\n\t\treturn this.#computedDefs\n\t}\n\n\tget fields() {\n\t\treturn {\n\t\t\t...(this.#pkField ? { [this.#pkField.name]: this.#pkField } : {}),\n\t\t\t...this.#fieldDefs,\n\t\t} as unknown as SchemaTaggedFields<this>\n\t}\n\n\tstatic from<N extends string>(name: N): SchemaBuilder<N> {\n\t\treturn new SchemaBuilder(name)\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf } = import.meta.vitest\n\n\tdescribe('SchemaBuilder', () => {\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => 'generated-id')\n\t\t\t.field('email', v.string())\n\t\t\t.field('name', v.string())\n\t\t\t.field('age', v.optional(v.number()))\n\t\t\t.field('createdAt', v.number(), { onCreate: () => 1000 })\n\t\t\t.field('updatedAt', v.number(), { onCreate: () => 1000, onUpdate: () => 2000 })\n\t\t\t.build()\n\n\t\tdescribe('Schema.from()', () => {\n\t\t\ttest('returns a SchemaBuilder instance', () => {\n\t\t\t\texpect(Schema.from('test')).toBeInstanceOf(SchemaBuilder)\n\t\t\t})\n\n\t\t\ttest('returns a Schema instance after build', () => {\n\t\t\t\tconst s = Schema.from('test').pk('id', v.string(), () => 'x').build()\n\t\t\t\texpect(s).toBeInstanceOf(Schema)\n\t\t\t})\n\n\t\t\ttest('Schema has no builder methods', () => {\n\t\t\t\tconst s = Schema.from('test').pk('id', v.string(), () => 'x').build()\n\t\t\t\texpect(s).not.toHaveProperty('pk')\n\t\t\t\texpect(s).not.toHaveProperty('field')\n\t\t\t\texpect(s).not.toHaveProperty('computed')\n\t\t\t\texpect(s).not.toHaveProperty('build')\n\t\t\t})\n\n\t\t\ttest('stores the schema name on builder', () => {\n\t\t\t\texpect(Schema.from('orders').name).toBe('orders')\n\t\t\t})\n\n\t\t\ttest('stores the schema name after build', () => {\n\t\t\t\texpect(Schema.from('orders').pk('id', v.string(), () => 'x').build().name).toBe('orders')\n\t\t\t})\n\n\t\t\ttest('full builder chain works', () => {\n\t\t\t\tconst s = Schema.from('users')\n\t\t\t\t\t.pk('id', v.string(), () => 'gen')\n\t\t\t\t\t.field('email', v.string())\n\t\t\t\t\t.field('age', v.number())\n\t\t\t\t\t.build()\n\t\t\t\texpect(s.name).toBe('users')\n\t\t\t\texpect(s.pkField.name).toBe('id')\n\t\t\t\texpect(Object.keys(s.fields)).toEqual(['id', 'email', 'age'])\n\t\t\t})\n\n\t\t\ttest('.build() is unavailable without .pk() at the type level', () => {\n\t\t\t\t// @ts-expect-error — .build() requires .pk() to have been called\n\t\t\t\tSchema.from('test').field('name', v.string()).build()\n\t\t\t})\n\t\t})\n\n\t\tdescribe('.name', () => {\n\t\t\ttest('returns the schema name', () => {\n\t\t\t\texpect(UserSchema.name).toBe('users')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('.pk()', () => {\n\t\t\ttest('pkField returns the pk field', () => {\n\t\t\t\texpect(UserSchema.pkField.name).toBe('id')\n\t\t\t})\n\n\t\t\ttest('pk entry is a SchemaField instance', () => {\n\t\t\t\texpect(UserSchema.fields.id).toBeInstanceOf(SchemaField)\n\t\t\t})\n\n\t\t\ttest('pk entry has correct pipe', () => {\n\t\t\t\texpect(UserSchema.fields.id.pipe).toBeDefined()\n\t\t\t})\n\n\t\t\ttest('pk entry has correct name and path', () => {\n\t\t\t\texpect(UserSchema.fields.id.name).toBe('id')\n\t\t\t\texpect(UserSchema.fields.id.path).toEqual(['id'])\n\t\t\t})\n\n\t\t\ttest('pk onCreate generates the value', () => {\n\t\t\t\texpect(UserSchema.fields.id.onCreate?.()).toBe('generated-id')\n\t\t\t})\n\n\t\t\ttest('pkField throws when no pk defined', () => {\n\t\t\t\tconst schema = new Schema('nopk', null, { name: new SchemaField('name', v.string()) }, {})\n\t\t\t\texpect(() => schema.pkField).toThrow()\n\t\t\t})\n\t\t})\n\n\t\tdescribe('.field()', () => {\n\t\t\ttest('all field entries are SchemaField instances', () => {\n\t\t\t\tfor (const entry of Object.values(UserSchema.fieldDefs)) {\n\t\t\t\t\texpect(entry).toBeInstanceOf(SchemaField)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\ttest('field has correct name and path', () => {\n\t\t\t\texpect(UserSchema.fields.email.name).toBe('email')\n\t\t\t\texpect(UserSchema.fields.email.path).toEqual(['email'])\n\t\t\t})\n\n\t\t\ttest('field has correct pipe', () => {\n\t\t\t\texpect(UserSchema.fields.email.pipe).toBeDefined()\n\t\t\t\texpect(UserSchema.fieldDefs.email.pipe).toBe(UserSchema.fields.email.pipe)\n\t\t\t})\n\n\t\t\ttest('field with onCreate stores the function', () => {\n\t\t\t\texpect(UserSchema.fields.createdAt.onCreate?.()).toBe(1000)\n\t\t\t})\n\n\t\t\ttest('field with onUpdate stores the function', () => {\n\t\t\t\texpect(UserSchema.fields.updatedAt.onUpdate?.()).toBe(2000)\n\t\t\t})\n\n\t\t\ttest('field with both hooks stores both', () => {\n\t\t\t\texpect(UserSchema.fields.updatedAt.onCreate?.()).toBe(1000)\n\t\t\t\texpect(UserSchema.fields.updatedAt.onUpdate?.()).toBe(2000)\n\t\t\t})\n\n\t\t\ttest('field with no lifecycle has undefined hooks', () => {\n\t\t\t\texpect(UserSchema.fields.email.onCreate).toBeUndefined()\n\t\t\t\texpect(UserSchema.fields.email.onUpdate).toBeUndefined()\n\t\t\t})\n\t\t})\n\n\t\tdescribe('.computed()', () => {\n\t\t\ttest('registers computed fields and dependencies', () => {\n\t\t\t\tconst WithComputed = Schema.from('users')\n\t\t\t\t\t.pk('id', v.string(), () => 'u1')\n\t\t\t\t\t.field('name', v.string())\n\t\t\t\t\t.field('email', v.string())\n\t\t\t\t\t.computed('display', ['name', 'email'], v.string(), ({ name, email }) => `${name} <${email}>`)\n\t\t\t\t\t.build()\n\n\t\t\t\texpect(Object.keys(WithComputed.computedDefs)).toEqual(['display'])\n\t\t\t\texpect(WithComputed.computedDefs.display.deps).toEqual(['name', 'email'])\n\t\t\t\texpect(WithComputed.computedDefs.display.compute({ name: 'Alice', email: 'a@b.com' })).toBe('Alice <a@b.com>')\n\t\t\t})\n\n\t\t\ttest('computed field names are included in schema output type', () => {\n\t\t\t\tconst WithComputed = Schema.from('users')\n\t\t\t\t\t.pk('id', v.string(), () => 'u1')\n\t\t\t\t\t.field('name', v.string())\n\t\t\t\t\t.computed('nameUpper', ['name'], v.string(), ({ name }) => name.toUpperCase())\n\t\t\t\t\t.build()\n\n\t\t\t\ttype Out = SchemaOutput<typeof WithComputed>\n\t\t\t\tconst value: Out = { id: 'u1', name: 'Alice', nameUpper: 'ALICE' }\n\t\t\t\texpect(Object.keys(WithComputed.computedDefs)).toEqual(['nameUpper'])\n\t\t\t\texpect(value.nameUpper).toBe('ALICE')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('.fields', () => {\n\t\t\ttest('includes all keys including pk', () => {\n\t\t\t\texpect(Object.keys(UserSchema.fields)).toEqual(['id', 'email', 'name', 'age', 'createdAt', 'updatedAt'])\n\t\t\t})\n\n\t\t\ttest('pk entry appears first', () => {\n\t\t\t\texpect(Object.keys(UserSchema.fields)[0]).toBe('id')\n\t\t\t})\n\n\t\t\ttest('schema with no fields has only pk in fields', () => {\n\t\t\t\tconst PkOnly = Schema.from('minimal').pk('id', v.string(), () => 'x').build()\n\t\t\t\texpect(Object.keys(PkOnly.fields)).toEqual(['id'])\n\t\t\t})\n\t\t})\n\n\t\tdescribe('.fieldDefs', () => {\n\t\t\ttest('excludes the pk field', () => {\n\t\t\t\texpect(Object.keys(UserSchema.fieldDefs)).not.toContain('id')\n\t\t\t})\n\n\t\t\ttest('contains all non-pk fields in order', () => {\n\t\t\t\texpect(Object.keys(UserSchema.fieldDefs)).toEqual(['email', 'name', 'age', 'createdAt', 'updatedAt'])\n\t\t\t})\n\n\t\t\ttest('schema with no fields has empty fieldDefs', () => {\n\t\t\t\tconst PkOnly = Schema.from('minimal').pk('id', v.string(), () => 'x').build()\n\t\t\t\texpect(Object.keys(PkOnly.fieldDefs)).toHaveLength(0)\n\t\t\t})\n\t\t})\n\t})\n\n\tdescribe('type-level: Schema.from uniqueness guard', () => {\n\t\ttest('duplicate .field() name is a TS error', () => {\n\t\t\t// @ts-expect-error — duplicate field name 'email' should fail\n\t\t\tSchema.from('test').pk('id', v.string(), () => 'x').field('email', v.string()).field('email', v.string()).build()\n\t\t})\n\t})\n\n\tdescribe('clone-on-step: fan-out independence', () => {\n\t\ttest('.field() returns a new builder, not the same instance', () => {\n\t\t\tconst base = Schema.from('test').pk('id', v.string(), () => 'x')\n\t\t\tconst a = base.field('email', v.string())\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\n\t\ttest('.pk() returns a new builder, not the same instance', () => {\n\t\t\tconst base = Schema.from('test')\n\t\t\tconst a = base.pk('id', v.string(), () => 'x')\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\n\t\ttest('.computed() returns a new builder, not the same instance', () => {\n\t\t\tconst base = Schema.from('test').pk('id', v.string(), () => 'x').field('name', v.string())\n\t\t\tconst a = base.computed('upper', ['name'], v.string(), ({ name }) => name.toUpperCase())\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\n\t\ttest('fan-out from shared base does not pollute either branch', () => {\n\t\t\tconst base = Schema.from('test').pk('id', v.string(), () => 'x')\n\t\t\tconst branchA = base.field('email', v.string()).build()\n\t\t\tconst branchB = base.field('age', v.number()).build()\n\n\t\t\texpect(Object.keys(branchA.fieldDefs)).toEqual(['email'])\n\t\t\texpect(Object.keys(branchB.fieldDefs)).toEqual(['age'])\n\t\t})\n\n\t\ttest('fan-out with computed does not pollute base', () => {\n\t\t\tconst base = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\tconst withComputed = base.computed('upper', ['name'], v.string(), ({ name }) => name.toUpperCase()).build()\n\t\t\tconst withoutComputed = base.build()\n\n\t\t\texpect(Object.keys(withComputed.computedDefs)).toEqual(['upper'])\n\t\t\texpect(Object.keys(withoutComputed.computedDefs)).toEqual([])\n\t\t})\n\t})\n\n\tdescribe('type-level: schema-tagged Fields', () => {\n\t\ttest('fields accessor returns schema-tagged Field instances', () => {\n\t\t\tconst _TestSchema = Schema.from('test').pk('id', v.string(), () => 'x').field('email', v.string()).build()\n\t\t\ttype FieldS = NonNullable<(typeof _TestSchema.fields.id)['__schema']>\n\t\t\texpectTypeOf<FieldS>().toEqualTypeOf<typeof _TestSchema>()\n\n\t\t\ttype FieldS2 = NonNullable<(typeof _TestSchema.fields.email)['__schema']>\n\t\t\texpectTypeOf<FieldS2>().toEqualTypeOf<typeof _TestSchema>()\n\t\t})\n\n\t\ttest('fields carry runtime __schema reference to parent schema', () => {\n\t\t\tconst TestSchema = Schema.from('test').pk('id', v.string(), () => 'x').field('email', v.string()).build()\n\t\t\texpect((TestSchema.fields.id as any).__schema).toBe(TestSchema)\n\t\t\texpect((TestSchema.fields.email as any).__schema).toBe(TestSchema)\n\t\t})\n\t})\n}\n","import type { Pipe, PipeInput, PipeOutput } from 'valleyed'\n\nimport { EquippedError } from '../../errors'\n\nexport type EventContext = {\n\tkey: string\n\tname: string\n\tts: number\n\tbody: unknown\n\tby: string | null\n\tat: Date\n\tfirstRun: boolean\n}\n\nexport type HandlerDef<P extends Pipe<any, any> = Pipe<any, any>, R = unknown> = {\n\tpipe: P\n\thandle: (payload: PipeOutput<P>, ctx: EventContext) => R | Promise<R>\n}\n\nexport type FireFn<P extends Pipe<any, any>, R> = (\n\tpayload: PipeInput<P>,\n\tctx?: { by?: string; at?: Date },\n) => Promise<R>\n\nexport class HandlerRegistry {\n\treadonly #handlers = new Map<string, HandlerDef>()\n\treadonly #order: string[] = []\n\n\tregister(name: string, def: HandlerDef): void {\n\t\tif (this.#handlers.has(name)) {\n\t\t\tthrow new EquippedError(`EventLog handler \"${name}\" is already registered`, { name })\n\t\t}\n\t\tthis.#handlers.set(name, def)\n\t\tthis.#order.push(name)\n\t}\n\n\tget(name: string): HandlerDef {\n\t\tconst def = this.#handlers.get(name)\n\t\tif (!def) {\n\t\t\tthrow new EquippedError(`EventLog handler \"${name}\" is not registered`, { name })\n\t\t}\n\t\treturn def\n\t}\n\n\tlist(): string[] {\n\t\treturn [...this.#order]\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { EquippedError } = await import('../../errors')\n\n\tdescribe('HandlerRegistry', () => {\n\t\ttest('register and get a handler', () => {\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tconst def: HandlerDef = { pipe: v.string(), handle: () => {} }\n\t\t\tregistry.register('user.signup', def)\n\t\t\texpect(registry.get('user.signup')).toBe(def)\n\t\t})\n\n\t\ttest('register duplicate name throws', () => {\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tconst def: HandlerDef = { pipe: v.string(), handle: () => {} }\n\t\t\tregistry.register('user.signup', def)\n\t\t\texpect(() => registry.register('user.signup', def)).toThrow(EquippedError)\n\t\t\texpect(() => registry.register('user.signup', def)).toThrow('already registered')\n\t\t})\n\n\t\ttest('get missing name throws', () => {\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\texpect(() => registry.get('nonexistent')).toThrow(EquippedError)\n\t\t\texpect(() => registry.get('nonexistent')).toThrow('not registered')\n\t\t})\n\n\t\ttest('list returns names in registration order', () => {\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tregistry.register('c', { pipe: v.string(), handle: () => {} })\n\t\t\tregistry.register('a', { pipe: v.string(), handle: () => {} })\n\t\t\tregistry.register('b', { pipe: v.string(), handle: () => {} })\n\t\t\texpect(registry.list()).toEqual(['c', 'a', 'b'])\n\t\t})\n\n\t\ttest('list returns a copy, not a reference', () => {\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tregistry.register('x', { pipe: v.string(), handle: () => {} })\n\t\t\tconst list = registry.list()\n\t\t\tlist.push('y')\n\t\t\texpect(registry.list()).toEqual(['x'])\n\t\t})\n\t})\n}\n","import type { EventContext, HandlerDef, HandlerRegistry } from './registry'\nimport { EventLogSchema } from './schema'\nimport { OrmReplayError } from '../errors/replay'\nimport type { Repo } from '../repo/repo'\n\nconst executeReplay = async (\n\trepo: Repo<any>,\n\tdef: HandlerDef,\n\trow: { key: string; name: string; ts: number; body: unknown; by: string | null },\n): Promise<void> => {\n\tconst evCtx: EventContext = {\n\t\tkey: row.key,\n\t\tname: row.name,\n\t\tts: row.ts,\n\t\tbody: row.body,\n\t\tby: row.by,\n\t\tat: new Date(row.ts),\n\t\tfirstRun: false,\n\t}\n\ttry {\n\t\tawait repo.session(async () => {\n\t\t\tawait def.handle(row.body, evCtx)\n\t\t})\n\t} catch (cause) {\n\t\tthrow new OrmReplayError({ key: row.key, name: row.name, cause })\n\t}\n}\n\nexport async function replay(\n\trepo: Repo<any>,\n\tregistry: HandlerRegistry,\n\topts?: { from?: Date },\n): Promise<void> {\n\tlet query = repo.on(EventLogSchema).all().orderBy('ts', 'asc')\n\tif (opts?.from) {\n\t\tquery = query.where((q) => q.gte('ts', opts.from!.getTime()))\n\t}\n\tconst rows = await query.find()\n\n\tfor (const row of rows) {\n\t\tawait executeReplay(repo, registry.get(row.name), row)\n\t}\n}\n\nexport async function rerun(\n\trepo: Repo<any>,\n\tregistry: HandlerRegistry,\n\tkey: string,\n): Promise<void> {\n\tconst row = await repo.on(EventLogSchema).one().id(key).find()\n\tif (!row) {\n\t\tthrow new OrmReplayError({ key, name: 'unknown', cause: new Error(`Event with key \"${key}\" not found`) })\n\t}\n\tawait executeReplay(repo, registry.get(row.name), row)\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { Schema } = await import('../schema')\n\tconst { Repo } = await import('../repo/repo')\n\tconst { OrmReplayError } = await import('../errors/replay')\n\tconst { HandlerRegistry } = await import('./registry')\n\tconst { EventLogSchema } = await import('./schema')\n\tconst { fire } = await import('./executor')\n\ttype EventContext = import('./registry').EventContext\n\n\tconst CounterSchema = Schema.from('counters')\n\t\t.pk('id', v.string(), () => `c-${Math.random()}`)\n\t\t.field('name', v.string())\n\t\t.field('count', v.number())\n\t\t.build()\n\n\tfunction makeRepo() {\n\t\tconst adapter = InMemoryAdapter.create({})\n\t\treturn new Repo({\n\t\t\tadapter,\n\t\t\tresolve: (s) => {\n\t\t\t\tif (s === EventLogSchema) return { table: 'events' }\n\t\t\t\tif (s === CounterSchema) return { table: 'counters' }\n\t\t\t\treturn { table: s.name }\n\t\t\t},\n\t\t})\n\t}\n\n\tdescribe('ReplayWalker', () => {\n\t\ttest('replay walks rows in ts-ascending order', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tconst order: string[] = []\n\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ label: v.string() }),\n\t\t\t\thandle: async (payload: { label: string }) => {\n\t\t\t\t\torder.push(payload.label)\n\t\t\t\t},\n\t\t\t}\n\t\t\tregistry.register('test', def)\n\n\t\t\tawait fire(repo, 'test', def, { label: 'second' }, { at: new Date('2025-01-02') })\n\t\t\tawait fire(repo, 'test', def, { label: 'first' }, { at: new Date('2025-01-01') })\n\t\t\tawait fire(repo, 'test', def, { label: 'third' }, { at: new Date('2025-01-03') })\n\n\t\t\torder.length = 0\n\t\t\tawait replay(repo, registry)\n\n\t\t\texpect(order).toEqual(['first', 'second', 'third'])\n\t\t})\n\n\t\ttest('replay({ from }) skips rows with ts < from', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tconst order: string[] = []\n\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ label: v.string() }),\n\t\t\t\thandle: async (payload: { label: string }) => {\n\t\t\t\t\torder.push(payload.label)\n\t\t\t\t},\n\t\t\t}\n\t\t\tregistry.register('test', def)\n\n\t\t\tawait fire(repo, 'test', def, { label: 'old' }, { at: new Date('2025-01-01') })\n\t\t\tawait fire(repo, 'test', def, { label: 'new' }, { at: new Date('2025-01-03') })\n\n\t\t\torder.length = 0\n\t\t\tawait replay(repo, registry, { from: new Date('2025-01-02') })\n\n\t\t\texpect(order).toEqual(['new'])\n\t\t})\n\n\t\ttest('replay passes firstRun: false in EventContext', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tlet captured: EventContext | undefined\n\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ x: v.number() }),\n\t\t\t\thandle: async (_payload: unknown, ctx: EventContext) => {\n\t\t\t\t\tcaptured = ctx\n\t\t\t\t},\n\t\t\t}\n\t\t\tregistry.register('test', def)\n\n\t\t\tawait fire(repo, 'test', def, { x: 1 })\n\n\t\t\tawait replay(repo, registry)\n\n\t\t\texpect(captured).toBeDefined()\n\t\t\texpect(captured!.firstRun).toBe(false)\n\t\t})\n\n\t\ttest('replay throws OrmReplayError on first failure with key/name/cause', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\n\t\t\tconst boom = new Error('handler exploded')\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ fail: v.boolean() }),\n\t\t\t\thandle: async (payload: { fail: boolean }, ctx: EventContext) => {\n\t\t\t\t\tif (!ctx.firstRun && payload.fail) throw boom\n\t\t\t\t},\n\t\t\t}\n\t\t\tregistry.register('evt', def)\n\n\t\t\tawait fire(repo, 'evt', def, { fail: false }, { at: new Date('2025-01-01') })\n\t\t\tawait fire(repo, 'evt', def, { fail: true }, { at: new Date('2025-01-02') })\n\t\t\tawait fire(repo, 'evt', def, { fail: false }, { at: new Date('2025-01-03') })\n\n\t\t\tconst rows = await repo.on(EventLogSchema).all().orderBy('ts', 'asc').find()\n\t\t\tconst failKey = rows[1].key\n\n\t\t\tawait expect(replay(repo, registry)).rejects.toThrow(OrmReplayError)\n\t\t\ttry {\n\t\t\t\tawait replay(repo, registry)\n\t\t\t} catch (e: any) {\n\t\t\t\texpect(e.key).toBe(failKey)\n\t\t\t\texpect(e.eventName).toBe('evt')\n\t\t\t\texpect(e.cause).toBe(boom)\n\t\t\t}\n\t\t})\n\n\t\ttest('replay stops on first failure — events after failure not attempted', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tconst executed: string[] = []\n\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ label: v.string(), fail: v.boolean() }),\n\t\t\t\thandle: async (payload: { label: string; fail: boolean }, ctx: EventContext) => {\n\t\t\t\t\texecuted.push(payload.label)\n\t\t\t\t\tif (!ctx.firstRun && payload.fail) throw new Error('boom')\n\t\t\t\t},\n\t\t\t}\n\t\t\tregistry.register('test', def)\n\n\t\t\tawait fire(repo, 'test', def, { label: 'A', fail: false }, { at: new Date('2025-01-01') })\n\t\t\tawait fire(repo, 'test', def, { label: 'B', fail: true }, { at: new Date('2025-01-02') })\n\t\t\tawait fire(repo, 'test', def, { label: 'C', fail: false }, { at: new Date('2025-01-03') })\n\n\t\t\texecuted.length = 0\n\t\t\tawait expect(replay(repo, registry)).rejects.toThrow(OrmReplayError)\n\n\t\t\texpect(executed).toEqual(['A', 'B'])\n\t\t})\n\n\t\ttest('per-event session isolation: failure on event N does not roll back events 1..N−1', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ name: v.string(), fail: v.boolean() }),\n\t\t\t\thandle: async (payload: { name: string; fail: boolean }, ctx: EventContext) => {\n\t\t\t\t\tawait repo.on(CounterSchema).one().create({ name: payload.name, count: 1 })\n\t\t\t\t\tif (!ctx.firstRun && payload.fail) throw new Error('boom')\n\t\t\t\t},\n\t\t\t}\n\t\t\tregistry.register('test', def)\n\n\t\t\tawait fire(repo, 'test', def, { name: 'alpha', fail: false }, { at: new Date('2025-01-01') })\n\t\t\tawait fire(repo, 'test', def, { name: 'beta', fail: true }, { at: new Date('2025-01-02') })\n\n\t\t\tconst countersBefore = await repo.on(CounterSchema).all().find()\n\t\t\texpect(countersBefore).toHaveLength(2)\n\n\t\t\tawait expect(replay(repo, registry)).rejects.toThrow(OrmReplayError)\n\n\t\t\tconst countersAfter = await repo.on(CounterSchema).all().find()\n\t\t\texpect(countersAfter).toHaveLength(3)\n\t\t\texpect(countersAfter.filter((c) => c.name === 'alpha')).toHaveLength(2)\n\t\t})\n\n\t\ttest('rerun re-executes a single event with firstRun: false', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\t\t\tlet captured: EventContext | undefined\n\n\t\t\tconst def = {\n\t\t\t\tpipe: v.object({ x: v.number() }),\n\t\t\t\thandle: async (_payload: unknown, ctx: EventContext) => {\n\t\t\t\t\tcaptured = ctx\n\t\t\t\t},\n\t\t\t}\n\t\t\tregistry.register('test', def)\n\n\t\t\tawait fire(repo, 'test', def, { x: 42 })\n\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\tconst key = rows[0].key\n\n\t\t\tawait rerun(repo, registry, key)\n\n\t\t\texpect(captured).toBeDefined()\n\t\t\texpect(captured!.firstRun).toBe(false)\n\t\t\texpect(captured!.key).toBe(key)\n\t\t\texpect(captured!.body).toEqual({ x: 42 })\n\t\t})\n\n\t\ttest('rerun throws OrmReplayError for missing key', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst registry = new HandlerRegistry()\n\n\t\t\tawait expect(rerun(repo, registry, 'nonexistent')).rejects.toThrow(OrmReplayError)\n\t\t})\n\t})\n}\n","import type { Pipe, PipeInput } from 'valleyed'\n\nimport { fire } from './executor'\nimport { HandlerRegistry, type FireFn, type HandlerDef } from './registry'\nimport { replay, rerun } from './walker'\nimport type { OrmAdapterLike } from '../adapters/base'\nimport type { Repo } from '../repo/repo'\n\nexport { type EventContext } from './registry'\n\nexport class EventLog<A extends OrmAdapterLike<any>> {\n\treadonly #repo: Repo<A>\n\treadonly #registry: HandlerRegistry\n\n\tconstructor(repo: Repo<A>, registry: HandlerRegistry) {\n\t\tthis.#repo = repo\n\t\tthis.#registry = registry\n\t}\n\n\thandler<P extends Pipe<any, any>, R>(\n\t\tname: string,\n\t\tdef: HandlerDef<P, R>,\n\t): FireFn<P, R> {\n\t\tthis.#registry.register(name, def)\n\t\treturn (payload: PipeInput<P>, ctx?: { by?: string; at?: Date }) =>\n\t\t\tfire<R>(this.#repo as Repo<any>, name, def, payload, ctx)\n\t}\n\n\treplay(opts?: { from?: Date }): Promise<void> {\n\t\treturn replay(this.#repo as Repo<any>, this.#registry, opts)\n\t}\n\n\trerun(key: string): Promise<void> {\n\t\treturn rerun(this.#repo as Repo<any>, this.#registry, key)\n\t}\n\n\tstatic from<A extends OrmAdapterLike<any>>(repo: Repo<A>) {\n\t\treturn { build: () => new EventLog(repo, new HandlerRegistry()) }\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { Schema } = await import('../schema')\n\tconst { Repo } = await import('../repo/repo')\n\tconst { OrmValidationError } = await import('../errors')\n\tconst { EventLogSchema } = await import('./schema')\n\n\tconst UserSchema = Schema.from('users')\n\t\t.pk('id', v.string(), () => `u-${Math.random()}`)\n\t\t.field('email', v.string())\n\t\t.field('createdAt', v.number(), { onCreate: () => Date.now() })\n\t\t.build()\n\n\tfunction makeRepo() {\n\t\tconst adapter = InMemoryAdapter.create({})\n\t\treturn new Repo({\n\t\t\tadapter,\n\t\t\tresolve: (s) => {\n\t\t\t\tif (s === EventLogSchema) return { table: 'events' }\n\t\t\t\tif (s === UserSchema) return { table: 'users' }\n\t\t\t\treturn { table: s.name }\n\t\t\t},\n\t\t})\n\t}\n\n\tdescribe('EventLog', () => {\n\t\ttest('EventLog.from(repo).build() constructs a working instance', () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\t\t\texpect(log).toBeInstanceOf(EventLog)\n\t\t})\n\n\t\ttest('handler returns a typed fire function', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\n\t\t\tconst fireSignup = log.handler('user.signup', {\n\t\t\t\tpipe: v.object({ email: v.string() }),\n\t\t\t\thandle: async (payload) => `created-${payload.email}`,\n\t\t\t})\n\n\t\t\tconst result = await fireSignup({ email: 'a@b.com' }, { by: 'admin' })\n\t\t\texpect(result).toBe('created-a@b.com')\n\t\t})\n\n\t\ttest('duplicate handler name throws', () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\t\t\tconst def = { pipe: v.string(), handle: async () => {} }\n\n\t\t\tlog.handler('test', def)\n\t\t\texpect(() => log.handler('test', def)).toThrow('already registered')\n\t\t})\n\n\t\ttest('fire persists EventLogSchema row', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\n\t\t\tconst fireEvent = log.handler('order.placed', {\n\t\t\t\tpipe: v.object({ item: v.string(), qty: v.number() }),\n\t\t\t\thandle: async () => {},\n\t\t\t})\n\n\t\t\tawait fireEvent({ item: 'widget', qty: 3 }, { by: 'user-42' })\n\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(rows).toHaveLength(1)\n\t\t\texpect(rows[0].name).toBe('order.placed')\n\t\t\texpect(rows[0].body).toEqual({ item: 'widget', qty: 3 })\n\t\t\texpect(rows[0].by).toBe('user-42')\n\t\t})\n\n\t\ttest('invalid payload throws OrmValidationError, no row persisted', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\n\t\t\tconst fireEvent = log.handler('test', {\n\t\t\t\tpipe: v.object({ x: v.number() }),\n\t\t\t\thandle: async () => {},\n\t\t\t})\n\n\t\t\tawait expect(fireEvent({ x: 'not-a-number' } as any)).rejects.toThrow(OrmValidationError)\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(rows).toHaveLength(0)\n\t\t})\n\n\t\ttest('handler throw rolls back session — no row persisted, no side writes', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\n\t\t\tconst fireEvent = log.handler('user.create', {\n\t\t\t\tpipe: v.object({ email: v.string() }),\n\t\t\t\thandle: async (payload) => {\n\t\t\t\t\tawait repo.on(UserSchema).one().create({ email: payload.email })\n\t\t\t\t\tthrow new Error('oops')\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tawait expect(fireEvent({ email: 'a@b.com' })).rejects.toThrow('oops')\n\n\t\t\tconst eventRows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(eventRows).toHaveLength(0)\n\n\t\t\tconst userRows = await repo.on(UserSchema).all().find()\n\t\t\texpect(userRows).toHaveLength(0)\n\t\t})\n\n\t\ttest('handler receives firstRun: true and correct EventContext', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\t\t\tlet captured: any\n\n\t\t\tconst fireEvent = log.handler('test', {\n\t\t\t\tpipe: v.object({ val: v.number() }),\n\t\t\t\thandle: async (_payload, ctx) => {\n\t\t\t\t\tcaptured = ctx\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tconst at = new Date('2025-06-01T00:00:00Z')\n\t\t\tawait fireEvent({ val: 99 }, { by: 'admin', at })\n\n\t\t\texpect(captured.firstRun).toBe(true)\n\t\t\texpect(captured.name).toBe('test')\n\t\t\texpect(captured.by).toBe('admin')\n\t\t\texpect(captured.at).toEqual(at)\n\t\t\texpect(captured.ts).toBe(at.getTime())\n\t\t\texpect(captured.body).toEqual({ val: 99 })\n\t\t})\n\n\t\ttest('end-to-end: register → fire → replay flow', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\t\t\tconst replayedPayloads: unknown[] = []\n\n\t\t\tconst fireEvent = log.handler('order.placed', {\n\t\t\t\tpipe: v.object({ item: v.string() }),\n\t\t\t\thandle: async (payload, ctx) => {\n\t\t\t\t\tif (!ctx.firstRun) replayedPayloads.push(payload)\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tawait fireEvent({ item: 'A' }, { at: new Date('2025-01-02') })\n\t\t\tawait fireEvent({ item: 'B' }, { at: new Date('2025-01-01') })\n\n\t\t\tawait log.replay()\n\n\t\t\texpect(replayedPayloads).toEqual([{ item: 'B' }, { item: 'A' }])\n\t\t})\n\n\t\ttest('rerun re-executes a single event with firstRun: false', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\t\t\tlet rerunCtx: any\n\n\t\t\tconst fireEvent = log.handler('test', {\n\t\t\t\tpipe: v.object({ val: v.number() }),\n\t\t\t\thandle: async (_payload, ctx) => {\n\t\t\t\t\tif (!ctx.firstRun) rerunCtx = ctx\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tawait fireEvent({ val: 7 }, { by: 'admin' })\n\n\t\t\tconst rows = await repo.on(EventLogSchema).all().find()\n\t\t\tawait log.rerun(rows[0].key)\n\n\t\t\texpect(rerunCtx).toBeDefined()\n\t\t\texpect(rerunCtx.firstRun).toBe(false)\n\t\t\texpect(rerunCtx.body).toEqual({ val: 7 })\n\t\t\texpect(rerunCtx.by).toBe('admin')\n\t\t})\n\n\t\ttest('repo.session(() => log.replay()) makes replay atomic — failure rolls back all', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\n\t\t\tconst fireEvent = log.handler('user.create', {\n\t\t\t\tpipe: v.object({ email: v.string(), fail: v.boolean() }),\n\t\t\t\thandle: async (payload, ctx) => {\n\t\t\t\t\tawait repo.on(UserSchema).one().create({ email: payload.email })\n\t\t\t\t\tif (!ctx.firstRun && payload.fail) throw new Error('replay boom')\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tawait fireEvent({ email: 'alice@test.com', fail: false }, { at: new Date('2025-01-01') })\n\t\t\tawait fireEvent({ email: 'bob@test.com', fail: true }, { at: new Date('2025-01-02') })\n\n\t\t\tconst usersBefore = await repo.on(UserSchema).all().find()\n\t\t\texpect(usersBefore).toHaveLength(2)\n\n\t\t\tawait expect(\n\t\t\t\trepo.session(() => log.replay()),\n\t\t\t).rejects.toThrow()\n\n\t\t\tconst usersAfter = await repo.on(UserSchema).all().find()\n\t\t\texpect(usersAfter).toHaveLength(2)\n\t\t})\n\n\t\ttest('end-to-end: fire persists row + handler-side writes atomically', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst log = EventLog.from(repo).build()\n\n\t\t\tconst fireSignup = log.handler('user.signup', {\n\t\t\t\tpipe: v.object({ email: v.string() }),\n\t\t\t\thandle: async (payload, evCtx) => {\n\t\t\t\t\tconst user = await repo.on(UserSchema).one().create({\n\t\t\t\t\t\temail: payload.email,\n\t\t\t\t\t\tcreatedAt: evCtx.at.getTime(),\n\t\t\t\t\t})\n\t\t\t\t\treturn user\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tconst user = await fireSignup({ email: 'alice@test.com' }, { by: 'system' })\n\t\t\texpect(user.email).toBe('alice@test.com')\n\n\t\t\tconst eventRows = await repo.on(EventLogSchema).all().find()\n\t\t\texpect(eventRows).toHaveLength(1)\n\n\t\t\tconst userRows = await repo.on(UserSchema).all().find()\n\t\t\texpect(userRows).toHaveLength(1)\n\t\t})\n\t})\n}\n","import type { FieldTypeName } from '../adapter'\nimport type { OrmAdapter } from '../orm-adapter'\nimport type { AnySchema } from '../schema'\nimport type { DiscoveredField, DiscoveredSchema } from './introspection-types'\nimport type { AnyChange } from './types'\n\nconst EMISSION_ORDER: AnyChange['kind'][] = [\n\t'dropForeignKey',\n\t'dropIndex',\n\t'dropField',\n\t'dropTable',\n\t'renameTable',\n\t'renameField',\n\t'createTable',\n\t'addField',\n\t'modifyField',\n\t'addIndex',\n\t'addForeignKey',\n]\n\ntype SchemaFieldInfo = {\n\tname: string\n\ttype: FieldTypeName\n\tnullable?: boolean\n\tdefault?: string | number | boolean | null\n\tunique?: boolean\n}\n\nfunction adapterSupports(adapter: OrmAdapter, kind: AnyChange['kind']): boolean {\n\tif (kind === 'execute') return true\n\tconst method = `apply${kind[0].toUpperCase()}${kind.slice(1)}` as keyof OrmAdapter\n\treturn typeof (adapter as any)[method] === 'function'\n}\n\nfunction fieldsEqual(target: SchemaFieldInfo, discovered: DiscoveredField): boolean {\n\tif (target.type !== discovered.type) return false\n\tif ((target.nullable ?? false) !== discovered.nullable) return false\n\tif (target.default !== discovered.default) return false\n\tif ((target.unique ?? false) !== (discovered.unique ?? false)) return false\n\treturn true\n}\n\nconst toFieldPayload = (f: SchemaFieldInfo) => ({\n\tname: f.name,\n\ttype: f.type,\n\t...(f.nullable ? { nullable: true as const } : {}),\n})\n\nfunction extractSchemaInfo(schema: AnySchema): {\n\tname: string\n\tpk: { name: string; type: FieldTypeName }\n\tfields: SchemaFieldInfo[]\n} {\n\tconst pkField = schema.pkField\n\tconst pkType = inferFieldType(pkField.pipe)\n\tconst fields: SchemaFieldInfo[] = []\n\tfor (const field of Object.values(schema.fieldDefs)) {\n\t\tconst f = field as any\n\t\tfields.push({\n\t\t\tname: f.name,\n\t\t\ttype: inferFieldType(f.pipe),\n\t\t\tnullable: isNullable(f.pipe),\n\t\t})\n\t}\n\treturn { name: schema.name, pk: { name: pkField.name, type: pkType }, fields }\n}\n\nconst KNOWN_FIELD_TYPES: ReadonlySet<string> = new Set<FieldTypeName>(['string', 'number', 'boolean', 'array', 'object', 'date'])\n\nfunction inferFieldType(pipe: any): FieldTypeName {\n\tif (!pipe) return 'string'\n\tconst schema = typeof pipe.schema === 'function' ? pipe.schema() : undefined\n\tif (schema?.type) {\n\t\tif (KNOWN_FIELD_TYPES.has(schema.type)) return schema.type as FieldTypeName\n\t\treturn 'string'\n\t}\n\tconst std = pipe['~standard']\n\tif (std?.validate) {\n\t\tif (!std.validate(0).issues) return 'number'\n\t\tif (!std.validate('').issues) return 'string'\n\t\tif (!std.validate(true).issues) return 'boolean'\n\t\tif (!std.validate([]).issues) return 'array'\n\t\tif (!std.validate({}).issues) return 'object'\n\t}\n\treturn 'string'\n}\n\nfunction isNullable(pipe: any): boolean {\n\tif (!pipe) return false\n\tconst ctx = typeof pipe.context === 'function' ? pipe.context() : undefined\n\tif (ctx?.optional) return true\n\tconst std = pipe['~standard']\n\tif (std?.validate && !std.validate(undefined).issues) return true\n\treturn false\n}\n\nexport function diffSchemas(\n\tadapter: OrmAdapter,\n\ttarget: ReadonlyArray<AnySchema>,\n\tcurrent: ReadonlyArray<DiscoveredSchema>,\n): ReadonlyArray<AnyChange> {\n\tconst changes: AnyChange[] = []\n\tconst currentByName = new Map(current.map((s) => [s.name, s]))\n\tconst targetByName = new Map(target.map((s) => [s.name, s]))\n\n\tfor (const disc of current) {\n\t\tif (!targetByName.has(disc.name)) {\n\t\t\tfor (const fk of disc.foreignKeys) {\n\t\t\t\tchanges.push({ kind: 'dropForeignKey', table: disc.name, name: fk.name })\n\t\t\t}\n\t\t\tfor (const idx of disc.indexes) {\n\t\t\t\tchanges.push({ kind: 'dropIndex', name: idx.name })\n\t\t\t}\n\t\t\tchanges.push({ kind: 'dropTable', name: disc.name })\n\t\t}\n\t}\n\n\tfor (const schema of target) {\n\t\tconst info = extractSchemaInfo(schema)\n\t\tconst disc = currentByName.get(info.name)\n\n\t\tif (!disc) {\n\t\t\tchanges.push({\n\t\t\t\tkind: 'createTable',\n\t\t\t\tname: info.name,\n\t\t\t\tpk: info.pk,\n\t\t\t\tfields: info.fields.map(toFieldPayload),\n\t\t\t})\n\t\t\tcontinue\n\t\t}\n\n\t\tconst discFieldsByName = new Map(disc.fields.map((f) => [f.name, f]))\n\t\tconst targetFieldsByName = new Map(info.fields.map((f) => [f.name, f]))\n\n\t\tfor (const discField of disc.fields) {\n\t\t\tif (!targetFieldsByName.has(discField.name)) {\n\t\t\t\tchanges.push({ kind: 'dropField', table: info.name, name: discField.name })\n\t\t\t}\n\t\t}\n\n\t\tfor (const tField of info.fields) {\n\t\t\tconst existing = discFieldsByName.get(tField.name)\n\t\t\tif (!existing) {\n\t\t\t\tchanges.push({ kind: 'addField', table: info.name, field: toFieldPayload(tField) })\n\t\t\t} else if (!fieldsEqual(tField, existing)) {\n\t\t\t\tchanges.push({ kind: 'modifyField', table: info.name, name: tField.name, to: toFieldPayload(tField) })\n\t\t\t}\n\t\t}\n\t}\n\n\treturn changes\n\t\t.filter((c) => adapterSupports(adapter, c.kind))\n\t\t.sort((a, b) => EMISSION_ORDER.indexOf(a.kind) - EMISSION_ORDER.indexOf(b.kind))\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { Schema } = await import('../schema')\n\n\tdescribe('diffSchemas', () => {\n\t\tfunction makeAdapter() {\n\t\t\treturn InMemoryAdapter.create({})\n\t\t}\n\n\t\ttest('returns empty array when no changes needed', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'email', type: 'string', nullable: false }],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\texpect(result).toEqual([])\n\t\t})\n\n\t\ttest('fresh DB returns createTable for everything', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.field('age', v.number())\n\t\t\t\t.build()\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], [])\n\t\t\texpect(result).toHaveLength(1)\n\t\t\texpect(result[0]).toEqual({\n\t\t\t\tkind: 'createTable',\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [\n\t\t\t\t\t{ name: 'email', type: 'string' },\n\t\t\t\t\t{ name: 'age', type: 'number' },\n\t\t\t\t],\n\t\t\t})\n\t\t})\n\n\t\ttest('add field detected when target has field absent from current', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.field('age', v.number())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'email', type: 'string', nullable: false }],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\texpect(result).toHaveLength(1)\n\t\t\texpect(result[0]).toEqual({\n\t\t\t\tkind: 'addField',\n\t\t\t\ttable: 'users',\n\t\t\t\tfield: { name: 'age', type: 'number' },\n\t\t\t})\n\t\t})\n\n\t\ttest('drop field detected when current has field absent from target', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [\n\t\t\t\t\t{ name: 'email', type: 'string', nullable: false },\n\t\t\t\t\t{ name: 'age', type: 'number', nullable: false },\n\t\t\t\t],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\texpect(result).toHaveLength(1)\n\t\t\texpect(result[0]).toEqual({\n\t\t\t\tkind: 'dropField',\n\t\t\t\ttable: 'users',\n\t\t\t\tname: 'age',\n\t\t\t})\n\t\t})\n\n\t\ttest('modify field detected when type changes', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('age', v.string())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'age', type: 'number', nullable: false }],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\texpect(result).toHaveLength(1)\n\t\t\texpect(result[0]).toEqual({\n\t\t\t\tkind: 'modifyField',\n\t\t\t\ttable: 'users',\n\t\t\t\tname: 'age',\n\t\t\t\tto: { name: 'age', type: 'string' },\n\t\t\t})\n\t\t})\n\n\t\ttest('drop table detected when current has table absent from target', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'posts',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'title', type: 'string', nullable: false }],\n\t\t\t\tindexes: [{ name: 'posts_title_idx', on: ['title'], unique: false }],\n\t\t\t\tforeignKeys: [{ name: 'posts_author_fk', on: 'authorId', references: { table: 'users', column: 'id' } }],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [], current)\n\t\t\tconst kinds = result.map((c) => c.kind)\n\t\t\texpect(kinds).toContain('dropForeignKey')\n\t\t\texpect(kinds).toContain('dropIndex')\n\t\t\texpect(kinds).toContain('dropTable')\n\t\t})\n\n\t\ttest('rename surfaces as drop+add (NOT auto-rename)', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('fullName', v.string())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'name', type: 'string', nullable: false }],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\texpect(result).toHaveLength(2)\n\t\t\tconst kinds = result.map((c) => c.kind)\n\t\t\texpect(kinds).toContain('dropField')\n\t\t\texpect(kinds).toContain('addField')\n\t\t\texpect(kinds).not.toContain('renameField')\n\t\t})\n\n\t\ttest('emission order respects fixed canonical sequence', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.field('newField', v.number())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [\n\t\t\t\t{\n\t\t\t\t\tname: 'users',\n\t\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\t\tfields: [\n\t\t\t\t\t\t{ name: 'email', type: 'number', nullable: false },\n\t\t\t\t\t\t{ name: 'oldField', type: 'string', nullable: false },\n\t\t\t\t\t],\n\t\t\t\t\tindexes: [],\n\t\t\t\t\tforeignKeys: [],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname: 'posts',\n\t\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\t\tfields: [],\n\t\t\t\t\tindexes: [],\n\t\t\t\t\tforeignKeys: [],\n\t\t\t\t},\n\t\t\t]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\tconst kinds = result.map((c) => c.kind)\n\t\t\tfor (let i = 0; i < kinds.length - 1; i++) {\n\t\t\t\tconst a = EMISSION_ORDER.indexOf(kinds[i])\n\t\t\t\tconst b = EMISSION_ORDER.indexOf(kinds[i + 1])\n\t\t\t\texpect(a).toBeLessThanOrEqual(b)\n\t\t\t}\n\t\t})\n\n\t\ttest('adapter-aware filtering removes unsupported variants', async () => {\n\t\t\tconst { OrmAdapter } = await import('../orm-adapter')\n\t\t\tclass IndexOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\tasync applyAddIndex() {}\n\t\t\t\tasync applyDropIndex() {}\n\t\t\t\tasync loadMigrations() { return [] }\n\t\t\t\tasync recordMigration() {}\n\t\t\t\tasync introspect() { return [] }\n\t\t\t}\n\t\t\tconst adapter = new (IndexOnlyAdapter as any)() as IndexOnlyAdapter\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.build()\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], [])\n\t\t\texpect(result).toEqual([])\n\t\t})\n\n\t\ttest('multiple tables: creates missing and drops extra', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.build()\n\t\t\tconst PostSchema = Schema.from('posts')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('title', v.string())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'email', type: 'string', nullable: false }],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}, {\n\t\t\t\tname: 'comments',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema, PostSchema], current)\n\t\t\tconst kinds = result.map((c) => c.kind)\n\t\t\texpect(kinds).toContain('dropTable')\n\t\t\texpect(kinds).toContain('createTable')\n\t\t\tconst dropTable = result.find((c) => c.kind === 'dropTable') as any\n\t\t\texpect(dropTable.name).toBe('comments')\n\t\t\tconst createTable = result.find((c) => c.kind === 'createTable') as any\n\t\t\texpect(createTable.name).toBe('posts')\n\t\t})\n\n\t\ttest('Mongo-style adapter with only index ops: field-bearing target returns empty diff', async () => {\n\t\t\tconst { OrmAdapter } = await import('../orm-adapter')\n\t\t\tclass MongoStyleAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number', 'object', 'array'] as const\n\t\t\t\tasync applyAddIndex() {}\n\t\t\t\tasync applyDropIndex() {}\n\t\t\t\tasync loadMigrations() { return [] }\n\t\t\t\tasync recordMigration() {}\n\t\t\t}\n\t\t\tconst adapter = new (MongoStyleAdapter as any)() as MongoStyleAdapter\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.field('age', v.number())\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [],\n\t\t\t\tindexes: [{ name: 'users_email_idx', on: ['email'], unique: true }],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\tconst kinds = result.map((c) => c.kind)\n\t\t\texpect(kinds.every((k) => k === 'addIndex' || k === 'dropIndex')).toBe(true)\n\t\t})\n\n\t\ttest('drop index and drop FK emitted when table is dropped', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'posts',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [],\n\t\t\t\tindexes: [{ name: 'posts_title_idx', on: ['title'], unique: false }],\n\t\t\t\tforeignKeys: [{ name: 'posts_author_fk', on: 'authorId', references: { table: 'users', column: 'id' } }],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [], current)\n\t\t\texpect(result).toHaveLength(3)\n\t\t\texpect(result[0].kind).toBe('dropForeignKey')\n\t\t\texpect(result[1].kind).toBe('dropIndex')\n\t\t\texpect(result[2].kind).toBe('dropTable')\n\t\t})\n\n\t\ttest('nullable field mismatch detected as modify', () => {\n\t\t\tconst adapter = makeAdapter()\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('email', v.optional(v.string()), { onCreate: () => undefined })\n\t\t\t\t.build()\n\t\t\tconst current: DiscoveredSchema[] = [{\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'email', type: 'string', nullable: false }],\n\t\t\t\tindexes: [],\n\t\t\t\tforeignKeys: [],\n\t\t\t}]\n\t\t\tconst result = diffSchemas(adapter, [UserSchema], current)\n\t\t\texpect(result).toHaveLength(1)\n\t\t\texpect(result[0].kind).toBe('modifyField')\n\t\t})\n\t})\n}\n","import type { OrmAdapterLike } from '../adapters/base'\nimport type { OrmAdapter } from '../orm-adapter'\nimport { diffSchemas } from './diff'\nimport type { DiscoveredSchema } from './introspection-types'\nimport type { ChangeFor } from './types'\nimport type { Repo } from '../repo/repo'\nimport type { AnySchema } from '../schema'\n\nexport type IntrospectableAdapter<A> = A extends { introspect(): Promise<DiscoveredSchema[]> } ? A : never\n\nexport class MigrationCodegen<A extends OrmAdapterLike<any>> {\n\treadonly #adapter: OrmAdapter & { introspect(): Promise<DiscoveredSchema[]> }\n\treadonly #target: ReadonlyArray<AnySchema>\n\n\tconstructor(\n\t\t_repo: Repo<A>,\n\t\tadapter: OrmAdapter & { introspect(): Promise<DiscoveredSchema[]> },\n\t\ttarget: ReadonlyArray<AnySchema>,\n\t) {\n\t\tthis.#adapter = adapter\n\t\tthis.#target = target\n\t}\n\n\tasync diff(): Promise<ReadonlyArray<ChangeFor<A>> | null> {\n\t\tconst current = await this.#adapter.introspect()\n\t\tconst changes = diffSchemas(this.#adapter, this.#target, current)\n\t\tif (changes.length === 0) return null\n\t\treturn changes as unknown as ReadonlyArray<ChangeFor<A>>\n\t}\n\n\tasync discover(): Promise<ReadonlyArray<DiscoveredSchema>> {\n\t\treturn this.#adapter.introspect()\n\t}\n\n\tstatic from<A extends OrmAdapterLike<any>>(\n\t\trepo: Repo<IntrospectableAdapter<A>>,\n\t\tadapter: A & OrmAdapter & { introspect(): Promise<DiscoveredSchema[]> },\n\t) {\n\t\treturn {\n\t\t\ttarget(schemas: ReadonlyArray<AnySchema>) {\n\t\t\t\treturn {\n\t\t\t\t\tbuild(): MigrationCodegen<A> {\n\t\t\t\t\t\treturn new MigrationCodegen(repo as unknown as Repo<A>, adapter, schemas)\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { Schema } = await import('../schema')\n\tconst { Repo } = await import('../repo/repo')\n\tconst { OrmIntrospectionError } = await import('../errors/introspection')\n\n\tconst UserSchema = Schema.from('users')\n\t\t.pk('id', v.string(), () => `u-${Math.random().toString(36).slice(2)}`)\n\t\t.field('email', v.string())\n\t\t.field('age', v.number())\n\t\t.build()\n\n\tconst PostSchema = Schema.from('posts')\n\t\t.pk('id', v.string(), () => `p-${Math.random().toString(36).slice(2)}`)\n\t\t.field('title', v.string())\n\t\t.field('authorId', v.string())\n\t\t.build()\n\n\tfunction makeEnv() {\n\t\tconst adapter = InMemoryAdapter.create({})\n\t\tconst repo = new Repo({ adapter, resolve: (s) => ({ table: s.name }) })\n\t\treturn { adapter, repo }\n\t}\n\n\tdescribe('MigrationCodegen', () => {\n\t\ttest('diff() returns createTable changes for fresh DB', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst codegen = MigrationCodegen.from(repo, adapter).target([UserSchema]).build()\n\t\t\tconst changes = await codegen.diff()\n\t\t\texpect(changes).not.toBeNull()\n\t\t\texpect(changes).toHaveLength(1)\n\t\t\texpect(changes![0].kind).toBe('createTable')\n\t\t\tconst ct = changes![0] as any\n\t\t\texpect(ct.name).toBe('users')\n\t\t\texpect(ct.pk).toEqual({ name: 'id', type: 'string' })\n\t\t\texpect(ct.fields).toEqual([\n\t\t\t\t{ name: 'email', type: 'string' },\n\t\t\t\t{ name: 'age', type: 'number' },\n\t\t\t])\n\t\t})\n\n\t\ttest('diff() returns null when target matches current', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({\n\t\t\t\tkind: 'createTable',\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [\n\t\t\t\t\t{ name: 'email', type: 'string' },\n\t\t\t\t\t{ name: 'age', type: 'number' },\n\t\t\t\t],\n\t\t\t})\n\t\t\tconst codegen = MigrationCodegen.from(repo, adapter).target([UserSchema]).build()\n\t\t\tconst changes = await codegen.diff()\n\t\t\texpect(changes).toBeNull()\n\t\t})\n\n\t\ttest('discover() returns raw introspection descriptors', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({\n\t\t\t\tkind: 'createTable',\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'email', type: 'string' }],\n\t\t\t})\n\t\t\tawait adapter.applyAddIndex({ kind: 'addIndex', table: 'users', on: ['email'], unique: true })\n\t\t\tconst codegen = MigrationCodegen.from(repo, adapter).target([UserSchema]).build()\n\t\t\tconst discovered = await codegen.discover()\n\t\t\texpect(discovered).toHaveLength(1)\n\t\t\texpect(discovered[0].name).toBe('users')\n\t\t\texpect(discovered[0].pk).toEqual({ name: 'id', type: 'string' })\n\t\t\texpect(discovered[0].fields).toEqual([{ name: 'email', type: 'string', nullable: false }])\n\t\t\texpect(discovered[0].indexes).toEqual([{ name: 'users_email_idx', on: ['email'], unique: true }])\n\t\t})\n\n\t\ttest('end-to-end: seed adapter state, declare target, call diff()', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({\n\t\t\t\tkind: 'createTable',\n\t\t\t\tname: 'users',\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: [{ name: 'email', type: 'string' }],\n\t\t\t})\n\t\t\tconst codegen = MigrationCodegen.from(repo, adapter).target([UserSchema, PostSchema]).build()\n\t\t\tconst changes = await codegen.diff()\n\t\t\texpect(changes).not.toBeNull()\n\t\t\tconst kinds = changes!.map((c) => c.kind)\n\t\t\texpect(kinds).toContain('addField')\n\t\t\texpect(kinds).toContain('createTable')\n\t\t\tconst addField = changes!.find((c) => c.kind === 'addField') as any\n\t\t\texpect(addField.table).toBe('users')\n\t\t\texpect(addField.field.name).toBe('age')\n\t\t\tconst createTable = changes!.find((c) => c.kind === 'createTable') as any\n\t\t\texpect(createTable.name).toBe('posts')\n\t\t})\n\n\t\ttest('compile-time: IntrospectableAdapter is never when adapter lacks introspect', async () => {\n\t\t\tconst { OrmAdapter } = await import('../orm-adapter')\n\t\t\tclass _NoIntrospectAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t}\n\t\t\ttype NoIntrospect = InstanceType<typeof _NoIntrospectAdapter>\n\t\t\texpectTypeOf<IntrospectableAdapter<NoIntrospect>>().toBeNever()\n\t\t})\n\n\t\ttest('OrmIntrospectionError thrown on unrecognized DB type (sentinel)', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\t// Inject a sentinel value with an invalid type into the adapter's table state\n\t\t\tadapter.tables.set('broken', {\n\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\tfields: new Map([['weirdCol', { name: 'weirdCol', type: 'bytea' as any }]]),\n\t\t\t})\n\t\t\tconst codegen = MigrationCodegen.from(repo, adapter).target([UserSchema]).build()\n\t\t\tconst origIntrospect = adapter.introspect.bind(adapter)\n\t\t\tadapter.introspect = async () => {\n\t\t\t\tconst schemas = await origIntrospect()\n\t\t\t\tfor (const s of schemas) {\n\t\t\t\t\tfor (const f of s.fields) {\n\t\t\t\t\t\tconst validTypes = ['string', 'number', 'boolean', 'null', 'object', 'array', 'date']\n\t\t\t\t\t\tif (!validTypes.includes(f.type)) {\n\t\t\t\t\t\t\tthrow new OrmIntrospectionError({ adapter: 'in-memory', table: s.name, cause: `unsupported type '${f.type}' on column '${f.name}'` })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn schemas\n\t\t\t}\n\t\t\tawait expect(codegen.diff()).rejects.toThrow(OrmIntrospectionError)\n\t\t\ttry {\n\t\t\t\tawait codegen.diff()\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err.adapter).toBe('in-memory')\n\t\t\t\texpect(err.table).toBe('broken')\n\t\t\t}\n\t\t})\n\t})\n}\n","import type { OrmAdapterLike } from '../adapters/base'\nimport type { OrmAdapter } from '../orm-adapter'\nimport type { AnyChange } from './types'\nimport type { Repo } from '../repo/repo'\n\nexport async function applyChange(adapter: OrmAdapter, repo: Repo<OrmAdapterLike<any>>, change: AnyChange): Promise<void> {\n\tif (change.kind === 'execute') {\n\t\tawait change.up(repo)\n\t\treturn\n\t}\n\n\tconst methodName = `apply${change.kind[0].toUpperCase()}${change.kind.slice(1)}`\n\tconst method = (adapter as any)[methodName]\n\tif (typeof method !== 'function') {\n\t\tthrow new Error(`Adapter does not support change kind '${change.kind}'`)\n\t}\n\tawait method.call(adapter, change)\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, vi } = import.meta.vitest\n\n\tdescribe('applyChange', () => {\n\t\ttest('routes addIndex to adapter.applyAddIndex', async () => {\n\t\t\tconst applyAddIndex = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyAddIndex } as any\n\t\t\tconst repo = {} as any\n\t\t\tawait applyChange(adapter, repo, { kind: 'addIndex', table: 'users', on: ['email'] })\n\t\t\texpect(applyAddIndex).toHaveBeenCalledWith({ kind: 'addIndex', table: 'users', on: ['email'] })\n\t\t})\n\n\t\ttest('routes execute to change.up(repo)', async () => {\n\t\t\tconst up = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = {} as any\n\t\t\tconst repo = {} as any\n\t\t\tawait applyChange(adapter, repo, { kind: 'execute', up })\n\t\t\texpect(up).toHaveBeenCalledWith(repo)\n\t\t})\n\n\t\ttest('throws when adapter lacks the apply method for a declarative change', async () => {\n\t\t\tconst adapter = {} as any\n\t\t\tconst repo = {} as any\n\t\t\tawait expect(applyChange(adapter, repo, { kind: 'addIndex', table: 't', on: ['a'] }))\n\t\t\t\t.rejects.toThrow(\"Adapter does not support change kind 'addIndex'\")\n\t\t})\n\n\t\ttest('routes createTable to adapter.applyCreateTable', async () => {\n\t\t\tconst applyCreateTable = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyCreateTable } as any\n\t\t\tconst change = { kind: 'createTable' as const, name: 'users', pk: { name: 'id', type: 'string' }, fields: [] }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyCreateTable).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes dropTable to adapter.applyDropTable', async () => {\n\t\t\tconst applyDropTable = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyDropTable } as any\n\t\t\tconst change = { kind: 'dropTable' as const, name: 'users' }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyDropTable).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes addField to adapter.applyAddField', async () => {\n\t\t\tconst applyAddField = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyAddField } as any\n\t\t\tconst change = { kind: 'addField' as const, table: 'users', field: { name: 'age', type: 'number' } }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyAddField).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes dropField to adapter.applyDropField', async () => {\n\t\t\tconst applyDropField = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyDropField } as any\n\t\t\tconst change = { kind: 'dropField' as const, table: 'users', name: 'age' }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyDropField).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes modifyField to adapter.applyModifyField', async () => {\n\t\t\tconst applyModifyField = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyModifyField } as any\n\t\t\tconst change = { kind: 'modifyField' as const, table: 'users', name: 'age', to: { name: 'age', type: 'string' } }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyModifyField).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes renameTable to adapter.applyRenameTable', async () => {\n\t\t\tconst applyRenameTable = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyRenameTable } as any\n\t\t\tconst change = { kind: 'renameTable' as const, from: 'users', to: 'accounts' }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyRenameTable).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes renameField to adapter.applyRenameField', async () => {\n\t\t\tconst applyRenameField = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyRenameField } as any\n\t\t\tconst change = { kind: 'renameField' as const, table: 'users', from: 'name', to: 'fullName' }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyRenameField).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes dropIndex to adapter.applyDropIndex', async () => {\n\t\t\tconst applyDropIndex = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyDropIndex } as any\n\t\t\tconst change = { kind: 'dropIndex' as const, name: 'users_email_idx' }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyDropIndex).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes addForeignKey to adapter.applyAddForeignKey', async () => {\n\t\t\tconst applyAddForeignKey = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyAddForeignKey } as any\n\t\t\tconst change = { kind: 'addForeignKey' as const, table: 'posts', on: 'authorId', references: { table: 'users', column: 'id' } }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyAddForeignKey).toHaveBeenCalledWith(change)\n\t\t})\n\n\t\ttest('routes dropForeignKey to adapter.applyDropForeignKey', async () => {\n\t\t\tconst applyDropForeignKey = vi.fn().mockResolvedValue(undefined)\n\t\t\tconst adapter = { applyDropForeignKey } as any\n\t\t\tconst change = { kind: 'dropForeignKey' as const, table: 'posts', name: 'posts_authorId_fk' }\n\t\t\tawait applyChange(adapter, {} as any, change)\n\t\t\texpect(applyDropForeignKey).toHaveBeenCalledWith(change)\n\t\t})\n\t})\n}\n","import type { AnyMigration } from './types'\nimport { OrmMigrationError } from '../errors/migration'\n\nexport type PendingOpts = { to?: string; steps?: number }\n\nexport function computePending(\n\tdeclared: ReadonlyArray<AnyMigration>,\n\tapplied: ReadonlyArray<{ id: string; appliedAt: number }>,\n\topts?: PendingOpts,\n): { pending: AnyMigration[]; skipped: string[] } {\n\tconst appliedIds = new Set(applied.map((a) => a.id))\n\tconst declaredIds = new Set(declared.map((d) => d.id))\n\n\tconst orphans = applied.filter((a) => !declaredIds.has(a.id)).map((a) => a.id)\n\tif (orphans.length > 0) {\n\t\tthrow new OrmMigrationError({\n\t\t\tid: orphans[0],\n\t\t\tphase: 'load',\n\t\t\tcause: `orphan migrations: [${orphans.join(', ')}]`,\n\t\t})\n\t}\n\n\tconst sorted = [...declared].sort((a, b) => a.id.localeCompare(b.id))\n\n\tconst pending: AnyMigration[] = []\n\tconst skipped: string[] = []\n\tfor (const m of sorted) {\n\t\tif (appliedIds.has(m.id)) {\n\t\t\tskipped.push(m.id)\n\t\t} else {\n\t\t\tpending.push(m)\n\t\t}\n\t}\n\n\tif (opts?.to !== undefined) {\n\t\tconst toIdx = pending.findIndex((m) => m.id === opts.to)\n\t\tif (toIdx === -1) {\n\t\t\tif (appliedIds.has(opts.to)) return { pending: [], skipped }\n\t\t\tthrow new OrmMigrationError({\n\t\t\t\tid: opts.to,\n\t\t\t\tphase: 'load',\n\t\t\t\tcause: `unknown migration id: ${opts.to}`,\n\t\t\t})\n\t\t}\n\t\tpending.splice(toIdx + 1)\n\t}\n\n\tif (opts?.steps !== undefined) {\n\t\tpending.splice(opts.steps)\n\t}\n\n\treturn { pending, skipped }\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { OrmMigrationError } = await import('../errors/migration')\n\n\tdescribe('computePending', () => {\n\t\ttest('returns all as pending when none applied', () => {\n\t\t\tconst declared = [\n\t\t\t\t{ id: '0002-add-index', changes: [] },\n\t\t\t\t{ id: '0001-create-table', changes: [] },\n\t\t\t]\n\t\t\tconst result = computePending(declared, [])\n\t\t\texpect(result.pending.map((m) => m.id)).toEqual(['0001-create-table', '0002-add-index'])\n\t\t\texpect(result.skipped).toEqual([])\n\t\t})\n\n\t\ttest('skips already applied migrations', () => {\n\t\t\tconst declared = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [] },\n\t\t\t\t{ id: '0003', changes: [] },\n\t\t\t]\n\t\t\tconst applied = [{ id: '0001', appliedAt: 1 }]\n\t\t\tconst result = computePending(declared, applied)\n\t\t\texpect(result.pending.map((m) => m.id)).toEqual(['0002', '0003'])\n\t\t\texpect(result.skipped).toEqual(['0001'])\n\t\t})\n\n\t\ttest('throws on orphan migrations', () => {\n\t\t\tconst declared = [{ id: '0001', changes: [] }]\n\t\t\tconst applied = [{ id: '0001', appliedAt: 1 }, { id: 'ghost', appliedAt: 2 }]\n\t\t\texpect(() => computePending(declared, applied)).toThrow(OrmMigrationError)\n\t\t\ttry {\n\t\t\t\tcomputePending(declared, applied)\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err.phase).toBe('load')\n\t\t\t\texpect(err.cause).toContain('ghost')\n\t\t\t}\n\t\t})\n\n\t\ttest('returns empty pending when all applied', () => {\n\t\t\tconst declared = [{ id: '0001', changes: [] }]\n\t\t\tconst applied = [{ id: '0001', appliedAt: 1 }]\n\t\t\tconst result = computePending(declared, applied)\n\t\t\texpect(result.pending).toEqual([])\n\t\t\texpect(result.skipped).toEqual(['0001'])\n\t\t})\n\n\t\ttest('sorts by lex id order', () => {\n\t\t\tconst declared = [\n\t\t\t\t{ id: 'c', changes: [] },\n\t\t\t\t{ id: 'a', changes: [] },\n\t\t\t\t{ id: 'b', changes: [] },\n\t\t\t]\n\t\t\tconst result = computePending(declared, [])\n\t\t\texpect(result.pending.map((m) => m.id)).toEqual(['a', 'b', 'c'])\n\t\t})\n\n\t\ttest('to: returns pending up to and including the named id', () => {\n\t\t\tconst declared = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [] },\n\t\t\t\t{ id: '0003', changes: [] },\n\t\t\t]\n\t\t\tconst result = computePending(declared, [], { to: '0002' })\n\t\t\texpect(result.pending.map((m) => m.id)).toEqual(['0001', '0002'])\n\t\t})\n\n\t\ttest('to: no-op when target id already applied', () => {\n\t\t\tconst declared = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [] },\n\t\t\t\t{ id: '0003', changes: [] },\n\t\t\t]\n\t\t\tconst applied = [{ id: '0001', appliedAt: 1 }, { id: '0002', appliedAt: 2 }]\n\t\t\tconst result = computePending(declared, applied, { to: '0002' })\n\t\t\texpect(result.pending).toEqual([])\n\t\t})\n\n\t\ttest('to: throws on unknown id', () => {\n\t\t\tconst declared = [{ id: '0001', changes: [] }]\n\t\t\texpect(() => computePending(declared, [], { to: 'unknown' })).toThrow(OrmMigrationError)\n\t\t\ttry {\n\t\t\t\tcomputePending(declared, [], { to: 'unknown' })\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err.phase).toBe('load')\n\t\t\t\texpect(err.id).toBe('unknown')\n\t\t\t}\n\t\t})\n\n\t\ttest('steps: returns the first N pending', () => {\n\t\t\tconst declared = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [] },\n\t\t\t\t{ id: '0003', changes: [] },\n\t\t\t]\n\t\t\tconst result = computePending(declared, [], { steps: 1 })\n\t\t\texpect(result.pending.map((m) => m.id)).toEqual(['0001'])\n\t\t})\n\n\t\ttest('steps: returns all pending when N exceeds count', () => {\n\t\t\tconst declared = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [] },\n\t\t\t]\n\t\t\tconst result = computePending(declared, [], { steps: 5 })\n\t\t\texpect(result.pending.map((m) => m.id)).toEqual(['0001', '0002'])\n\t\t})\n\t})\n}\n","import { OrmValidationError, type OrmValidationFailure } from '../errors/validation'\nimport type { OrmAdapter } from '../orm-adapter'\nimport type { AnyChange, AnyMigration } from './types'\n\nfunction isEmpty(s: string | undefined | null): boolean {\n\treturn !s || s.trim() === ''\n}\n\ntype ChangeContext = { adapter: OrmAdapter; migrationId: string; changeIndex: number; failures: OrmValidationFailure[] }\n\nfunction validateFieldSpec(ctx: ChangeContext, fieldSpec: { name: string; type: string }, fieldLabel: string): void {\n\tif (isEmpty(fieldSpec.name)) {\n\t\tctx.failures.push({ migrationId: ctx.migrationId, changeIndex: ctx.changeIndex, field: fieldLabel, cause: `${fieldLabel} name must be non-empty` })\n\t}\n\tif (ctx.adapter.supportedFieldTypes.length > 0 && !ctx.adapter.supportedFieldTypes.includes(fieldSpec.type as any)) {\n\t\tctx.failures.push({ migrationId: ctx.migrationId, changeIndex: ctx.changeIndex, field: fieldLabel, cause: `unsupported field type '${fieldSpec.type}'` })\n\t}\n}\n\nfunction fail(ctx: ChangeContext, field: string | undefined, cause: string): void {\n\tctx.failures.push({ migrationId: ctx.migrationId, changeIndex: ctx.changeIndex, ...(field ? { field } : {}), cause })\n}\n\nfunction validateChange(adapter: OrmAdapter, change: AnyChange, migrationId: string, changeIndex: number, failures: OrmValidationFailure[]): void {\n\tif (change.kind === 'execute') return\n\n\tconst ctx: ChangeContext = { adapter, migrationId, changeIndex, failures }\n\tconst methodName = `apply${change.kind[0].toUpperCase()}${change.kind.slice(1)}`\n\tif (typeof (adapter as any)[methodName] !== 'function') {\n\t\tfail(ctx, undefined, `adapter does not support change kind '${change.kind}'`)\n\t}\n\n\tswitch (change.kind) {\n\t\tcase 'createTable': {\n\t\t\tif (isEmpty(change.name)) fail(ctx, 'name', 'table name must be non-empty')\n\t\t\tif (isEmpty(change.pk.name)) fail(ctx, 'pk.name', 'pk name must be non-empty')\n\t\t\tif (adapter.supportedFieldTypes.length > 0 && !adapter.supportedFieldTypes.includes(change.pk.type as any)) {\n\t\t\t\tfail(ctx, 'pk.type', `unsupported field type '${change.pk.type}'`)\n\t\t\t}\n\t\t\tconst fieldNames = new Set<string>()\n\t\t\tfor (const f of change.fields) {\n\t\t\t\tvalidateFieldSpec(ctx, f, 'field')\n\t\t\t\tif (fieldNames.has(f.name)) fail(ctx, 'fields', `duplicate field name '${f.name}' in createTable`)\n\t\t\t\tfieldNames.add(f.name)\n\t\t\t}\n\t\t\tif (change.pk.name && fieldNames.has(change.pk.name)) {\n\t\t\t\tfail(ctx, 'pk.name', `pk name '${change.pk.name}' collides with a field name`)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tcase 'dropTable': {\n\t\t\tif (isEmpty(change.name)) fail(ctx, 'name', 'table name must be non-empty')\n\t\t\tbreak\n\t\t}\n\t\tcase 'addField': {\n\t\t\tif (isEmpty(change.table)) fail(ctx, 'table', 'table name must be non-empty')\n\t\t\tvalidateFieldSpec(ctx, change.field, 'field')\n\t\t\tbreak\n\t\t}\n\t\tcase 'dropField': {\n\t\t\tif (isEmpty(change.table)) fail(ctx, 'table', 'table name must be non-empty')\n\t\t\tif (isEmpty(change.name)) fail(ctx, 'name', 'field name must be non-empty')\n\t\t\tbreak\n\t\t}\n\t\tcase 'modifyField': {\n\t\t\tif (isEmpty(change.table)) fail(ctx, 'table', 'table name must be non-empty')\n\t\t\tif (isEmpty(change.name)) fail(ctx, 'name', 'field name must be non-empty')\n\t\t\tvalidateFieldSpec(ctx, change.to, 'to')\n\t\t\tbreak\n\t\t}\n\t\tcase 'renameTable': {\n\t\t\tif (isEmpty(change.from)) fail(ctx, 'from', 'table name must be non-empty')\n\t\t\tif (isEmpty(change.to)) fail(ctx, 'to', 'table name must be non-empty')\n\t\t\tbreak\n\t\t}\n\t\tcase 'renameField': {\n\t\t\tif (isEmpty(change.table)) fail(ctx, 'table', 'table name must be non-empty')\n\t\t\tif (isEmpty(change.from)) fail(ctx, 'from', 'field name must be non-empty')\n\t\t\tif (isEmpty(change.to)) fail(ctx, 'to', 'field name must be non-empty')\n\t\t\tbreak\n\t\t}\n\t\tcase 'addIndex': {\n\t\t\tif (isEmpty(change.table)) fail(ctx, 'table', 'table name must be non-empty')\n\t\t\tif (!change.on || change.on.length === 0) fail(ctx, 'on', 'addIndex.on must be non-empty')\n\t\t\tbreak\n\t\t}\n\t\tcase 'dropIndex': {\n\t\t\tif (isEmpty(change.name)) fail(ctx, 'name', 'index name must be non-empty')\n\t\t\tbreak\n\t\t}\n\t\tcase 'addForeignKey': {\n\t\t\tif (isEmpty(change.table)) fail(ctx, 'table', 'table name must be non-empty')\n\t\t\tif (isEmpty(change.on)) fail(ctx, 'on', 'addForeignKey.on must be non-empty')\n\t\t\tif (isEmpty(change.references?.table)) fail(ctx, 'references.table', 'references table must be non-empty')\n\t\t\tif (isEmpty(change.references?.column)) fail(ctx, 'references.column', 'references column must be non-empty')\n\t\t\tbreak\n\t\t}\n\t\tcase 'dropForeignKey': {\n\t\t\tif (isEmpty(change.table)) fail(ctx, 'table', 'table name must be non-empty')\n\t\t\tif (isEmpty(change.name)) fail(ctx, 'name', 'foreign key name must be non-empty')\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nexport function assertNormalisedChanges(adapter: OrmAdapter, migrations: ReadonlyArray<AnyMigration>): void {\n\tconst failures: OrmValidationFailure[] = []\n\n\tconst seenIds = new Set<string>()\n\tfor (const m of migrations) {\n\t\tif (isEmpty(m.id)) {\n\t\t\tfailures.push({ cause: 'migration id must be a non-empty string' })\n\t\t\tcontinue\n\t\t}\n\n\t\tif (seenIds.has(m.id)) {\n\t\t\tfailures.push({ migrationId: m.id, cause: `duplicate migration id '${m.id}'` })\n\t\t}\n\t\tseenIds.add(m.id)\n\n\t\tfor (let i = 0; i < m.changes.length; i++) {\n\t\t\tvalidateChange(adapter, m.changes[i], m.id, i, failures)\n\t\t}\n\t}\n\n\tif (failures.length > 0) {\n\t\tthrow new OrmValidationError('changes', 'migrations', 'build', failures)\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\n\tdescribe('assertNormalisedChanges', () => {\n\t\tconst fullAdapter = {\n\t\t\tsupportedFieldTypes: ['string', 'number', 'boolean'],\n\t\t\tapplyCreateTable: async () => {},\n\t\t\tapplyDropTable: async () => {},\n\t\t\tapplyAddField: async () => {},\n\t\t\tapplyDropField: async () => {},\n\t\t\tapplyModifyField: async () => {},\n\t\t\tapplyRenameTable: async () => {},\n\t\t\tapplyRenameField: async () => {},\n\t\t\tapplyAddIndex: async () => {},\n\t\t\tapplyDropIndex: async () => {},\n\t\t\tapplyAddForeignKey: async () => {},\n\t\t\tapplyDropForeignKey: async () => {},\n\t\t} as any\n\n\t\tconst adapterWithIndex = { supportedFieldTypes: ['string', 'number'], applyAddIndex: async () => {} } as any\n\t\tconst adapterWithout = { supportedFieldTypes: [] } as any\n\n\t\ttest('passes for valid migrations with all variant kinds', () => {\n\t\t\tconst migrations = [\n\t\t\t\t{ id: '0001', changes: [\n\t\t\t\t\t{ kind: 'createTable' as const, name: 'users', pk: { name: 'id', type: 'string' }, fields: [{ name: 'email', type: 'string' }] },\n\t\t\t\t\t{ kind: 'addField' as const, table: 'users', field: { name: 'age', type: 'number' } },\n\t\t\t\t\t{ kind: 'addIndex' as const, table: 'users', on: ['email'] },\n\t\t\t\t\t{ kind: 'addForeignKey' as const, table: 'users', on: 'orgId', references: { table: 'orgs', column: 'id' } },\n\t\t\t\t] },\n\t\t\t]\n\t\t\texpect(() => assertNormalisedChanges(fullAdapter, migrations)).not.toThrow()\n\t\t})\n\n\t\ttest('invariant A: throws on empty migration id', () => {\n\t\t\tconst migrations = [{ id: '', changes: [] }]\n\t\t\texpect(() => assertNormalisedChanges(fullAdapter, migrations)).toThrow(OrmValidationError)\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.kind).toBe('changes')\n\t\t\t\texpect(err.failures).toHaveLength(1)\n\t\t\t\texpect(err.failures[0].cause).toContain('non-empty')\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant B: throws on duplicate migration ids with migrationId field', () => {\n\t\t\tconst migrations = [{ id: 'dup', changes: [] }, { id: 'dup', changes: [] }]\n\t\t\texpect(() => assertNormalisedChanges(fullAdapter, migrations)).toThrow(OrmValidationError)\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.kind).toBe('changes')\n\t\t\t\tconst dup = err.failures.find((f: any) => f.cause.includes('duplicate'))\n\t\t\t\texpect(dup).toBeDefined()\n\t\t\t\texpect(dup.migrationId).toBe('dup')\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant C: throws when field type not in supportedFieldTypes', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'createTable' as const, name: 'users', pk: { name: 'id', type: 'string' }, fields: [{ name: 'data', type: 'bigint' }] },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes(\"unsupported field type 'bigint'\"))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant C: throws when pk type not in supportedFieldTypes', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'createTable' as const, name: 'users', pk: { name: 'id', type: 'uuid' }, fields: [] },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes(\"unsupported field type 'uuid'\"))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant C: throws for addField with unsupported type', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'addField' as const, table: 'users', field: { name: 'x', type: 'bigint' } },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes(\"unsupported field type\"))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant C: throws for modifyField with unsupported type', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'modifyField' as const, table: 'users', name: 'x', to: { name: 'x', type: 'bigint' } },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes(\"unsupported field type\"))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant C: skips field type check when supportedFieldTypes is empty', () => {\n\t\t\tconst adapter = { ...fullAdapter, supportedFieldTypes: [] } as any\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'createTable' as const, name: 'users', pk: { name: 'id', type: 'anything' }, fields: [{ name: 'x', type: 'custom' }] },\n\t\t\t] }]\n\t\t\texpect(() => assertNormalisedChanges(adapter, migrations)).not.toThrow()\n\t\t})\n\n\t\ttest('invariant D: throws on duplicate field names in createTable', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'createTable' as const, name: 'users', pk: { name: 'id', type: 'string' }, fields: [\n\t\t\t\t\t{ name: 'email', type: 'string' },\n\t\t\t\t\t{ name: 'email', type: 'string' },\n\t\t\t\t] },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes(\"duplicate field name 'email'\"))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant D: throws when pk.name collides with a field name', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'createTable' as const, name: 'users', pk: { name: 'email', type: 'string' }, fields: [\n\t\t\t\t\t{ name: 'email', type: 'string' },\n\t\t\t\t] },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes(\"pk name 'email' collides\"))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant E: throws on empty table name in addIndex', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [{ kind: 'addIndex' as const, table: '', on: ['email'] }] }]\n\t\t\texpect(() => assertNormalisedChanges(adapterWithIndex, migrations)).toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('invariant E: throws on empty names in dropTable, dropField, renameTable, renameField, dropIndex, dropForeignKey', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'dropTable' as const, name: '' },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('table name must be non-empty'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant E: throws on empty field name in dropField', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'dropField' as const, table: 'users', name: '' },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('field name must be non-empty'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant E: throws on empty renameTable from/to', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'renameTable' as const, from: '', to: '' },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.filter((f: any) => f.cause.includes('table name must be non-empty'))).toHaveLength(2)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant E: throws on empty renameField from/to/table', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'renameField' as const, table: '', from: '', to: '' },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.length).toBeGreaterThanOrEqual(3)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant E: throws on empty dropIndex name', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'dropIndex' as const, name: '' },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('index name must be non-empty'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant E: throws on empty dropForeignKey table/name', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'dropForeignKey' as const, table: '', name: '' },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.length).toBeGreaterThanOrEqual(2)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant F: throws on empty addIndex.on', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [{ kind: 'addIndex' as const, table: 'users', on: [] as string[] }] }]\n\t\t\texpect(() => assertNormalisedChanges(adapterWithIndex, migrations)).toThrow(OrmValidationError)\n\t\t\ttry { assertNormalisedChanges(adapterWithIndex, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('non-empty'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant F: throws on empty addForeignKey.on', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'addForeignKey' as const, table: 'users', on: '', references: { table: 'orgs', column: 'id' } },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('addForeignKey.on must be non-empty'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant F: throws on empty addForeignKey references', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'addForeignKey' as const, table: 'users', on: 'orgId', references: { table: '', column: '' } },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(fullAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('references table must be non-empty'))).toBe(true)\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('references column must be non-empty'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant G: throws when adapter lacks required apply method', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [{ kind: 'addIndex' as const, table: 'users', on: ['email'] }] }]\n\t\t\texpect(() => assertNormalisedChanges(adapterWithout, migrations)).toThrow(OrmValidationError)\n\t\t\ttry { assertNormalisedChanges(adapterWithout, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes('does not support'))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant G: throws for each unsupported variant', () => {\n\t\t\tconst noOpAdapter = { supportedFieldTypes: ['string'] } as any\n\t\t\tconst migrations = [{ id: '0001', changes: [\n\t\t\t\t{ kind: 'createTable' as const, name: 'users', pk: { name: 'id', type: 'string' }, fields: [] },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(noOpAdapter, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.some((f: any) => f.cause.includes(\"does not support change kind 'createTable'\"))).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('invariant H: allows empty changes array (no-op baseline)', () => {\n\t\t\tconst migrations = [{ id: '0001-baseline', changes: [] }]\n\t\t\texpect(() => assertNormalisedChanges(adapterWithIndex, migrations)).not.toThrow()\n\t\t})\n\n\t\ttest('invariant I: collects all failures into single throw', () => {\n\t\t\tconst migrations = [\n\t\t\t\t{ id: '', changes: [] },\n\t\t\t\t{ id: 'dup', changes: [] },\n\t\t\t\t{ id: 'dup', changes: [{ kind: 'addIndex' as const, table: '', on: [] as string[] }] },\n\t\t\t]\n\t\t\ttry { assertNormalisedChanges(adapterWithIndex, migrations) } catch (err: any) {\n\t\t\t\texpect(err.failures.length).toBeGreaterThanOrEqual(4)\n\t\t\t}\n\t\t})\n\n\t\ttest('failures carry migrationId and changeIndex', () => {\n\t\t\tconst migrations = [{ id: 'mig-1', changes: [\n\t\t\t\t{ kind: 'addIndex' as const, table: '', on: [] as string[] },\n\t\t\t] }]\n\t\t\ttry { assertNormalisedChanges(adapterWithIndex, migrations) } catch (err: any) {\n\t\t\t\tfor (const f of err.failures) {\n\t\t\t\t\texpect(f.migrationId).toBe('mig-1')\n\t\t\t\t\texpect(f.changeIndex).toBe(0)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\ttest('passes for execute changes without adapter check', () => {\n\t\t\tconst migrations = [{ id: '0001', changes: [{ kind: 'execute' as const, up: async () => {} }] }]\n\t\t\texpect(() => assertNormalisedChanges(adapterWithout, migrations)).not.toThrow()\n\t\t})\n\t})\n}\n","import type { OrmAdapterLike } from '../adapters/base'\nimport { OrmMigrationError } from '../errors/migration'\nimport type { OrmAdapter } from '../orm-adapter'\nimport { applyChange } from './apply'\nimport { computePending, type PendingOpts } from './pending'\nimport type { AnyChange, AnyMigration, Migration } from './types'\nimport { assertNormalisedChanges } from './validate'\nimport type { Repo } from '../repo/repo'\n\ntype RunResult = { ran: string[]; skipped: string[] }\nexport type StatusEntry = { id: string; applied: boolean; appliedAt?: number }\n\ntype HasAcquireMigrationLock<A> =\n\t'acquireMigrationLock' extends keyof A\n\t\t? A['acquireMigrationLock'] extends (...args: any) => any ? true : false\n\t\t: false\n\ntype WithoutLockStep<A extends OrmAdapterLike<any>> = HasAcquireMigrationLock<A> extends true\n\t? {}\n\t: { withoutLock(): { build(): Migrator<A> } }\n\nexport class Migrator<A extends OrmAdapterLike<any>> {\n\treadonly #repo: Repo<A>\n\treadonly #adapter: OrmAdapter\n\treadonly #migrations: ReadonlyArray<Migration<A>>\n\treadonly #withoutLock: boolean\n\n\tconstructor(repo: Repo<A>, adapter: OrmAdapter, migrations: ReadonlyArray<Migration<A>>, withoutLock = false) {\n\t\tthis.#repo = repo\n\t\tthis.#adapter = adapter\n\t\tthis.#migrations = migrations\n\t\tthis.#withoutLock = withoutLock\n\t}\n\n\t#requireMethod(name: 'loadMigrations' | 'recordMigration'): void {\n\t\tif (typeof this.#adapter[name] !== 'function') {\n\t\t\tconst phase = name === 'loadMigrations' ? 'load' : 'record' as const\n\t\t\tthrow new OrmMigrationError({ id: '', phase, cause: `adapter does not implement ${name}` })\n\t\t}\n\t}\n\n\tasync up(opts?: PendingOpts): Promise<RunResult> {\n\t\tthis.#requireMethod('loadMigrations')\n\t\tthis.#requireMethod('recordMigration')\n\n\t\tconst useLock = !this.#withoutLock && typeof this.#adapter.acquireMigrationLock === 'function'\n\t\tif (useLock) {\n\t\t\ttry {\n\t\t\t\treturn await this.#adapter.acquireMigrationLock!(() => this.#runPending(opts))\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof OrmMigrationError) throw err\n\t\t\t\tthrow new OrmMigrationError({ id: '', phase: 'lock', cause: err })\n\t\t\t}\n\t\t}\n\t\treturn this.#runPending(opts)\n\t}\n\n\tasync status(): Promise<StatusEntry[]> {\n\t\tthis.#requireMethod('loadMigrations')\n\t\tconst applied = await this.#adapter.loadMigrations!()\n\t\tconst appliedMap = new Map(applied.map((a) => [a.id, a.appliedAt]))\n\t\tconst sorted = [...this.#migrations].sort((a, b) => a.id.localeCompare(b.id))\n\t\treturn sorted.map((m) => {\n\t\t\tconst appliedAt = appliedMap.get(m.id)\n\t\t\tif (appliedAt !== undefined) return { id: m.id, applied: true as const, appliedAt }\n\t\t\treturn { id: m.id, applied: false as const }\n\t\t})\n\t}\n\n\tasync dry(opts?: PendingOpts): Promise<{ would: string[] }> {\n\t\tthis.#requireMethod('loadMigrations')\n\t\tconst applied = await this.#adapter.loadMigrations!()\n\t\tconst { pending } = computePending(this.#migrations as unknown as ReadonlyArray<AnyMigration>, applied, opts)\n\t\treturn { would: pending.map((m) => m.id) }\n\t}\n\n\tasync #runPending(opts?: PendingOpts): Promise<RunResult> {\n\t\tlet applied: { id: string; appliedAt: number }[]\n\t\ttry {\n\t\t\tapplied = await this.#adapter.loadMigrations!()\n\t\t} catch (err) {\n\t\t\tthrow new OrmMigrationError({ id: '', phase: 'load', cause: err })\n\t\t}\n\n\t\tconst { pending, skipped } = computePending(this.#migrations as unknown as ReadonlyArray<AnyMigration>, applied, opts)\n\t\tconst ran: string[] = []\n\n\t\tfor (const m of pending) {\n\t\t\tconst execute = async () => {\n\t\t\t\tfor (const c of m.changes) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait applyChange(this.#adapter, this.#repo as unknown as Repo<OrmAdapterLike<any>>, c as AnyChange)\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tthrow new OrmMigrationError({ id: m.id, phase: 'user', cause: err })\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#adapter.recordMigration!(m.id, Date.now())\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthrow new OrmMigrationError({ id: m.id, phase: 'record', cause: err })\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst shouldWrapInSession = m.tx !== false && typeof this.#adapter.session === 'function'\n\t\t\tif (shouldWrapInSession) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.#repo.session(execute)\n\t\t\t\t} catch (err) {\n\t\t\t\t\tif (err instanceof OrmMigrationError) throw err\n\t\t\t\t\tthrow new OrmMigrationError({ id: m.id, phase: 'session', cause: err })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tawait execute()\n\t\t\t}\n\n\t\t\tran.push(m.id)\n\t\t}\n\n\t\treturn { ran, skipped }\n\t}\n\n\tstatic from<A extends OrmAdapterLike<any>>(repo: Repo<A>, adapter: A & OrmAdapter) {\n\t\treturn {\n\t\t\tmigrations(migrations: ReadonlyArray<Migration<A>>) {\n\t\t\t\treturn {\n\t\t\t\t\tbuild(): Migrator<A> {\n\t\t\t\t\t\tassertNormalisedChanges(adapter, migrations as any)\n\t\t\t\t\t\treturn new Migrator(repo, adapter, migrations)\n\t\t\t\t\t},\n\t\t\t\t\twithoutLock() {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tbuild(): Migrator<A> {\n\t\t\t\t\t\t\t\tassertNormalisedChanges(adapter, migrations as any)\n\t\t\t\t\t\t\t\treturn new Migrator(repo, adapter, migrations, true)\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t} as { build(): Migrator<A> } & WithoutLockStep<A>\n\t\t\t},\n\t\t}\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { Schema } = await import('../schema')\n\tconst { Repo } = await import('../repo/repo')\n\tconst { OrmValidationError } = await import('../errors/validation')\n\tconst { OrmMigrationError } = await import('../errors/migration')\n\n\tconst UserSchema = Schema.from('users')\n\t\t.pk('id', v.string(), () => `u-${Math.random().toString(36).slice(2)}`)\n\t\t.field('email', v.string())\n\t\t.build()\n\n\tfunction makeEnv() {\n\t\tconst adapter = InMemoryAdapter.create({})\n\t\tconst repo = new Repo({ adapter, resolve: (s) => ({ table: s.name }) })\n\t\treturn { adapter, repo }\n\t}\n\n\tdescribe('Migrator', () => {\n\t\ttest('addIndex migration applies and is recorded', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-add-email-idx',\n\t\t\t\tchanges: [{ kind: 'addIndex', table: 'users', on: ['email'], unique: true }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tconst result = await migrator.up()\n\t\t\texpect(result.ran).toEqual(['0001-add-email-idx'])\n\t\t\texpect(result.skipped).toEqual([])\n\n\t\t\texpect(adapter.indexes.has('users_email_idx')).toBe(true)\n\t\t\tconst idx = adapter.indexes.get('users_email_idx')!\n\t\t\texpect(idx.table).toBe('users')\n\t\t\texpect(idx.on).toEqual(['email'])\n\t\t\texpect(idx.unique).toBe(true)\n\n\t\t\tconst recorded = await adapter.loadMigrations()\n\t\t\texpect(recorded).toHaveLength(1)\n\t\t\texpect(recorded[0].id).toBe('0001-add-email-idx')\n\t\t})\n\n\t\ttest('execute migration runs user code with the same Repo', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-seed-user',\n\t\t\t\tchanges: [{\n\t\t\t\t\tkind: 'execute',\n\t\t\t\t\tup: async (r) => {\n\t\t\t\t\t\tawait r.on(UserSchema).one().create({ id: 'seed-1', email: 'test@example.com' })\n\t\t\t\t\t},\n\t\t\t\t}],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\n\t\t\tconst row = await repo.on(UserSchema).one().id('seed-1').find()\n\t\t\texpect(row).not.toBeNull()\n\t\t\texpect(row!.email).toBe('test@example.com')\n\t\t})\n\n\t\ttest('duplicate id throws OrmValidationError at build()', () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\texpect(() =>\n\t\t\t\tMigrator.from(repo, adapter)\n\t\t\t\t\t.migrations([\n\t\t\t\t\t\t{ id: 'dup', changes: [] },\n\t\t\t\t\t\t{ id: 'dup', changes: [] },\n\t\t\t\t\t])\n\t\t\t\t\t.build(),\n\t\t\t).toThrow(OrmValidationError)\n\n\t\t\ttry {\n\t\t\t\tMigrator.from(repo, adapter)\n\t\t\t\t\t.migrations([\n\t\t\t\t\t\t{ id: 'dup', changes: [] },\n\t\t\t\t\t\t{ id: 'dup', changes: [] },\n\t\t\t\t\t])\n\t\t\t\t\t.build()\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err.kind).toBe('changes')\n\t\t\t}\n\t\t})\n\n\t\ttest('up() skips already-applied migrations', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m1: Migration<typeof adapter> = { id: '0001', changes: [] }\n\t\t\tconst m2: Migration<typeof adapter> = {\n\t\t\t\tid: '0002',\n\t\t\t\tchanges: [{ kind: 'addIndex', table: 'users', on: ['email'] }],\n\t\t\t}\n\n\t\t\tawait adapter.recordMigration('0001', Date.now())\n\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m1, m2]).build()\n\t\t\tconst result = await migrator.up()\n\t\t\texpect(result.ran).toEqual(['0002'])\n\t\t\texpect(result.skipped).toEqual(['0001'])\n\t\t})\n\n\t\ttest('up() returns empty ran when all applied', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.recordMigration('0001', Date.now())\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([{ id: '0001', changes: [] }]).build()\n\t\t\tconst result = await migrator.up()\n\t\t\texpect(result.ran).toEqual([])\n\t\t\texpect(result.skipped).toEqual(['0001'])\n\t\t})\n\n\t\ttest('failed migration rolls back via session and throws OrmMigrationError', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m1: Migration<typeof adapter> = {\n\t\t\t\tid: '0001',\n\t\t\t\tchanges: [{ kind: 'addIndex', table: 'users', on: ['email'] }],\n\t\t\t}\n\t\t\tconst m2: Migration<typeof adapter> = {\n\t\t\t\tid: '0002',\n\t\t\t\tchanges: [{\n\t\t\t\t\tkind: 'execute',\n\t\t\t\t\tup: async () => { throw new Error('boom') },\n\t\t\t\t}],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m1, m2]).build()\n\t\t\tawait expect(migrator.up()).rejects.toThrow(OrmMigrationError)\n\n\t\t\tconst recorded = await adapter.loadMigrations()\n\t\t\texpect(recorded.map((r) => r.id)).toEqual(['0001'])\n\t\t})\n\n\t\ttest('multiple changes in one migration all apply', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-multi',\n\t\t\t\tchanges: [\n\t\t\t\t\t{ kind: 'addIndex', table: 'users', on: ['email'], unique: true },\n\t\t\t\t\t{ kind: 'addIndex', table: 'users', on: ['id', 'email'], name: 'users_compound_idx' },\n\t\t\t\t],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\n\t\t\texpect(adapter.indexes.size).toBe(2)\n\t\t\texpect(adapter.indexes.has('users_email_idx')).toBe(true)\n\t\t\texpect(adapter.indexes.has('users_compound_idx')).toBe(true)\n\t\t})\n\n\t\ttest('lex-sorts migrations by id before running', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst order: string[] = []\n\t\t\tconst m1: Migration<typeof adapter> = {\n\t\t\t\tid: '0002',\n\t\t\t\tchanges: [{ kind: 'execute', up: async () => { order.push('0002') } }],\n\t\t\t}\n\t\t\tconst m2: Migration<typeof adapter> = {\n\t\t\t\tid: '0001',\n\t\t\t\tchanges: [{ kind: 'execute', up: async () => { order.push('0001') } }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m1, m2]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(order).toEqual(['0001', '0002'])\n\t\t})\n\n\t\ttest('createTable migration creates table metadata', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-create-users',\n\t\t\t\tchanges: [{\n\t\t\t\t\tkind: 'createTable',\n\t\t\t\t\tname: 'users',\n\t\t\t\t\tpk: { name: 'id', type: 'string' },\n\t\t\t\t\tfields: [\n\t\t\t\t\t\t{ name: 'email', type: 'string', unique: true },\n\t\t\t\t\t\t{ name: 'age', type: 'number', nullable: true },\n\t\t\t\t\t],\n\t\t\t\t}],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\n\t\t\texpect(adapter.tables.has('users')).toBe(true)\n\t\t\tconst table = adapter.tables.get('users')!\n\t\t\texpect(table.pk).toEqual({ name: 'id', type: 'string' })\n\t\t\texpect(table.fields.size).toBe(2)\n\t\t\texpect(table.fields.get('email')).toEqual({ name: 'email', type: 'string', unique: true })\n\t\t\texpect(table.fields.get('age')).toEqual({ name: 'age', type: 'number', nullable: true })\n\t\t})\n\n\t\ttest('dropTable migration removes table metadata', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({ kind: 'createTable', name: 'users', pk: { name: 'id', type: 'string' }, fields: [] })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-drop-users',\n\t\t\t\tchanges: [{ kind: 'dropTable', name: 'users' }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.tables.has('users')).toBe(false)\n\t\t})\n\n\t\ttest('addField migration adds field to existing table', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({ kind: 'createTable', name: 'users', pk: { name: 'id', type: 'string' }, fields: [] })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-add-email',\n\t\t\t\tchanges: [{ kind: 'addField', table: 'users', field: { name: 'email', type: 'string' } }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.tables.get('users')!.fields.has('email')).toBe(true)\n\t\t})\n\n\t\ttest('dropField migration removes field from table', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({ kind: 'createTable', name: 'users', pk: { name: 'id', type: 'string' }, fields: [{ name: 'email', type: 'string' }] })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-drop-email',\n\t\t\t\tchanges: [{ kind: 'dropField', table: 'users', name: 'email' }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.tables.get('users')!.fields.has('email')).toBe(false)\n\t\t})\n\n\t\ttest('modifyField migration updates field spec', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({ kind: 'createTable', name: 'users', pk: { name: 'id', type: 'string' }, fields: [{ name: 'age', type: 'string' }] })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-modify-age',\n\t\t\t\tchanges: [{ kind: 'modifyField', table: 'users', name: 'age', to: { name: 'age', type: 'number', nullable: true } }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.tables.get('users')!.fields.get('age')).toEqual({ name: 'age', type: 'number', nullable: true })\n\t\t})\n\n\t\ttest('renameTable migration renames the table key', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({ kind: 'createTable', name: 'users', pk: { name: 'id', type: 'string' }, fields: [] })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-rename-users',\n\t\t\t\tchanges: [{ kind: 'renameTable', from: 'users', to: 'accounts' }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.tables.has('users')).toBe(false)\n\t\t\texpect(adapter.tables.has('accounts')).toBe(true)\n\t\t})\n\n\t\ttest('renameField migration renames a field', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyCreateTable({ kind: 'createTable', name: 'users', pk: { name: 'id', type: 'string' }, fields: [{ name: 'name', type: 'string' }] })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-rename-name',\n\t\t\t\tchanges: [{ kind: 'renameField', table: 'users', from: 'name', to: 'fullName' }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.tables.get('users')!.fields.has('name')).toBe(false)\n\t\t\texpect(adapter.tables.get('users')!.fields.has('fullName')).toBe(true)\n\t\t\texpect(adapter.tables.get('users')!.fields.get('fullName')!.name).toBe('fullName')\n\t\t})\n\n\t\ttest('dropIndex migration removes an index', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyAddIndex({ kind: 'addIndex', table: 'users', on: ['email'], name: 'users_email_idx' })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-drop-idx',\n\t\t\t\tchanges: [{ kind: 'dropIndex', name: 'users_email_idx' }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.indexes.has('users_email_idx')).toBe(false)\n\t\t})\n\n\t\ttest('addForeignKey migration creates a FK entry', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-add-fk',\n\t\t\t\tchanges: [{ kind: 'addForeignKey', table: 'posts', on: 'authorId', references: { table: 'users', column: 'id' } }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.foreignKeys.has('posts_authorId_fk')).toBe(true)\n\t\t\tconst fk = adapter.foreignKeys.get('posts_authorId_fk')!\n\t\t\texpect(fk.table).toBe('posts')\n\t\t\texpect(fk.on).toBe('authorId')\n\t\t\texpect(fk.references).toEqual({ table: 'users', column: 'id' })\n\t\t})\n\n\t\ttest('dropForeignKey migration removes a FK entry', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.applyAddForeignKey({ kind: 'addForeignKey', table: 'posts', on: 'authorId', references: { table: 'users', column: 'id' }, name: 'posts_authorId_fk' })\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-drop-fk',\n\t\t\t\tchanges: [{ kind: 'dropForeignKey', table: 'posts', name: 'posts_authorId_fk' }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait migrator.up()\n\t\t\texpect(adapter.foreignKeys.has('posts_authorId_fk')).toBe(false)\n\t\t})\n\n\t\ttest('full lifecycle: create table, add fields, indexes, FKs, then rename and drop', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{\n\t\t\t\t\tid: '0001-create',\n\t\t\t\t\tchanges: [\n\t\t\t\t\t\t{ kind: 'createTable', name: 'users', pk: { name: 'id', type: 'string' }, fields: [{ name: 'email', type: 'string' }] },\n\t\t\t\t\t\t{ kind: 'createTable', name: 'posts', pk: { name: 'id', type: 'string' }, fields: [{ name: 'title', type: 'string' }, { name: 'authorId', type: 'string' }] },\n\t\t\t\t\t\t{ kind: 'addIndex', table: 'users', on: ['email'], unique: true },\n\t\t\t\t\t\t{ kind: 'addForeignKey', table: 'posts', on: 'authorId', references: { table: 'users', column: 'id' } },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: '0002-evolve',\n\t\t\t\t\tchanges: [\n\t\t\t\t\t\t{ kind: 'addField', table: 'users', field: { name: 'age', type: 'number', nullable: true } },\n\t\t\t\t\t\t{ kind: 'renameField', table: 'users', from: 'email', to: 'emailAddress' },\n\t\t\t\t\t\t{ kind: 'renameTable', from: 'posts', to: 'articles' },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: '0003-cleanup',\n\t\t\t\t\tchanges: [\n\t\t\t\t\t\t{ kind: 'dropField', table: 'users', name: 'age' },\n\t\t\t\t\t\t{ kind: 'dropForeignKey', table: 'posts', name: 'posts_authorId_fk' },\n\t\t\t\t\t\t{ kind: 'dropIndex', name: 'users_email_idx' },\n\t\t\t\t\t\t{ kind: 'dropTable', name: 'articles' },\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tconst result = await migrator.up()\n\t\t\texpect(result.ran).toEqual(['0001-create', '0002-evolve', '0003-cleanup'])\n\n\t\t\texpect(adapter.tables.has('users')).toBe(true)\n\t\t\texpect(adapter.tables.get('users')!.fields.has('emailAddress')).toBe(true)\n\t\t\texpect(adapter.tables.get('users')!.fields.has('age')).toBe(false)\n\t\t\texpect(adapter.tables.has('articles')).toBe(false)\n\t\t\texpect(adapter.indexes.has('users_email_idx')).toBe(false)\n\t\t\texpect(adapter.foreignKeys.has('posts_authorId_fk')).toBe(false)\n\t\t})\n\n\t\ttest('tx:true migration that throws rolls back both user effects and tracker row', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-will-fail',\n\t\t\t\tchanges: [\n\t\t\t\t\t{ kind: 'addIndex', table: 'users', on: ['email'] },\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: 'execute',\n\t\t\t\t\t\tup: async (r) => {\n\t\t\t\t\t\t\tawait r.on(UserSchema).one().create({ id: 'partial', email: 'fail@test.com' })\n\t\t\t\t\t\t\tthrow new Error('mid-migration boom')\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait expect(migrator.up()).rejects.toThrow(OrmMigrationError)\n\n\t\t\tconst recorded = await adapter.loadMigrations()\n\t\t\texpect(recorded).toHaveLength(0)\n\t\t\texpect(adapter.indexes.size).toBe(0)\n\t\t\tconst row = await repo.on(UserSchema).one().id('partial').find()\n\t\t\texpect(row).toBeNull()\n\t\t})\n\n\t\ttest('tx:false migration that throws leaves partial state durable', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-no-tx',\n\t\t\t\ttx: false,\n\t\t\t\tchanges: [\n\t\t\t\t\t{ kind: 'addIndex', table: 'users', on: ['email'] },\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: 'execute',\n\t\t\t\t\t\tup: async (r) => {\n\t\t\t\t\t\t\tawait r.on(UserSchema).one().create({ id: 'durable', email: 'stay@test.com' })\n\t\t\t\t\t\t\tthrow new Error('partial failure')\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait expect(migrator.up()).rejects.toThrow(OrmMigrationError)\n\n\t\t\tconst recorded = await adapter.loadMigrations()\n\t\t\texpect(recorded).toHaveLength(0)\n\t\t\texpect(adapter.indexes.has('users_email_idx')).toBe(true)\n\t\t\tconst row = await repo.on(UserSchema).one().id('durable').find()\n\t\t\texpect(row).not.toBeNull()\n\t\t\texpect(row!.email).toBe('stay@test.com')\n\t\t})\n\n\t\ttest('atomic-across-migrations: outer repo.session() wraps all in one tx', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m1: Migration<typeof adapter> = {\n\t\t\t\tid: '0001',\n\t\t\t\tchanges: [{ kind: 'addIndex', table: 'users', on: ['email'] }],\n\t\t\t}\n\t\t\tconst m2: Migration<typeof adapter> = {\n\t\t\t\tid: '0002',\n\t\t\t\tchanges: [{\n\t\t\t\t\tkind: 'execute',\n\t\t\t\t\tup: async () => { throw new Error('fail in m2') },\n\t\t\t\t}],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m1, m2]).build()\n\t\t\tawait expect(\n\t\t\t\trepo.session(() => migrator.up()),\n\t\t\t).rejects.toThrow()\n\n\t\t\tconst recorded = await adapter.loadMigrations()\n\t\t\texpect(recorded).toHaveLength(0)\n\t\t\texpect(adapter.indexes.size).toBe(0)\n\t\t})\n\n\t\ttest('simultaneous up() calls serialize via acquireMigrationLock', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst order: string[] = []\n\t\t\tlet resolveGate!: () => void\n\t\t\tconst gate = new Promise<void>((r) => { resolveGate = r })\n\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{\n\t\t\t\t\tid: '0001',\n\t\t\t\t\tchanges: [{\n\t\t\t\t\t\tkind: 'execute',\n\t\t\t\t\t\tup: async () => {\n\t\t\t\t\t\t\torder.push('0001-start')\n\t\t\t\t\t\t\tawait gate\n\t\t\t\t\t\t\torder.push('0001-end')\n\t\t\t\t\t\t},\n\t\t\t\t\t}],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tid: '0002',\n\t\t\t\t\tchanges: [{\n\t\t\t\t\t\tkind: 'execute',\n\t\t\t\t\t\tup: async () => { order.push('0002') },\n\t\t\t\t\t}],\n\t\t\t\t},\n\t\t\t]\n\n\t\t\tconst migrator1 = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tconst migrator2 = Migrator.from(repo, adapter).migrations(migrations).build()\n\n\t\t\tconst p1 = migrator1.up()\n\t\t\tconst p2 = migrator2.up()\n\n\t\t\tawait new Promise((r) => setTimeout(r, 10))\n\t\t\texpect(order).toEqual(['0001-start'])\n\n\t\t\tresolveGate()\n\t\t\tawait Promise.all([p1, p2])\n\n\t\t\texpect(order).toEqual(['0001-start', '0001-end', '0002'])\n\t\t})\n\n\t\ttest('tx:false retries cleanly from scratch after failure', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tlet callCount = 0\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001-retry',\n\t\t\t\ttx: false,\n\t\t\t\tchanges: [{\n\t\t\t\t\tkind: 'execute',\n\t\t\t\t\tup: async (r) => {\n\t\t\t\t\t\tcallCount++\n\t\t\t\t\t\tif (callCount === 1) {\n\t\t\t\t\t\t\tawait r.on(UserSchema).one().create({ id: 'partial', email: 'p@test.com' })\n\t\t\t\t\t\t\tthrow new Error('first attempt fails')\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait r.on(UserSchema).one().create({ id: 'complete', email: 'c@test.com' })\n\t\t\t\t\t},\n\t\t\t\t}],\n\t\t\t}\n\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tawait expect(migrator.up()).rejects.toThrow(OrmMigrationError)\n\t\t\texpect(callCount).toBe(1)\n\n\t\t\tconst migrator2 = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\tconst result = await migrator2.up()\n\t\t\texpect(result.ran).toEqual(['0001-retry'])\n\t\t\texpect(callCount).toBe(2)\n\t\t})\n\n\t\ttest('type-level: withoutLock() rejected when adapter declares acquireMigrationLock', () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst step = Migrator.from(repo, adapter).migrations([])\n\t\t\texpectTypeOf(step).toHaveProperty('build')\n\t\t\texpectTypeOf(step).not.toHaveProperty('withoutLock')\n\t\t})\n\n\t\ttest('type-level: withoutLock() accepted when adapter lacks acquireMigrationLock', async () => {\n\t\t\tconst { OrmAdapter } = await import('../orm-adapter')\n\t\t\tclass NoLockAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync loadMigrations() { return [] }\n\t\t\t\tasync recordMigration() {}\n\t\t\t\tasync session<T>(fn: () => Promise<T>): Promise<T> { return fn() }\n\t\t\t}\n\t\t\tconst nlAdapter = new (NoLockAdapter as any)() as NoLockAdapter\n\t\t\tconst nlRepo = new Repo({ adapter: nlAdapter, resolve: (s) => ({ table: s.name }) })\n\t\t\tconst step = Migrator.from(nlRepo, nlAdapter).migrations([])\n\t\t\texpectTypeOf(step).toHaveProperty('build')\n\t\t\texpectTypeOf(step).toHaveProperty('withoutLock')\n\t\t})\n\n\t\ttest('up({ to }) runs only migrations up to and including the target', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst order: string[] = []\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: 'm-001', changes: [{ kind: 'execute', up: async () => { order.push('m-001') } }] },\n\t\t\t\t{ id: 'm-002', changes: [{ kind: 'execute', up: async () => { order.push('m-002') } }] },\n\t\t\t\t{ id: 'm-003', changes: [{ kind: 'execute', up: async () => { order.push('m-003') } }] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tconst result = await migrator.up({ to: 'm-002' })\n\t\t\texpect(result.ran).toEqual(['m-001', 'm-002'])\n\t\t\texpect(order).toEqual(['m-001', 'm-002'])\n\t\t})\n\n\t\ttest('up({ to }) is a no-op when target already applied', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.recordMigration('m-001', Date.now())\n\t\t\tawait adapter.recordMigration('m-002', Date.now())\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: 'm-001', changes: [] },\n\t\t\t\t{ id: 'm-002', changes: [] },\n\t\t\t\t{ id: 'm-003', changes: [] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tconst result = await migrator.up({ to: 'm-002' })\n\t\t\texpect(result.ran).toEqual([])\n\t\t})\n\n\t\ttest('up({ to: unknown }) throws OrmMigrationError before any side effect', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tlet executed = false\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: 'm-001', changes: [{ kind: 'execute', up: async () => { executed = true } }] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tawait expect(migrator.up({ to: 'unknown' })).rejects.toThrow(OrmMigrationError)\n\t\t\texpect(executed).toBe(false)\n\t\t\ttry {\n\t\t\t\tawait Migrator.from(repo, adapter).migrations(migrations).build().up({ to: 'unknown' })\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err.phase).toBe('load')\n\t\t\t}\n\t\t})\n\n\t\ttest('up({ steps: 1 }) runs exactly 1 pending migration', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: '0001', changes: [{ kind: 'addIndex', table: 'users', on: ['email'] }] },\n\t\t\t\t{ id: '0002', changes: [{ kind: 'addIndex', table: 'users', on: ['id'] }] },\n\t\t\t\t{ id: '0003', changes: [{ kind: 'addIndex', table: 'users', on: ['email', 'id'] }] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tconst result = await migrator.up({ steps: 1 })\n\t\t\texpect(result.ran).toEqual(['0001'])\n\t\t\texpect(adapter.indexes.size).toBe(1)\n\t\t})\n\n\t\ttest('status() returns sorted entries with correct applied/pending flags', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst now = Date.now()\n\t\t\tawait adapter.recordMigration('0002', now)\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: '0003', changes: [] },\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tconst entries = await migrator.status()\n\t\t\texpect(entries).toEqual([\n\t\t\t\t{ id: '0001', applied: false },\n\t\t\t\t{ id: '0002', applied: true, appliedAt: now },\n\t\t\t\t{ id: '0003', applied: false },\n\t\t\t])\n\t\t})\n\n\t\ttest('dry() returns the same plan up() would execute, without executing', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.recordMigration('0001', Date.now())\n\t\t\tlet executed = false\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [{ kind: 'execute', up: async () => { executed = true } }] },\n\t\t\t\t{ id: '0003', changes: [] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\tconst plan = await migrator.dry()\n\t\t\texpect(plan).toEqual({ would: ['0002', '0003'] })\n\t\t\texpect(executed).toBe(false)\n\t\t})\n\n\t\ttest('dry(opts) respects to/steps', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t\t{ id: '0002', changes: [] },\n\t\t\t\t{ id: '0003', changes: [] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\texpect(await migrator.dry({ to: '0002' })).toEqual({ would: ['0001', '0002'] })\n\t\t\texpect(await migrator.dry({ steps: 1 })).toEqual({ would: ['0001'] })\n\t\t})\n\n\t\ttest('orphan migration in tracker throws OrmMigrationError with phase load', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tawait adapter.recordMigration('ghost-001', Date.now())\n\t\t\tconst migrations: Migration<typeof adapter>[] = [\n\t\t\t\t{ id: '0001', changes: [] },\n\t\t\t]\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations(migrations).build()\n\t\t\ttry {\n\t\t\t\tawait migrator.up()\n\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err).toBeInstanceOf(OrmMigrationError)\n\t\t\t\texpect(err.phase).toBe('load')\n\t\t\t\texpect(err.cause).toContain('ghost-001')\n\t\t\t}\n\t\t})\n\n\t\ttest('failing user code throws OrmMigrationError with phase user', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst m: Migration<typeof adapter> = {\n\t\t\t\tid: '0001',\n\t\t\t\tchanges: [{ kind: 'execute', up: async () => { throw new Error('user boom') } }],\n\t\t\t}\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\ttry {\n\t\t\t\tawait migrator.up()\n\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err).toBeInstanceOf(OrmMigrationError)\n\t\t\t\texpect(err.phase).toBe('user')\n\t\t\t\texpect(err.id).toBe('0001')\n\t\t\t}\n\t\t})\n\n\t\ttest('failing recordMigration throws OrmMigrationError with phase record', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tconst origRecord = adapter.recordMigration.bind(adapter)\n\t\t\tadapter.recordMigration = async (id: string, at: number) => {\n\t\t\t\tif (id === '0001') throw new Error('record boom')\n\t\t\t\treturn origRecord(id, at)\n\t\t\t}\n\t\t\tconst m: Migration<typeof adapter> = { id: '0001', changes: [] }\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\ttry {\n\t\t\t\tawait migrator.up()\n\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err).toBeInstanceOf(OrmMigrationError)\n\t\t\t\texpect(err.phase).toBe('record')\n\t\t\t\texpect(err.id).toBe('0001')\n\t\t\t}\n\t\t})\n\n\t\ttest('failing acquireMigrationLock throws OrmMigrationError with phase lock', async () => {\n\t\t\tconst { adapter, repo } = makeEnv()\n\t\t\tadapter.acquireMigrationLock = async () => { throw new Error('lock boom') }\n\t\t\tconst m: Migration<typeof adapter> = { id: '0001', changes: [] }\n\t\t\tconst migrator = Migrator.from(repo, adapter).migrations([m]).build()\n\t\t\ttry {\n\t\t\t\tawait migrator.up()\n\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t} catch (err: any) {\n\t\t\t\texpect(err).toBeInstanceOf(OrmMigrationError)\n\t\t\t\texpect(err.phase).toBe('lock')\n\t\t\t}\n\t\t})\n\t})\n}\n","import { toFieldName, type AnyField } from './fields'\n\nexport class OrderBy {\n\treadonly field: string\n\tconstructor(\n\t\tfield: string | AnyField,\n\t\treadonly direction: 'asc' | 'desc',\n\t) {\n\t\tthis.field = toFieldName(field)\n\t}\n}\n\nexport type QueryOptions<Sel extends string = string> = {\n\torderBy?: OrderBy[]\n\tlimit?: number\n\toffset?: number\n\tselect?: readonly Sel[]\n}\n\nexport type IterationOptions = {\n\tbatchSize?: number\n}\n\nexport type IterationQueryOptions<Sel extends string = string> = QueryOptions<Sel> & IterationOptions\n","import type { AnyField, Field } from './fields'\nimport type { AnySchema, SchemaOutput } from './schema'\n\ntype SchemaPkValueType<S extends AnySchema> = NonNullable<S['pkField']['__valueType']>\ntype FkPkMatch<S extends AnySchema, FK extends AnyField> =\n\tNonNullable<FK['__valueType']> extends SchemaPkValueType<S> ? FK : never\n\nexport class ManyRelation<\n\tN extends string = string,\n\tTgtOutput extends Record<string, any> = Record<string, any>,\n\tFK extends AnyField = AnyField,\n> {\n\tdeclare readonly _output: TgtOutput\n\tconstructor(\n\t\treadonly name: N,\n\t\treadonly source: AnySchema,\n\t\treadonly target: AnySchema,\n\t\treadonly foreignKey: FK,\n\t\treadonly references: AnyField,\n\t) {}\n}\n\nexport class OneRelation<\n\tN extends string = string,\n\tTgtOutput extends Record<string, any> = Record<string, any>,\n\tFK extends AnyField = AnyField,\n> {\n\tdeclare readonly _output: TgtOutput\n\tconstructor(\n\t\treadonly name: N,\n\t\treadonly source: AnySchema,\n\t\treadonly target: AnySchema,\n\t\treadonly foreignKey: FK,\n\t\treadonly fkOwner: 'source' | 'target',\n\t\treadonly references: AnyField,\n\t) {}\n}\n\nexport type AnyRelDef = ManyRelation<string, Record<string, any>, any> | OneRelation<string, Record<string, any>, any>\n\nexport type ResolveRelDef<D extends AnyRelDef> =\n\tD extends OneRelation<any, infer TOut, any> ? TOut | null : D extends ManyRelation<any, infer TOut, any> ? TOut[] : never\n\nexport interface NestedPreloadDef<D extends AnyRelDef = AnyRelDef> {\n\tdef: D\n\tpreloads?: readonly AnyPreloadDef[]\n}\n\nexport type AnyPreloadDef = AnyRelDef | NestedPreloadDef\n\ntype Shift<T extends readonly unknown[]> = T extends readonly [unknown, ...infer R] ? R : []\ntype NodeName<N extends AnyPreloadDef> = N extends AnyRelDef\n\t? N['name']\n\t: N extends NestedPreloadDef<infer D extends AnyRelDef>\n\t\t? D['name']\n\t\t: never\n\ntype ResolveRelDefWithNested<D extends AnyRelDef, P extends readonly AnyPreloadDef[], Depth extends readonly unknown[]> = Depth extends []\n\t? ResolveRelDef<D>\n\t: D extends OneRelation<any, infer TOut, any>\n\t\t? (TOut & PreloadedMap<P, Shift<Depth>>) | null\n\t\t: D extends ManyRelation<any, infer TOut, any>\n\t\t\t? (TOut & PreloadedMap<P, Shift<Depth>>)[]\n\t\t\t: never\n\ntype NodeValue<N extends AnyPreloadDef, Depth extends readonly unknown[]> =\n\tN extends OneRelation<any, infer TOut, any>\n\t\t? TOut | null\n\t\t: N extends ManyRelation<any, infer TOut, any>\n\t\t\t? TOut[]\n\t\t\t: N extends { def: infer D extends AnyRelDef; preloads?: infer P extends readonly AnyPreloadDef[] }\n\t\t\t\t? ResolveRelDefWithNested<D, P, Depth>\n\t\t\t\t: never\n\nexport type PreloadedMap<P extends readonly AnyPreloadDef[], Depth extends readonly unknown[] = [1, 2, 3, 4, 5]> = Depth extends []\n\t? Record<never, never>\n\t: {\n\t\t\t[N in P[number] as NodeName<N>]: NodeValue<N, Depth>\n\t\t}\n\nclass RelationsBuilder<S extends AnySchema, R extends Record<string, AnyRelDef> = Record<never, never>> {\n\treadonly #source: S\n\treadonly #defs: Record<string, AnyRelDef>\n\n\tconstructor(source: S, defs?: Record<string, AnyRelDef>) {\n\t\tthis.#source = source\n\t\tthis.#defs = defs ?? {}\n\t}\n\n\thasMany<K extends string, T extends AnySchema, FK extends Field<any, any, T>>(\n\t\tname: K extends keyof R ? never : K,\n\t\tfk: FkPkMatch<S, FK>,\n\t): RelationsBuilder<\n\t\tS,\n\t\t{\n\t\t\t[Key in keyof R | K]: Key extends K\n\t\t\t\t? ManyRelation<K, SchemaOutput<T>, FK>\n\t\t\t\t: Key extends keyof R\n\t\t\t\t\t? R[Key]\n\t\t\t\t\t: never\n\t\t}\n\t> {\n\t\tconst target = (fk as any).__schema as AnySchema\n\t\tconst nextDefs = { ...this.#defs, [name]: new ManyRelation(name, this.#source, target, fk as any, this.#source.pkField) }\n\t\treturn new RelationsBuilder(this.#source, nextDefs) as any\n\t}\n\n\thasOne<K extends string, T extends AnySchema, FK extends Field<any, any, T>>(\n\t\tname: K extends keyof R ? never : K,\n\t\tfk: FkPkMatch<S, FK>,\n\t): RelationsBuilder<\n\t\tS,\n\t\t{\n\t\t\t[Key in keyof R | K]: Key extends K\n\t\t\t\t? OneRelation<K, SchemaOutput<T>, FK>\n\t\t\t\t: Key extends keyof R\n\t\t\t\t\t? R[Key]\n\t\t\t\t\t: never\n\t\t}\n\t> {\n\t\tconst target = (fk as any).__schema as AnySchema\n\t\tconst nextDefs = { ...this.#defs, [name]: new OneRelation(name, this.#source, target, fk as any, 'target', this.#source.pkField) }\n\t\treturn new RelationsBuilder(this.#source, nextDefs) as any\n\t}\n\n\tbelongsTo<K extends string, T extends AnySchema, FK extends Field<any, any, S>>(\n\t\tname: K extends keyof R ? never : K,\n\t\tfk: FkPkMatch<T, FK>,\n\t\ttarget: T,\n\t\treferences?: Field<NonNullable<FK['__valueType']>, any, T>,\n\t): RelationsBuilder<\n\t\tS,\n\t\t{\n\t\t\t[Key in keyof R | K]: Key extends K\n\t\t\t\t? OneRelation<K, SchemaOutput<T>, FK>\n\t\t\t\t: Key extends keyof R\n\t\t\t\t\t? R[Key]\n\t\t\t\t\t: never\n\t\t}\n\t> {\n\t\tconst ref = references ?? target.pkField\n\t\tconst nextDefs = { ...this.#defs, [name]: new OneRelation(name, this.#source, target, fk as any, 'source', ref as any) }\n\t\treturn new RelationsBuilder(this.#source, nextDefs) as any\n\t}\n\n\tbuild(): R {\n\t\treturn this.#defs as R\n\t}\n}\n\nexport class Relations {\n\tstatic from<S extends AnySchema>(source: S) {\n\t\treturn new RelationsBuilder(source)\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { Schema } = await import('./schema')\n\n\tdescribe('Relations.from()', () => {\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => 'user-id')\n\t\t\t.field('name', v.string())\n\t\t\t.field('orgId', v.string())\n\t\t\t.build()\n\n\t\tconst PostSchema = Schema.from('posts')\n\t\t\t.pk('id', v.string(), () => 'post-id')\n\t\t\t.field('title', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\tconst OrgSchema = Schema.from('orgs')\n\t\t\t.pk('id', v.string(), () => 'org-id')\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\n\t\tconst ProfileSchema = Schema.from('profiles')\n\t\t\t.pk('id', v.string(), () => 'profile-id')\n\t\t\t.field('bio', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\ttest('builds relations with hasMany, belongsTo, hasOne', () => {\n\t\t\tconst rels = Relations.from(UserSchema)\n\t\t\t\t.hasMany('posts', PostSchema.fields.userId)\n\t\t\t\t.belongsTo('org', UserSchema.fields.orgId, OrgSchema)\n\t\t\t\t.hasOne('profile', ProfileSchema.fields.userId)\n\t\t\t\t.build()\n\n\t\t\texpect(rels.posts).toBeInstanceOf(ManyRelation)\n\t\t\texpect(rels.org).toBeInstanceOf(OneRelation)\n\t\t\texpect(rels.profile).toBeInstanceOf(OneRelation)\n\t\t})\n\n\t\ttest('source references use outer-scope const (no src param)', () => {\n\t\t\tconst rels = Relations.from(UserSchema)\n\t\t\t\t.belongsTo('org', UserSchema.fields.orgId, OrgSchema)\n\t\t\t\t.build()\n\n\t\t\texpect(rels.org.foreignKey).toBe(UserSchema.fields.orgId)\n\t\t\texpect(rels.org.target).toBe(OrgSchema)\n\t\t})\n\n\t\ttest('clone-on-step: .hasMany() returns a new builder', () => {\n\t\t\tconst base = Relations.from(UserSchema)\n\t\t\tconst a = base.hasMany('posts', PostSchema.fields.userId)\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\n\t\ttest('clone-on-step: .hasOne() returns a new builder', () => {\n\t\t\tconst base = Relations.from(UserSchema)\n\t\t\tconst a = base.hasOne('profile', ProfileSchema.fields.userId)\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\n\t\ttest('clone-on-step: .belongsTo() returns a new builder', () => {\n\t\t\tconst base = Relations.from(UserSchema)\n\t\t\tconst a = base.belongsTo('org', UserSchema.fields.orgId, OrgSchema)\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\n\t\ttest('clone-on-step: fan-out from shared base does not pollute either branch', () => {\n\t\t\tconst base = Relations.from(UserSchema)\n\t\t\tconst branchA = base.hasMany('posts', PostSchema.fields.userId).build()\n\t\t\tconst branchB = base.hasOne('profile', ProfileSchema.fields.userId).build()\n\n\t\t\texpect(Object.keys(branchA)).toEqual(['posts'])\n\t\t\texpect(Object.keys(branchB)).toEqual(['profile'])\n\t\t})\n\t})\n\n\tdescribe('Relations.from() behavior', () => {\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => 'user-id')\n\t\t\t.field('name', v.string())\n\t\t\t.field('orgId', v.string())\n\t\t\t.field('managerId', v.optional(v.string()), { onCreate: () => undefined })\n\t\t\t.build()\n\n\t\tconst PostSchema = Schema.from('posts')\n\t\t\t.pk('id', v.string(), () => 'post-id')\n\t\t\t.field('title', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\tconst OrgSchema = Schema.from('orgs')\n\t\t\t.pk('id', v.string(), () => 'org-id')\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\n\t\tconst TagSchema = Schema.from('tags')\n\t\t\t.pk('id', v.string(), () => 'tag-id')\n\t\t\t.field('label', v.string())\n\t\t\t.build()\n\n\t\tconst PostTagSchema = Schema.from('post_tags')\n\t\t\t.pk('id', v.string(), () => 'pt-id')\n\t\t\t.field('postId', v.string())\n\t\t\t.field('tagId', v.string())\n\t\t\t.build()\n\n\t\tconst ProfileSchema = Schema.from('profiles')\n\t\t\t.pk('id', v.string(), () => 'profile-id')\n\t\t\t.field('bio', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\tconst UserRels = Relations.from(UserSchema)\n\t\t\t.hasMany('posts', PostSchema.fields.userId)\n\t\t\t.belongsTo('org', UserSchema.fields.orgId, OrgSchema)\n\t\t\t.hasOne('profile', ProfileSchema.fields.userId)\n\t\t\t.build()\n\n\t\tconst PostRels = Relations.from(PostSchema)\n\t\t\t.belongsTo('author', PostSchema.fields.userId, UserSchema)\n\t\t\t.hasMany('postTags', PostTagSchema.fields.postId)\n\t\t\t.build()\n\n\t\tconst TagRels = Relations.from(TagSchema)\n\t\t\t.hasMany('postTags', PostTagSchema.fields.tagId)\n\t\t\t.build()\n\n\t\tconst PostTagRels = Relations.from(PostTagSchema)\n\t\t\t.belongsTo('post', PostTagSchema.fields.postId, PostSchema)\n\t\t\t.belongsTo('tag', PostTagSchema.fields.tagId, TagSchema)\n\t\t\t.build()\n\n\t\ttest('hasMany returns ManyRelation instances', () => {\n\t\t\texpect(UserRels.posts).toBeInstanceOf(ManyRelation)\n\t\t\texpect(PostRels.postTags).toBeInstanceOf(ManyRelation)\n\t\t\texpect(TagRels.postTags).toBeInstanceOf(ManyRelation)\n\t\t})\n\n\t\ttest('belongsTo returns OneRelation instances', () => {\n\t\t\texpect(UserRels.org).toBeInstanceOf(OneRelation)\n\t\t\texpect(PostRels.author).toBeInstanceOf(OneRelation)\n\t\t\texpect(PostTagRels.post).toBeInstanceOf(OneRelation)\n\t\t\texpect(PostTagRels.tag).toBeInstanceOf(OneRelation)\n\t\t})\n\n\t\ttest('hasOne returns OneRelation instances', () => {\n\t\t\texpect(UserRels.profile).toBeInstanceOf(OneRelation)\n\t\t})\n\n\t\ttest('hasMany stores correct source, target, foreignKey, and references', () => {\n\t\t\tconst rel = UserRels.posts\n\t\t\texpect(rel.name).toBe('posts')\n\t\t\texpect(rel.source).toBe(UserSchema)\n\t\t\texpect(rel.target).toBe(PostSchema)\n\t\t\texpect(rel.foreignKey).toBe(PostSchema.fields.userId)\n\t\t\texpect(rel.references).toBe(UserSchema.pkField)\n\t\t})\n\n\t\ttest('hasOne stores correct source, target, foreignKey, and fkOwner', () => {\n\t\t\tconst rel = UserRels.profile\n\t\t\texpect(rel.name).toBe('profile')\n\t\t\texpect(rel.source).toBe(UserSchema)\n\t\t\texpect(rel.target).toBe(ProfileSchema)\n\t\t\texpect(rel.foreignKey).toBe(ProfileSchema.fields.userId)\n\t\t\texpect(rel.fkOwner).toBe('target')\n\t\t\texpect(rel.references).toBe(UserSchema.pkField)\n\t\t})\n\n\t\ttest('belongsTo stores correct source, target, foreignKey, and fkOwner', () => {\n\t\t\tconst rel = UserRels.org\n\t\t\texpect(rel.name).toBe('org')\n\t\t\texpect(rel.source).toBe(UserSchema)\n\t\t\texpect(rel.target).toBe(OrgSchema)\n\t\t\texpect(rel.foreignKey).toBe(UserSchema.fields.orgId)\n\t\t\texpect(rel.fkOwner).toBe('source')\n\t\t\texpect(rel.references).toBe(OrgSchema.pkField)\n\t\t})\n\n\t\ttest('join-table hasMany stores correct metadata', () => {\n\t\t\tconst rel = PostRels.postTags\n\t\t\texpect(rel.name).toBe('postTags')\n\t\t\texpect(rel.source).toBe(PostSchema)\n\t\t\texpect(rel.target).toBe(PostTagSchema)\n\t\t\texpect(rel.foreignKey).toBe(PostTagSchema.fields.postId)\n\t\t})\n\n\t\ttest('join-table belongsTo stores correct metadata', () => {\n\t\t\tconst rel = PostTagRels.tag\n\t\t\texpect(rel.name).toBe('tag')\n\t\t\texpect(rel.source).toBe(PostTagSchema)\n\t\t\texpect(rel.target).toBe(TagSchema)\n\t\t\texpect(rel.foreignKey).toBe(PostTagSchema.fields.tagId)\n\t\t})\n\n\t\ttest('self-referential relation works without special casing', () => {\n\t\t\tconst SelfRels = Relations.from(UserSchema)\n\t\t\t\t.belongsTo('manager', UserSchema.fields.managerId!, UserSchema)\n\t\t\t\t.build()\n\n\t\t\texpect(SelfRels.manager).toBeInstanceOf(OneRelation)\n\t\t\texpect(SelfRels.manager.source).toBe(UserSchema)\n\t\t\texpect(SelfRels.manager.target).toBe(UserSchema)\n\t\t\texpect(SelfRels.manager.fkOwner).toBe('source')\n\t\t})\n\n\t\ttest('many-to-many via explicit join schema', () => {\n\t\t\texpect(PostRels.postTags).toBeInstanceOf(ManyRelation)\n\t\t\texpect(PostTagRels.post).toBeInstanceOf(OneRelation)\n\t\t\texpect(PostTagRels.tag).toBeInstanceOf(OneRelation)\n\t\t\texpect(PostTagRels.post.target).toBe(PostSchema)\n\t\t\texpect(PostTagRels.tag.target).toBe(TagSchema)\n\t\t})\n\t})\n\n\tdescribe('type-level: Relations.from uniqueness guard', () => {\n\t\ttest('duplicate relation name is a TS error', () => {\n\t\t\tconst S = Schema.from('test').pk('id', v.string(), () => 'x').build()\n\t\t\tconst T = Schema.from('targets')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('sId', v.string())\n\t\t\t\t.build()\n\t\t\t// @ts-expect-error — duplicate name 'items' should fail\n\t\t\tRelations.from(S).hasMany('items', T.fields.sId).hasMany('items', T.fields.sId)\n\t\t})\n\t})\n\n\tdescribe('type-level: FK-PK type-match guarantee', () => {\n\t\ttest('string FK pointing at number PK is a TS error', () => {\n\t\t\tconst NumPkSchema = Schema.from('nums')\n\t\t\t\t.pk('id', v.number(), () => 0)\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\n\t\t\tconst StringFkSchema = Schema.from('strings')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('numRef', v.string())\n\t\t\t\t.build()\n\n\t\t\t// @ts-expect-error — string FK does not match number PK\n\t\t\tRelations.from(NumPkSchema).hasMany('items', StringFkSchema.fields.numRef)\n\t\t})\n\n\t\ttest('matching FK-PK types compile correctly', () => {\n\t\t\tconst S = Schema.from('source').pk('id', v.string(), () => 'x').build()\n\t\t\tconst T = Schema.from('target')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('sourceId', v.string())\n\t\t\t\t.build()\n\t\t\tconst rels = Relations.from(S).hasMany('items', T.fields.sourceId).build()\n\t\t\texpect(rels.items).toBeInstanceOf(ManyRelation)\n\t\t})\n\t})\n\n\tdescribe('type-level: Field-only-FK rule', () => {\n\t\ttest('raw string FK is a TS error for hasMany', () => {\n\t\t\tconst S = Schema.from('s').pk('id', v.string(), () => 'x').build()\n\t\t\t// @ts-expect-error — raw string not allowed, must be a Field instance\n\t\t\tRelations.from(S).hasMany('items', 'someKey')\n\t\t})\n\n\t\ttest('raw string FK is a TS error for belongsTo', () => {\n\t\t\tconst S = Schema.from('s')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('ref', v.string())\n\t\t\t\t.build()\n\t\t\tconst T = Schema.from('t').pk('id', v.string(), () => 'x').build()\n\t\t\t// @ts-expect-error — raw string not allowed, must be a Field instance\n\t\t\tRelations.from(S).belongsTo('parent', 'ref', T)\n\t\t})\n\t})\n\n\tdescribe('type-level: schema relations-agnosticism', () => {\n\t\ttest('schema artifact contains no relational information', () => {\n\t\t\tconst _S = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\ttype SKeys = keyof typeof _S\n\t\t\texpectTypeOf<'relations' extends SKeys ? true : false>().toEqualTypeOf<false>()\n\t\t})\n\t})\n}\n","import { v, type Pipe } from 'valleyed'\n\nimport { EquippedError } from '../../../errors'\nimport type { AnySchema } from '../../schema'\n\nexport type ComputedSelectionPlan = {\n\trequestedSelect: Set<string> | null\n\tadapterSelect?: string[]\n\tcomputeNames: string[]\n}\n\nexport function planSelection(schema: AnySchema, select?: readonly string[]): ComputedSelectionPlan {\n\tconst computedDefs = schema.computedDefs as Record<string, { deps: readonly string[] }>\n\tconst computedNames = new Set(Object.keys(computedDefs))\n\tconst persistedNames = new Set(Object.keys(schema.fields))\n\n\tif (!select || select.length === 0) {\n\t\treturn {\n\t\t\trequestedSelect: null,\n\t\t\tadapterSelect: undefined,\n\t\t\tcomputeNames: [...computedNames],\n\t\t}\n\t}\n\n\tconst requestedSelect = new Set(select)\n\tconst adapterSelect = new Set<string>()\n\tconst selectedComputeNames = new Set<string>()\n\n\tfor (const key of select) {\n\t\tif (persistedNames.has(key)) {\n\t\t\tadapterSelect.add(key)\n\t\t\tcontinue\n\t\t}\n\t\tif (computedNames.has(key)) {\n\t\t\tselectedComputeNames.add(key)\n\t\t\tfor (const dep of computedDefs[key].deps) adapterSelect.add(dep)\n\t\t\tcontinue\n\t\t}\n\t\tthrow new EquippedError('Unknown selected field', {\n\t\t\tschema: schema.name,\n\t\t\tselectedField: key,\n\t\t\tavailableFields: [...persistedNames, ...computedNames],\n\t\t})\n\t}\n\n\treturn {\n\t\trequestedSelect,\n\t\tadapterSelect: [...adapterSelect],\n\t\tcomputeNames: [...selectedComputeNames],\n\t}\n}\n\ntype ComputedDef = {\n\tpipe: Pipe<any, any>\n\tdeps: readonly string[]\n\tcompute: (data: Record<string, unknown>) => unknown\n}\n\nexport function applyComputedSelection(\n\tschema: AnySchema,\n\trows: Record<string, unknown>[],\n\tplan: ComputedSelectionPlan,\n): Record<string, unknown>[] {\n\tconst computedDefs = schema.computedDefs as Record<string, ComputedDef>\n\n\treturn rows.map((row) => {\n\t\tconst enriched: Record<string, unknown> = { ...row }\n\t\tfor (const computeName of plan.computeNames) {\n\t\t\tconst def = computedDefs[computeName]\n\t\t\tconst depInput: Record<string, unknown> = {}\n\t\t\tfor (const dep of def.deps) {\n\t\t\t\tif (!(dep in row)) {\n\t\t\t\t\tthrow new EquippedError('Computed field dependency missing from adapter result', {\n\t\t\t\t\t\tschema: schema.name,\n\t\t\t\t\t\tcomputedField: computeName,\n\t\t\t\t\t\tdependency: dep,\n\t\t\t\t\t\tdependencies: def.deps,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tdepInput[dep] = row[dep]\n\t\t\t}\n\t\t\tconst r = v.validate(def.pipe, def.compute(depInput))\n\t\t\tif (!r.valid) throw new EquippedError('Computed field validation failed', {\n\t\t\t\tschema: schema.name,\n\t\t\t\tcomputedField: computeName,\n\t\t\t\tcause: r.error,\n\t\t\t})\n\t\t\tenriched[computeName] = r.value\n\t\t}\n\n\t\tif (!plan.requestedSelect) return enriched\n\n\t\tconst shaped: Record<string, unknown> = {}\n\t\tfor (const key of plan.requestedSelect) {\n\t\t\tif (key in enriched) shaped[key] = enriched[key]\n\t\t}\n\t\treturn shaped\n\t})\n}\n","import { Instance } from '../../../instance'\nimport { OrmValidationError, type OrmValidationFailure } from '../../errors'\nimport { ManyRelation, OneRelation, type AnyPreloadDef, type AnyRelDef, type NestedPreloadDef } from '../../relations'\nimport type { AnySchema } from '../../schema'\n\nconst FALLBACK_PAGINATION_DEFAULT_LIMIT = 100\nconst MAX_PRELOAD_DEPTH = 5\n\nexport type ReadOffsetSource =\n\t| { kind: 'offset'; value: unknown }\n\t| { kind: 'page'; value: unknown }\n\nexport type ReadLimitSource = { value: unknown }\n\nexport type NormalisedAllReadQuery = {\n\tlimit?: number\n\toffset?: number\n}\n\nexport type NormalisedIterationReadQuery = NormalisedAllReadQuery & {\n\tbatchSize?: number\n}\n\nexport type NormalisedPaginatedReadQuery = {\n\tlimit: number\n\toffset: number\n\tcurrent: number\n}\n\nfunction isRelDef(def: unknown): def is AnyRelDef {\n\treturn def instanceof ManyRelation || def instanceof OneRelation\n}\n\nfunction isNestedPreloadDef(def: AnyPreloadDef): def is NestedPreloadDef {\n\treturn typeof def === 'object' && def != null && 'def' in def\n}\n\nfunction relationStep(def: AnyRelDef) {\n\treturn `${def.source.name}.${def.name}->${def.target.name}`\n}\n\nfunction isPositiveInteger(value: unknown): value is number {\n\treturn typeof value === 'number' && Number.isSafeInteger(value) && value > 0\n}\n\nfunction isNonNegativeInteger(value: unknown): value is number {\n\treturn typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\n}\n\nfunction getPaginationDefaultLimit() {\n\treturn Instance.maybeGet()?.settings.utils.paginationDefaultLimit ?? FALLBACK_PAGINATION_DEFAULT_LIMIT\n}\n\nfunction collectSelectFailures(schema: AnySchema, select: readonly string[] | undefined, failures: OrmValidationFailure[]) {\n\tif (!select || select.length === 0) return\n\n\tconst persistedNames = new Set(Object.keys(schema.fields))\n\tconst computedNames = new Set(Object.keys(schema.computedDefs as Record<string, unknown>))\n\tfor (const field of select) {\n\t\tif (persistedNames.has(field) || computedNames.has(field)) continue\n\t\tfailures.push({\n\t\t\tfield,\n\t\t\tcause: `Unknown selected field \"${field}\" on schema \"${schema.name}\"`,\n\t\t})\n\t}\n}\n\nfunction collectPreloadFailures(\n\tschema: AnySchema,\n\tdefs: readonly AnyPreloadDef[] | undefined,\n\tfailures: OrmValidationFailure[],\n\tdepth = 1,\n\tpath: readonly string[] = [],\n) {\n\tif (defs == null) return\n\tif (!Array.isArray(defs)) {\n\t\tfailures.push({ cause: 'Preloads must be an array' })\n\t\treturn\n\t}\n\tif (defs.length === 0) return\n\n\tif (depth > MAX_PRELOAD_DEPTH) {\n\t\tfailures.push({ cause: `Preload depth exceeded max depth ${MAX_PRELOAD_DEPTH}` })\n\t\treturn\n\t}\n\n\tfor (const preload of defs) {\n\t\tconst rawDef = isRelDef(preload) ? preload : isNestedPreloadDef(preload) ? preload.def : undefined\n\t\tif (!isRelDef(rawDef)) {\n\t\t\tfailures.push({ cause: 'Invalid preload definition: expected a relation definition or nested preload definition with `def`' })\n\t\t\tcontinue\n\t\t}\n\n\t\tif (rawDef.source !== schema) {\n\t\t\tfailures.push({\n\t\t\t\tfield: rawDef.name,\n\t\t\t\tcause: `Preload relation \"${rawDef.name}\" belongs to source schema \"${rawDef.source.name}\" but was used from schema \"${schema.name}\"`,\n\t\t\t})\n\t\t}\n\n\t\tconst step = relationStep(rawDef)\n\t\tif (path.includes(step)) {\n\t\t\tfailures.push({ field: rawDef.name, cause: `Preload cycle detected: ${[...path, step].join(' -> ')}` })\n\t\t\tcontinue\n\t\t}\n\n\t\tif (isNestedPreloadDef(preload)) {\n\t\t\tconst nested = preload.preloads ?? []\n\t\t\tif (!Array.isArray(nested)) {\n\t\t\t\tfailures.push({ field: rawDef.name, cause: `Nested preloads for relation \"${rawDef.name}\" must be an array` })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcollectPreloadFailures(rawDef.target, nested, failures, depth + 1, [...path, step])\n\t\t}\n\t}\n}\n\nfunction collectLimitFailure(value: unknown, failures: OrmValidationFailure[]): number | undefined {\n\tif (isPositiveInteger(value)) return value\n\tfailures.push({ field: 'limit', option: 'limit', cause: 'Limit must be a positive safe integer' })\n\treturn undefined\n}\n\nfunction collectOffsetFailure(value: unknown, failures: OrmValidationFailure[]): number | undefined {\n\tif (isNonNegativeInteger(value)) return value\n\tfailures.push({ field: 'offset', option: 'offset', cause: 'Offset must be a non-negative safe integer' })\n\treturn undefined\n}\n\nfunction collectPageFailure(value: unknown, failures: OrmValidationFailure[]): number | undefined {\n\tif (isPositiveInteger(value)) return value\n\tfailures.push({ field: 'page', option: 'page', cause: 'Page must be a positive safe integer' })\n\treturn undefined\n}\n\nfunction collectBatchSizeFailure(value: unknown, failures: OrmValidationFailure[]): number | undefined {\n\tif (value === undefined) return undefined\n\tif (isPositiveInteger(value)) return value\n\tfailures.push({ field: 'batchSize', option: 'batchSize', cause: 'Batch size must be a positive safe integer' })\n\treturn undefined\n}\n\nfunction collectIterationOptionFailures(options: unknown, failures: OrmValidationFailure[]): number | undefined {\n\tif (options === undefined) return undefined\n\tif (options == null || typeof options !== 'object' || Array.isArray(options)) {\n\t\tfailures.push({ option: 'batchSize', cause: 'Iteration options must be an object' })\n\t\treturn undefined\n\t}\n\treturn collectBatchSizeFailure((options as { batchSize?: unknown }).batchSize, failures)\n}\n\nfunction throwIfFailures(schema: AnySchema, operation: string, failures: OrmValidationFailure[]) {\n\tif (failures.length > 0) throw new OrmValidationError('query-shape', schema.name, operation, failures)\n}\n\nexport function assertNormalisedFindReadShape(\n\tschema: AnySchema,\n\toperation: string,\n\tstate: {\n\t\tselect: readonly string[] | undefined\n\t\tpreloads: readonly AnyPreloadDef[] | undefined\n\t},\n): void {\n\tconst failures: OrmValidationFailure[] = []\n\tcollectSelectFailures(schema, state.select, failures)\n\tcollectPreloadFailures(schema, state.preloads, failures)\n\tthrowIfFailures(schema, operation, failures)\n}\n\nexport function normaliseAllFindReadShape(\n\tschema: AnySchema,\n\toperation: string,\n\tstate: {\n\t\tselect: readonly string[] | undefined\n\t\tpreloads: readonly AnyPreloadDef[] | undefined\n\t\tlimitSource?: ReadLimitSource\n\t\toffsetSource?: ReadOffsetSource\n\t},\n): NormalisedAllReadQuery {\n\tconst failures: OrmValidationFailure[] = []\n\tcollectSelectFailures(schema, state.select, failures)\n\tcollectPreloadFailures(schema, state.preloads, failures)\n\n\tlet limit = state.limitSource === undefined ? undefined : collectLimitFailure(state.limitSource.value, failures)\n\tlet offset: number | undefined\n\n\tif (state.offsetSource?.kind === 'offset') {\n\t\toffset = collectOffsetFailure(state.offsetSource.value, failures)\n\t} else if (state.offsetSource?.kind === 'page') {\n\t\tconst page = collectPageFailure(state.offsetSource.value, failures)\n\t\tif (limit === undefined && state.limitSource === undefined) {\n\t\t\tlimit = getPaginationDefaultLimit()\n\t\t\tif (!isPositiveInteger(limit)) {\n\t\t\t\tfailures.push({ field: 'limit', cause: 'Default page limit must be a positive safe integer' })\n\t\t\t\tlimit = undefined\n\t\t\t}\n\t\t}\n\t\tif (page !== undefined && limit !== undefined) {\n\t\t\tconst resolvedOffset = (page - 1) * limit\n\t\t\tif (isNonNegativeInteger(resolvedOffset)) {\n\t\t\t\toffset = resolvedOffset\n\t\t\t} else {\n\t\t\t\tfailures.push({ field: 'offset', cause: 'Resolved page offset must be a non-negative safe integer' })\n\t\t\t}\n\t\t}\n\t}\n\n\tthrowIfFailures(schema, operation, failures)\n\treturn { limit, offset }\n}\n\nexport function normaliseAllIterateReadShape(\n\tschema: AnySchema,\n\toperation: string,\n\tstate: {\n\t\tselect: readonly string[] | undefined\n\t\tpreloads: readonly AnyPreloadDef[] | undefined\n\t\tlimitSource?: ReadLimitSource\n\t\toffsetSource?: ReadOffsetSource\n\t},\n\toptions?: unknown,\n): NormalisedIterationReadQuery {\n\tconst failures: OrmValidationFailure[] = []\n\tcollectSelectFailures(schema, state.select, failures)\n\tcollectPreloadFailures(schema, state.preloads, failures)\n\n\tlet limit = state.limitSource === undefined ? undefined : collectLimitFailure(state.limitSource.value, failures)\n\tlet offset: number | undefined\n\tconst batchSize = collectIterationOptionFailures(options, failures)\n\n\tif (state.offsetSource?.kind === 'offset') {\n\t\toffset = collectOffsetFailure(state.offsetSource.value, failures)\n\t} else if (state.offsetSource?.kind === 'page') {\n\t\tconst page = collectPageFailure(state.offsetSource.value, failures)\n\t\tif (limit === undefined && state.limitSource === undefined) {\n\t\t\tlimit = getPaginationDefaultLimit()\n\t\t\tif (!isPositiveInteger(limit)) {\n\t\t\t\tfailures.push({ field: 'limit', option: 'limit', cause: 'Default page limit must be a positive safe integer' })\n\t\t\t\tlimit = undefined\n\t\t\t}\n\t\t}\n\t\tif (page !== undefined && limit !== undefined) {\n\t\t\tconst resolvedOffset = (page - 1) * limit\n\t\t\tif (isNonNegativeInteger(resolvedOffset)) {\n\t\t\t\toffset = resolvedOffset\n\t\t\t} else {\n\t\t\t\tfailures.push({ field: 'offset', option: 'offset', cause: 'Resolved page offset must be a non-negative safe integer' })\n\t\t\t}\n\t\t}\n\t}\n\n\tthrowIfFailures(schema, operation, failures)\n\treturn { limit, offset, batchSize }\n}\n\nexport function normaliseAllPaginateReadShape(\n\tschema: AnySchema,\n\toperation: string,\n\tstate: {\n\t\tselect: readonly string[] | undefined\n\t\tpreloads: readonly AnyPreloadDef[] | undefined\n\t\tlimitSource?: ReadLimitSource\n\t\toffsetSource?: ReadOffsetSource\n\t},\n): NormalisedPaginatedReadQuery {\n\tconst failures: OrmValidationFailure[] = []\n\tcollectSelectFailures(schema, state.select, failures)\n\tcollectPreloadFailures(schema, state.preloads, failures)\n\n\tlet limit: number | undefined\n\tif (state.limitSource === undefined) {\n\t\tlimit = getPaginationDefaultLimit()\n\t\tif (!isPositiveInteger(limit)) {\n\t\t\tfailures.push({ field: 'limit', cause: 'Default page limit must be a positive safe integer' })\n\t\t\tlimit = undefined\n\t\t}\n\t} else {\n\t\tlimit = collectLimitFailure(state.limitSource.value, failures)\n\t}\n\n\tlet offset = 0\n\tlet current = 1\n\n\tif (state.offsetSource?.kind === 'offset') {\n\t\tconst resolvedOffset = collectOffsetFailure(state.offsetSource.value, failures)\n\t\tif (resolvedOffset !== undefined) {\n\t\t\toffset = resolvedOffset\n\t\t\tif (limit !== undefined) current = Math.floor(resolvedOffset / limit) + 1\n\t\t}\n\t} else if (state.offsetSource?.kind === 'page') {\n\t\tconst page = collectPageFailure(state.offsetSource.value, failures)\n\t\tif (page !== undefined) current = page\n\t\tif (page !== undefined && limit !== undefined) {\n\t\t\tconst resolvedOffset = (page - 1) * limit\n\t\t\tif (isNonNegativeInteger(resolvedOffset)) {\n\t\t\t\toffset = resolvedOffset\n\t\t\t} else {\n\t\t\t\tfailures.push({ field: 'offset', cause: 'Resolved page offset must be a non-negative safe integer' })\n\t\t\t}\n\t\t}\n\t}\n\n\tthrowIfFailures(schema, operation, failures)\n\treturn { limit: limit as number, offset, current }\n}\n","import { v, type Pipe, type PipeInput, type PipeOutput } from 'valleyed'\n\nimport { OrmValidationError, type OrmValidationFailure } from './errors'\nimport type { AnySchemaField, SchemaField } from './fields'\nimport { Schema, type AnySchema, type SchemaFields, type SchemaOutput } from './schema'\nimport { SetOp, isUpdateOp, opTouchedFields, type AnyUpdateOp } from './updates'\nimport type { Prettify } from './utils'\n\nexport type SchemaCreateInput<S extends AnySchema> = Prettify<\n\t{\n\t\t[K in keyof SchemaFields<S> as SchemaFields<S>[K] extends SchemaField<any, any, true>\n\t\t\t? never\n\t\t\t: K]: SchemaFields<S>[K] extends AnySchemaField ? PipeInput<SchemaFields<S>[K]['pipe']> : never\n\t} & {\n\t\t[K in keyof SchemaFields<S> as SchemaFields<S>[K] extends SchemaField<any, any, true>\n\t\t\t? K\n\t\t\t: never]?: SchemaFields<S>[K] extends AnySchemaField ? PipeInput<SchemaFields<S>[K]['pipe']> : never\n\t}\n>\n\nexport type SchemaUpdateInput<S extends AnySchema> =\n\tS extends Schema<any, any, infer F>\n\t\t? { [K in keyof F]?: F[K] extends AnySchemaField ? PipeInput<F[K]['pipe']> | AnyUpdateOp : never }\n\t\t: Record<string, unknown>\n\nexport type SchemaUpdateOutput<S extends AnySchema> =\n\tS extends Schema<any, any, infer F>\n\t\t? { [K in keyof F]?: F[K] extends AnySchemaField ? PipeOutput<F[K]['pipe']> | AnyUpdateOp : never }\n\t\t: Record<string, unknown>\n\nexport function tryValidateCreateRow<S extends AnySchema>(\n\ts: S,\n\tdata: Record<string, unknown>,\n): { ok: true; value: SchemaOutput<S> } | { ok: false; failures: OrmValidationFailure[] } {\n\tconst failures: OrmValidationFailure[] = []\n\tconst result: Record<string, unknown> = {}\n\n\tfor (const [key, entry] of Object.entries(s.fields)) {\n\t\tconst pipe = entry.onCreate ? v.defaults(entry.pipe, entry.onCreate()) : entry.pipe\n\t\tconst fieldValue = key in data ? data[key] : undefined\n\t\tconst validated = v.validate(pipe, fieldValue)\n\t\tif (validated.valid) {\n\t\t\tresult[key] = validated.value\n\t\t} else {\n\t\t\tfailures.push({ field: key, cause: validated.error })\n\t\t}\n\t}\n\n\tif (failures.length > 0) return { ok: false, failures }\n\treturn { ok: true, value: result as SchemaOutput<S> }\n}\n\nexport function validateCreate<S extends AnySchema>(s: S, data: Record<string, unknown>): SchemaOutput<S> {\n\tconst result = tryValidateCreateRow(s, data)\n\tif (!result.ok) {\n\t\tthrow new OrmValidationError('validation', s.name, 'createOne', result.failures)\n\t}\n\treturn result.value\n}\n\nexport function validateCreateMany<S extends AnySchema>(s: S, rows: Record<string, unknown>[]): SchemaOutput<S>[] {\n\tconst allFailures: OrmValidationFailure[] = []\n\tconst validated: SchemaOutput<S>[] = []\n\n\tfor (let i = 0; i < rows.length; i++) {\n\t\tconst result = tryValidateCreateRow(s, rows[i])\n\t\tif (result.ok) {\n\t\t\tvalidated.push(result.value)\n\t\t} else {\n\t\t\tfor (const failure of result.failures) {\n\t\t\t\tallFailures.push({ ...failure, rowIndex: i })\n\t\t\t}\n\t\t}\n\t}\n\n\tif (allFailures.length > 0) {\n\t\tthrow new OrmValidationError('validation', s.name, 'createMany', allFailures)\n\t}\n\n\treturn validated\n}\n\nexport function validateUpdate<S extends AnySchema>(s: S, data: Record<string, unknown>): SchemaUpdateOutput<S> {\n\tconst pipes: Record<string, Pipe<any, any>> = {}\n\tconst ops: Record<string, unknown> = {}\n\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (isUpdateOp(value)) ops[key] = value\n\t\telse if (key in s.fieldDefs) pipes[key] = s.fieldDefs[key].pipe\n\t}\n\n\tfor (const [key, entry] of Object.entries(s.fieldDefs)) {\n\t\tif (!(key in data) && entry.onUpdate) pipes[key] = v.defaults(entry.pipe, entry.onUpdate())\n\t}\n\n\tconst r = v.validate(v.object(pipes), data)\n\tif (!r.valid) throw new OrmValidationError('validation', s.name, 'updateByPk', [{ cause: r.error }])\n\tconst validated = r.value as Record<string, unknown>\n\treturn { ...validated, ...ops } as SchemaUpdateOutput<S>\n}\n\nexport function validateUpdateOps(schema: AnySchema, ops: AnyUpdateOp[], operation = 'updateByPk'): AnyUpdateOp[] {\n\tconst touched = new Map<string, number[]>()\n\n\tfor (let i = 0; i < ops.length; i++) {\n\t\tfor (const field of opTouchedFields(ops[i])) {\n\t\t\tif (!touched.has(field)) touched.set(field, [])\n\t\t\ttouched.get(field)!.push(i)\n\t\t}\n\t}\n\n\tconst autoBumped: AnyUpdateOp[] = []\n\tfor (const [key, entry] of Object.entries(schema.fieldDefs)) {\n\t\tif (entry.onUpdate && !touched.has(key)) {\n\t\t\tconst bumpOp = new SetOp({ [key]: entry.onUpdate() })\n\t\t\tautoBumped.push(bumpOp)\n\t\t\ttouched.set(key, [ops.length + autoBumped.length - 1])\n\t\t}\n\t}\n\n\tconst allOps = [...ops, ...autoBumped]\n\n\tconst conflicts: OrmValidationFailure[] = []\n\tfor (const [field, indices] of touched) {\n\t\tif (indices.length > 1) {\n\t\t\tconflicts.push({ field, cause: `field \"${field}\" touched by multiple ops at indices [${indices.join(', ')}]` })\n\t\t}\n\t}\n\tif (conflicts.length > 0) {\n\t\tthrow new OrmValidationError('conflicting-ops', schema.name, operation, conflicts)\n\t}\n\n\tconst failures: OrmValidationFailure[] = []\n\tfor (let i = 0; i < allOps.length; i++) {\n\t\tconst op = allOps[i]\n\t\tif (op instanceof SetOp) {\n\t\t\tfor (const [key, value] of Object.entries(op.values)) {\n\t\t\t\tconst fieldDef = schema.fields[key] as AnySchemaField | undefined\n\t\t\t\tif (!fieldDef) continue\n\t\t\t\tconst r = v.validate(fieldDef.pipe, value)\n\t\t\t\tif (!r.valid) {\n\t\t\t\t\tfailures.push({ opIndex: i, field: key, cause: r.error })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (failures.length > 0) {\n\t\tthrow new OrmValidationError('validation', schema.name, operation, failures)\n\t}\n\n\treturn allOps\n}\n\nexport function composeSchemaConfig(\n\tresolve: (schema: AnySchema) => unknown,\n\ttransforms: ReadonlyArray<(config: any, schema: AnySchema) => any>,\n\tschema: AnySchema,\n\tpipe: Pipe<any, any>,\n): unknown {\n\tlet config = resolve(schema)\n\tfor (const transform of transforms) {\n\t\tconfig = transform(config, schema)\n\t}\n\tconst r = v.validate(pipe, config)\n\tif (!r.valid) throw new OrmValidationError('validation', schema.name, 'schemaConfig', [{ cause: r.error }])\n\treturn r.value\n}\n\nexport function validateUpsertConflicts(\n\tschema: AnySchema,\n\trawCreate: Record<string, unknown>,\n\tops: AnyUpdateOp[],\n): void {\n\tconst createFields = new Set(Object.keys(rawCreate))\n\tconst conflicts: OrmValidationFailure[] = []\n\tfor (const op of ops) {\n\t\tif (op instanceof SetOp) continue\n\t\tfor (const field of opTouchedFields(op)) {\n\t\t\tif (createFields.has(field)) {\n\t\t\t\tconflicts.push({ field, cause: `field \"${field}\" present in both create payload and atomic op \"${op.kind}\"` })\n\t\t\t}\n\t\t}\n\t}\n\tif (conflicts.length > 0) {\n\t\tthrow new OrmValidationError('conflicting-ops', schema.name, 'upsertOne', conflicts)\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { IncOp, MulOp, SetOp, UnsetOp } = await import('./updates')\n\tconst { EquippedError } = await import('../errors')\n\n\tdescribe('validateCreate', () => {\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => 'auto-id')\n\t\t\t.field('email', v.string())\n\t\t\t.field('name', v.string())\n\t\t\t.field('age', v.optional(v.number()))\n\t\t\t.field('createdAt', v.number(), { onCreate: () => 1000 })\n\t\t\t.build()\n\n\t\ttest('generates pk when not provided', () => {\n\t\t\tconst result = validateCreate(UserSchema, { email: 'a@b.com', name: 'Alice' })\n\t\t\texpect(result.id).toBe('auto-id')\n\t\t})\n\n\t\ttest('uses provided pk when given', () => {\n\t\t\tconst result = validateCreate(UserSchema, { id: 'custom-id', email: 'a@b.com', name: 'Alice' })\n\t\t\texpect(result.id).toBe('custom-id')\n\t\t})\n\n\t\ttest('applies onCreate generator for generated fields', () => {\n\t\t\tconst result = validateCreate(UserSchema, { email: 'a@b.com', name: 'Alice' })\n\t\t\texpect(result.createdAt).toBe(1000)\n\t\t})\n\n\t\ttest('allows overriding generated field values', () => {\n\t\t\tconst result = validateCreate(UserSchema, { email: 'a@b.com', name: 'Alice', createdAt: 9999 })\n\t\t\texpect(result.createdAt).toBe(9999)\n\t\t})\n\n\t\ttest('optional fields default to undefined when not provided', () => {\n\t\t\tconst result = validateCreate(UserSchema, { email: 'a@b.com', name: 'Alice' })\n\t\t\texpect(result.age).toBeUndefined()\n\t\t})\n\n\t\ttest('strips unknown fields', () => {\n\t\t\tconst result = validateCreate(UserSchema, { email: 'a@b.com', name: 'Alice', admin: true })\n\t\t\texpect((result as any).admin).toBeUndefined()\n\t\t})\n\n\t\ttest('throws OrmValidationError on invalid field type', () => {\n\t\t\ttry {\n\t\t\t\tvalidateCreate(UserSchema, { email: 123, name: 'Alice' })\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\texpect(err.schema).toBe('users')\n\t\t\t\texpect(err.operation).toBe('createOne')\n\t\t\t\texpect(err.failures.length).toBeGreaterThan(0)\n\t\t\t\texpect(err.failures[0].field).toBe('email')\n\t\t\t}\n\t\t})\n\n\t\ttest('throws OrmValidationError when required field is missing', () => {\n\t\t\ttry {\n\t\t\t\tvalidateCreate(UserSchema, { email: 'a@b.com' })\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\texpect(err.failures.some((f) => f.field === 'name')).toBe(true)\n\t\t\t}\n\t\t})\n\n\t\ttest('collects all field failures in a single error', () => {\n\t\t\ttry {\n\t\t\t\tvalidateCreate(UserSchema, { email: 123, name: 456 })\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\tconst failedFields = err.failures.map((f) => f.field)\n\t\t\t\texpect(failedFields).toContain('email')\n\t\t\t\texpect(failedFields).toContain('name')\n\t\t\t}\n\t\t})\n\t})\n\n\tdescribe('validateCreateMany', () => {\n\t\tconst ItemSchema = Schema.from('items')\n\t\t\t.pk('id', v.string(), () => 'item-id')\n\t\t\t.field('title', v.string())\n\t\t\t.field('price', v.number())\n\t\t\t.build()\n\n\t\ttest('validates all rows and returns validated documents', () => {\n\t\t\tconst results = validateCreateMany(ItemSchema, [\n\t\t\t\t{ title: 'A', price: 10 },\n\t\t\t\t{ title: 'B', price: 20 },\n\t\t\t])\n\t\t\texpect(results).toHaveLength(2)\n\t\t\texpect(results[0].title).toBe('A')\n\t\t\texpect(results[1].title).toBe('B')\n\t\t})\n\n\t\ttest('collects failures across rows with rowIndex populated', () => {\n\t\t\ttry {\n\t\t\t\tvalidateCreateMany(ItemSchema, [\n\t\t\t\t\t{ title: 'Good', price: 10 },\n\t\t\t\t\t{ title: 123, price: 20 },\n\t\t\t\t\t{ title: 'Also bad', price: 'not-a-number' },\n\t\t\t\t])\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\texpect(err.schema).toBe('items')\n\t\t\t\texpect(err.operation).toBe('createMany')\n\t\t\t\texpect(err.failures.length).toBeGreaterThanOrEqual(2)\n\t\t\t\tconst rowIndices = err.failures.map((f) => f.rowIndex)\n\t\t\t\texpect(rowIndices).toContain(1)\n\t\t\t\texpect(rowIndices).toContain(2)\n\t\t\t\texpect(rowIndices).not.toContain(0)\n\t\t\t}\n\t\t})\n\n\t\ttest('throws single error even with multiple bad rows', () => {\n\t\t\ttry {\n\t\t\t\tvalidateCreateMany(ItemSchema, [\n\t\t\t\t\t{ title: 1, price: 'bad' },\n\t\t\t\t\t{ title: 2, price: 'worse' },\n\t\t\t\t])\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\texpect(err.failures.filter((f) => f.rowIndex === 0).length).toBeGreaterThan(0)\n\t\t\t\texpect(err.failures.filter((f) => f.rowIndex === 1).length).toBeGreaterThan(0)\n\t\t\t}\n\t\t})\n\t})\n\n\tdescribe('validateUpdate', () => {\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => 'auto-id')\n\t\t\t.field('email', v.string())\n\t\t\t.field('name', v.string())\n\t\t\t.field('updatedAt', v.number(), { onCreate: () => 1000, onUpdate: () => 2000 })\n\t\t\t.build()\n\n\t\ttest('validates only provided fields', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { name: 'Bob' })\n\t\t\texpect(result).toHaveProperty('name', 'Bob')\n\t\t\texpect(result).not.toHaveProperty('email')\n\t\t})\n\n\t\ttest('applies onUpdate generator for unset generated fields', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { name: 'Bob' })\n\t\t\texpect(result).toHaveProperty('updatedAt', 2000)\n\t\t})\n\n\t\ttest('allows overriding onUpdate field explicitly', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { name: 'Bob', updatedAt: 5000 })\n\t\t\texpect(result).toHaveProperty('updatedAt', 5000)\n\t\t})\n\n\t\ttest('strips unknown fields', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { name: 'Bob', admin: true })\n\t\t\texpect((result as any).admin).toBeUndefined()\n\t\t})\n\n\t\ttest('throws on invalid field type', () => {\n\t\t\texpect(() => validateUpdate(UserSchema, { name: 123 })).toThrow()\n\t\t})\n\n\t\ttest('passes Op instances through without validation', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { name: new IncOp('name', 1) })\n\t\t\texpect(result.name).toBeInstanceOf(IncOp)\n\t\t})\n\n\t\ttest('Op on a field suppresses onUpdate for that field but not others', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { updatedAt: new IncOp('updatedAt', 1) })\n\t\t\texpect(result.updatedAt).toBeInstanceOf(IncOp)\n\t\t})\n\n\t\ttest('plain value and Op can coexist in same update', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { name: 'Bob', email: new UnsetOp('email') })\n\t\t\texpect(result.name).toBe('Bob')\n\t\t\texpect(result.email).toBeInstanceOf(UnsetOp)\n\t\t\texpect(result.updatedAt).toBe(2000)\n\t\t})\n\n\t\ttest('Op on onUpdate field suppresses its generator', () => {\n\t\t\tconst result = validateUpdate(UserSchema, { updatedAt: new MulOp('updatedAt', 2) })\n\t\t\texpect(result.updatedAt).toBeInstanceOf(MulOp)\n\t\t})\n\t})\n\n\tdescribe('OrmValidationError', () => {\n\t\ttest('has correct kind, schema, operation, and failures', () => {\n\t\t\tconst err = new OrmValidationError('conflicting-ops', 'users', 'updateByPk', [\n\t\t\t\t{ field: 'views', cause: 'conflict' },\n\t\t\t])\n\t\t\texpect(err).toBeInstanceOf(EquippedError)\n\t\t\texpect(err.kind).toBe('conflicting-ops')\n\t\t\texpect(err.schema).toBe('users')\n\t\t\texpect(err.operation).toBe('updateByPk')\n\t\t\texpect(err.failures).toHaveLength(1)\n\t\t})\n\t})\n\n\tdescribe('validateUpdateOps', () => {\n\t\tconst UpdateSchema = Schema.from('items')\n\t\t\t.pk('id', v.string(), () => 'auto')\n\t\t\t.field('name', v.string())\n\t\t\t.field('views', v.number())\n\t\t\t.field('updatedAt', v.number(), { onCreate: () => 1000, onUpdate: () => 2000 })\n\t\t\t.build()\n\n\t\ttest('passes through valid ops', () => {\n\t\t\tconst ops = validateUpdateOps(UpdateSchema, [new SetOp({ name: 'New Name' })])\n\t\t\texpect(ops.length).toBeGreaterThanOrEqual(1)\n\t\t\texpect(ops[0]).toBeInstanceOf(SetOp)\n\t\t})\n\n\t\ttest('auto-bumps onUpdate fields not touched by user ops', () => {\n\t\t\tconst ops = validateUpdateOps(UpdateSchema, [new SetOp({ name: 'New Name' })])\n\t\t\tconst bumped = ops.find((op) => op instanceof SetOp && 'updatedAt' in op.values && op !== ops[0])\n\t\t\texpect(bumped).toBeDefined()\n\t\t\texpect((bumped as SetOp).values.updatedAt).toBe(2000)\n\t\t})\n\n\t\ttest('user set({updatedAt:X}) suppresses auto-bump for that field', () => {\n\t\t\tconst ops = validateUpdateOps(UpdateSchema, [new SetOp({ updatedAt: 9999 })])\n\t\t\tconst setOps = ops.filter((op) => op instanceof SetOp) as SetOp[]\n\t\t\tconst allUpdatedAtValues = setOps.flatMap((op) =>\n\t\t\t\t'updatedAt' in op.values ? [op.values.updatedAt] : [],\n\t\t\t)\n\t\t\texpect(allUpdatedAtValues).toEqual([9999])\n\t\t})\n\n\t\ttest('atomic op on onUpdate field suppresses auto-bump', () => {\n\t\t\tconst ops = validateUpdateOps(UpdateSchema, [new IncOp('updatedAt', 1)])\n\t\t\tconst setOps = ops.filter((op) => op instanceof SetOp) as SetOp[]\n\t\t\tconst hasAutoBumpedUpdatedAt = setOps.some((op) => 'updatedAt' in op.values)\n\t\t\texpect(hasAutoBumpedUpdatedAt).toBe(false)\n\t\t})\n\n\t\ttest('set({views:0}) + inc(views, 1) throws conflicting-ops', () => {\n\t\t\texpect(() =>\n\t\t\t\tvalidateUpdateOps(UpdateSchema, [new SetOp({ views: 0 }), new IncOp('views', 1)]),\n\t\t\t).toThrow(OrmValidationError)\n\n\t\t\ttry {\n\t\t\t\tvalidateUpdateOps(UpdateSchema, [new SetOp({ views: 0 }), new IncOp('views', 1)])\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('conflicting-ops')\n\t\t\t\texpect(err.failures[0].field).toBe('views')\n\t\t\t}\n\t\t})\n\n\t\ttest('cross-kind conflict (unset + inc on same field) throws', () => {\n\t\t\tconst UpdateSchema2 = Schema.from('items2')\n\t\t\t\t.pk('id', v.string(), () => 'auto')\n\t\t\t\t.field('score', v.optional(v.number()))\n\t\t\t\t.build()\n\t\t\texpect(() =>\n\t\t\t\tvalidateUpdateOps(UpdateSchema2, [new UnsetOp('score'), new IncOp('score', 5)]),\n\t\t\t).toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('SetOp values are pipe-validated', () => {\n\t\t\texpect(() =>\n\t\t\t\tvalidateUpdateOps(UpdateSchema, [new SetOp({ name: 123 as any })]),\n\t\t\t).toThrow(OrmValidationError)\n\n\t\t\ttry {\n\t\t\t\tvalidateUpdateOps(UpdateSchema, [new SetOp({ name: 123 as any })])\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\texpect(err.failures[0].field).toBe('name')\n\t\t\t}\n\t\t})\n\n\t\ttest('atomic op operands are NOT pipe-validated', () => {\n\t\t\tconst ops = validateUpdateOps(UpdateSchema, [new IncOp('views', -999)])\n\t\t\texpect(ops).toHaveLength(2)\n\t\t})\n\n\t\ttest('auto-bumped SetOp values are pipe-validated', () => {\n\t\t\tconst BadSchema = Schema.from('bad')\n\t\t\t\t.pk('id', v.string(), () => 'auto')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.field('counter', v.number(), { onUpdate: () => 'not-a-number' as any })\n\t\t\t\t.build()\n\t\t\texpect(() =>\n\t\t\t\tvalidateUpdateOps(BadSchema, [new SetOp({ name: 'test' })]),\n\t\t\t).toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('collects all conflicts in one error', () => {\n\t\t\ttry {\n\t\t\t\tvalidateUpdateOps(UpdateSchema, [\n\t\t\t\t\tnew SetOp({ views: 0, name: 'A' }),\n\t\t\t\t\tnew IncOp('views', 1),\n\t\t\t\t\tnew SetOp({ name: 'B' }),\n\t\t\t\t])\n\t\t\t} catch (e) {\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('conflicting-ops')\n\t\t\t\texpect(err.failures.length).toBe(2)\n\t\t\t}\n\t\t})\n\t})\n\n\tdescribe('composeSchemaConfig', () => {\n\t\tconst TestSchema = Schema.from('cfg_test')\n\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t.build()\n\n\t\tconst tablePipe = v.object({ table: v.string() })\n\n\t\ttest('composes base resolver output and validates against pipe', () => {\n\t\t\tconst result = composeSchemaConfig(\n\t\t\t\t() => ({ table: 'users' }),\n\t\t\t\t[],\n\t\t\t\tTestSchema,\n\t\t\t\ttablePipe,\n\t\t\t)\n\t\t\texpect(result).toEqual({ table: 'users' })\n\t\t})\n\n\t\ttest('applies transforms in order before validation', () => {\n\t\t\tconst result = composeSchemaConfig(\n\t\t\t\t() => ({ table: 'users' }),\n\t\t\t\t[\n\t\t\t\t\t(cfg: any) => ({ ...cfg, table: `a_${cfg.table}` }),\n\t\t\t\t\t(cfg: any) => ({ ...cfg, table: `b_${cfg.table}` }),\n\t\t\t\t],\n\t\t\t\tTestSchema,\n\t\t\t\ttablePipe,\n\t\t\t)\n\t\t\texpect(result).toEqual({ table: 'b_a_users' })\n\t\t})\n\n\t\ttest('throws OrmValidationError when composed config fails pipe validation', () => {\n\t\t\ttry {\n\t\t\t\tcomposeSchemaConfig(\n\t\t\t\t\t() => ({ table: 123 }),\n\t\t\t\t\t[],\n\t\t\t\t\tTestSchema,\n\t\t\t\t\ttablePipe,\n\t\t\t\t)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\texpect(err.schema).toBe('cfg_test')\n\t\t\t\texpect(err.operation).toBe('schemaConfig')\n\t\t\t\texpect(err.failures).toHaveLength(1)\n\t\t\t}\n\t\t})\n\n\t\ttest('throws OrmValidationError when transform corrupts valid base config', () => {\n\t\t\ttry {\n\t\t\t\tcomposeSchemaConfig(\n\t\t\t\t\t() => ({ table: 'users' }),\n\t\t\t\t\t[(cfg: any) => ({ ...cfg, table: 42 })],\n\t\t\t\t\tTestSchema,\n\t\t\t\t\ttablePipe,\n\t\t\t\t)\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as OrmValidationError\n\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\texpect(err.operation).toBe('schemaConfig')\n\t\t\t}\n\t\t})\n\n\t\ttest('returns typed effective config on success', () => {\n\t\t\tconst pipe = v.object({ table: v.string(), prefix: v.optional(v.string()) })\n\t\t\tconst result = composeSchemaConfig(\n\t\t\t\t() => ({ table: 'orders' }),\n\t\t\t\t[(cfg: any) => ({ ...cfg, prefix: 'tenant1' })],\n\t\t\t\tTestSchema,\n\t\t\t\tpipe,\n\t\t\t)\n\t\t\texpect(result).toEqual({ table: 'orders', prefix: 'tenant1' })\n\t\t})\n\t})\n}\n","import type { PipeInput, PipeOutput } from 'valleyed'\n\nimport type { AnyField, AnySchemaField, SchemaField } from './fields'\nimport { toFieldName } from './fields'\nimport type { AnySchema, SchemaFields } from './schema'\nimport type { Prettify } from './utils'\n\nexport type NumericFieldOf<S extends AnySchema> = {\n\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] extends SchemaField<any, infer P, any>\n\t\t? PipeOutput<P> extends number\n\t\t\t? SchemaFields<S>[K]\n\t\t\t: never\n\t\t: never\n}[keyof SchemaFields<S>]\n\nexport type ComparableFieldOf<S extends AnySchema> = {\n\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] extends SchemaField<any, infer P, any>\n\t\t? PipeOutput<P> extends number | string | Date\n\t\t\t? SchemaFields<S>[K]\n\t\t\t: never\n\t\t: never\n}[keyof SchemaFields<S>]\n\nexport type OptionalFieldOf<S extends AnySchema> = {\n\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] extends SchemaField<any, infer P, any>\n\t\t? undefined extends PipeOutput<P>\n\t\t\t? SchemaFields<S>[K]\n\t\t\t: never\n\t\t: never\n}[keyof SchemaFields<S>]\n\nexport type ArrayFieldOf<S extends AnySchema> = {\n\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] extends SchemaField<any, infer P, any>\n\t\t? PipeOutput<P> extends readonly any[]\n\t\t\t? SchemaFields<S>[K]\n\t\t\t: never\n\t\t: never\n}[keyof SchemaFields<S>]\n\nexport type ObjectFieldOf<S extends AnySchema> = {\n\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] extends SchemaField<any, infer P, any>\n\t\t? PipeOutput<P> extends Record<string, any>\n\t\t\t? PipeOutput<P> extends readonly any[]\n\t\t\t\t? never\n\t\t\t\t: SchemaFields<S>[K]\n\t\t\t: never\n\t\t: never\n}[keyof SchemaFields<S>]\n\nexport type SetValues<S extends AnySchema> = Prettify<\n\tPartial<{\n\t\t[K in keyof SchemaFields<S>]: SchemaFields<S>[K] extends AnySchemaField ? PipeInput<SchemaFields<S>[K]['pipe']> : never\n\t}>\n>\n\nexport class SetOp {\n\treadonly kind = 'set' as const\n\tconstructor(readonly values: Record<string, unknown>) {}\n}\nexport class IncOp {\n\treadonly kind = 'inc' as const\n\tconstructor(\n\t\treadonly field: string,\n\t\treadonly value: number,\n\t) {}\n}\nexport class MulOp {\n\treadonly kind = 'mul' as const\n\tconstructor(\n\t\treadonly field: string,\n\t\treadonly value: number,\n\t) {}\n}\nexport class MinOp<T = unknown> {\n\treadonly kind = 'min' as const\n\tconstructor(\n\t\treadonly field: string,\n\t\treadonly value: T,\n\t) {}\n}\nexport class MaxOp<T = unknown> {\n\treadonly kind = 'max' as const\n\tconstructor(\n\t\treadonly field: string,\n\t\treadonly value: T,\n\t) {}\n}\nexport class UnsetOp {\n\treadonly kind = 'unset' as const\n\tconstructor(readonly field: string) {}\n}\nexport class PushOp<T = unknown> {\n\treadonly kind = 'push' as const\n\tconstructor(\n\t\treadonly field: string,\n\t\treadonly value: T,\n\t) {}\n}\nexport class PullOp<T = unknown> {\n\treadonly kind = 'pull' as const\n\tconstructor(\n\t\treadonly field: string,\n\t\treadonly value: T,\n\t) {}\n}\nexport class PatchOp<T = unknown> {\n\treadonly kind = 'patch' as const\n\tconstructor(\n\t\treadonly field: string,\n\t\treadonly value: T,\n\t) {}\n}\n\nexport type AnyUpdateOp = SetOp | IncOp | MulOp | MinOp | MaxOp | UnsetOp | PushOp | PullOp | PatchOp\n\nexport function isUpdateOp(v: unknown): v is AnyUpdateOp {\n\treturn (\n\t\tv instanceof SetOp ||\n\t\tv instanceof IncOp ||\n\t\tv instanceof MulOp ||\n\t\tv instanceof MinOp ||\n\t\tv instanceof MaxOp ||\n\t\tv instanceof UnsetOp ||\n\t\tv instanceof PushOp ||\n\t\tv instanceof PullOp ||\n\t\tv instanceof PatchOp\n\t)\n}\n\nexport type HasOp<A, Op extends string> = A extends { updateOps: readonly (infer U)[] } ? (Op extends U ? true : false) : false\n\nexport type UpdateOp<_S extends AnySchema, A> =\n\t| (HasOp<A, 'set'> extends true ? SetOp : never)\n\t| (HasOp<A, 'inc'> extends true ? IncOp : never)\n\t| (HasOp<A, 'mul'> extends true ? MulOp : never)\n\t| (HasOp<A, 'min'> extends true ? MinOp : never)\n\t| (HasOp<A, 'max'> extends true ? MaxOp : never)\n\t| (HasOp<A, 'unset'> extends true ? UnsetOp : never)\n\t| (HasOp<A, 'push'> extends true ? PushOp : never)\n\t| (HasOp<A, 'pull'> extends true ? PullOp : never)\n\t| (HasOp<A, 'patch'> extends true ? PatchOp : never)\n\nexport function set<S extends AnySchema>(values: SetValues<S>): SetOp {\n\treturn new SetOp(values as Record<string, unknown>)\n}\n\nexport function inc<S extends AnySchema>(field: NumericFieldOf<S>, value: number): IncOp {\n\treturn new IncOp(toFieldName(field as AnyField), value)\n}\n\nexport function mul<S extends AnySchema>(field: NumericFieldOf<S>, value: number): MulOp {\n\treturn new MulOp(toFieldName(field as AnyField), value)\n}\n\nexport function min<S extends AnySchema>(field: ComparableFieldOf<S>, value: unknown): MinOp {\n\treturn new MinOp(toFieldName(field as AnyField), value)\n}\n\nexport function max<S extends AnySchema>(field: ComparableFieldOf<S>, value: unknown): MaxOp {\n\treturn new MaxOp(toFieldName(field as AnyField), value)\n}\n\nexport function unset<S extends AnySchema>(field: OptionalFieldOf<S>): UnsetOp {\n\treturn new UnsetOp(toFieldName(field as AnyField))\n}\n\nexport function push<S extends AnySchema>(field: ArrayFieldOf<S>, value: unknown): PushOp {\n\treturn new PushOp(toFieldName(field as AnyField), value)\n}\n\nexport function pull<S extends AnySchema>(field: ArrayFieldOf<S>, value: unknown): PullOp {\n\treturn new PullOp(toFieldName(field as AnyField), value)\n}\n\nexport function patch<S extends AnySchema>(field: ObjectFieldOf<S>, value: Record<string, unknown>): PatchOp {\n\treturn new PatchOp(toFieldName(field as AnyField), value)\n}\n\nexport function opTouchedFields(op: AnyUpdateOp): string[] {\n\tif (op instanceof SetOp) return Object.keys(op.values)\n\treturn [op.field]\n}\n\nexport function flattenOps(ops: AnyUpdateOp[]): Record<string, unknown> {\n\tconst data: Record<string, unknown> = {}\n\tfor (const op of ops) {\n\t\tif (op instanceof SetOp) Object.assign(data, op.values)\n\t\telse data[op.field] = op\n\t}\n\treturn data\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { Schema } = await import('./schema')\n\n\tconst TestSchema = Schema.from('test')\n\t\t.pk('id', v.string(), () => 'x')\n\t\t.field('name', v.string())\n\t\t.field('age', v.number())\n\t\t.field('score', v.optional(v.number()))\n\t\t.field('tags', v.array(v.string()))\n\t\t.field('meta', v.object({ a: v.number() }))\n\t\t.field('createdAt', v.number(), { onCreate: () => 1000 })\n\t\t.field('updatedAt', v.number(), { onCreate: () => 1000, onUpdate: () => 2000 })\n\t\t.build()\n\n\tdescribe('op classes', () => {\n\t\ttest('SetOp has kind \"set\" and stores values', () => {\n\t\t\tconst op = new SetOp({ name: 'Alice' })\n\t\t\texpect(op.kind).toBe('set')\n\t\t\texpect(op.values).toEqual({ name: 'Alice' })\n\t\t})\n\t\ttest('IncOp has kind \"inc\" and stores field + value', () => {\n\t\t\tconst op = new IncOp('age', 5)\n\t\t\texpect(op.kind).toBe('inc')\n\t\t\texpect(op.field).toBe('age')\n\t\t\texpect(op.value).toBe(5)\n\t\t})\n\t\ttest('MulOp has kind \"mul\" and stores field + value', () => {\n\t\t\tconst op = new MulOp('score', 3)\n\t\t\texpect(op.kind).toBe('mul')\n\t\t\texpect(op.field).toBe('score')\n\t\t\texpect(op.value).toBe(3)\n\t\t})\n\t\ttest('MinOp has kind \"min\"', () => {\n\t\t\texpect(new MinOp('age', 10).kind).toBe('min')\n\t\t})\n\t\ttest('MaxOp has kind \"max\"', () => {\n\t\t\texpect(new MaxOp('age', 99).kind).toBe('max')\n\t\t})\n\t\ttest('UnsetOp has kind \"unset\"', () => {\n\t\t\texpect(new UnsetOp('score').kind).toBe('unset')\n\t\t})\n\t\ttest('PushOp has kind \"push\"', () => {\n\t\t\texpect(new PushOp('tags', 'x').kind).toBe('push')\n\t\t})\n\t\ttest('PullOp has kind \"pull\"', () => {\n\t\t\texpect(new PullOp('tags', 'x').kind).toBe('pull')\n\t\t})\n\t\ttest('PatchOp has kind \"patch\"', () => {\n\t\t\texpect(new PatchOp('meta', { a: 9 }).kind).toBe('patch')\n\t\t})\n\t})\n\n\tdescribe('isUpdateOp', () => {\n\t\ttest('returns true for every op class', () => {\n\t\t\texpect(isUpdateOp(new SetOp({}))).toBe(true)\n\t\t\texpect(isUpdateOp(new IncOp('f', 1))).toBe(true)\n\t\t\texpect(isUpdateOp(new MulOp('f', 2))).toBe(true)\n\t\t\texpect(isUpdateOp(new MinOp('f', 0))).toBe(true)\n\t\t\texpect(isUpdateOp(new MaxOp('f', 10))).toBe(true)\n\t\t\texpect(isUpdateOp(new UnsetOp('f'))).toBe(true)\n\t\t\texpect(isUpdateOp(new PushOp('f', 'v'))).toBe(true)\n\t\t\texpect(isUpdateOp(new PullOp('f', 'v'))).toBe(true)\n\t\t\texpect(isUpdateOp(new PatchOp('f', {}))).toBe(true)\n\t\t})\n\t\ttest('returns false for non-op values', () => {\n\t\t\texpect(isUpdateOp('string')).toBe(false)\n\t\t\texpect(isUpdateOp(42)).toBe(false)\n\t\t\texpect(isUpdateOp(null)).toBe(false)\n\t\t\texpect(isUpdateOp({})).toBe(false)\n\t\t})\n\t})\n\n\tdescribe('op helper functions', () => {\n\t\ttest('set() creates SetOp with values', () => {\n\t\t\tconst op = set<typeof TestSchema>({ name: 'Alice', age: 30 })\n\t\t\texpect(op).toBeInstanceOf(SetOp)\n\t\t\texpect(op.kind).toBe('set')\n\t\t\texpect(op.values).toEqual({ name: 'Alice', age: 30 })\n\t\t})\n\t\ttest('inc() creates IncOp from field ref', () => {\n\t\t\tconst op = inc<typeof TestSchema>(TestSchema.fields.age, 5)\n\t\t\texpect(op).toBeInstanceOf(IncOp)\n\t\t\texpect(op.field).toBe('age')\n\t\t\texpect(op.value).toBe(5)\n\t\t})\n\t\ttest('mul() creates MulOp from field ref', () => {\n\t\t\tconst op = mul<typeof TestSchema>(TestSchema.fields.age, 3)\n\t\t\texpect(op).toBeInstanceOf(MulOp)\n\t\t\texpect(op.field).toBe('age')\n\t\t\texpect(op.value).toBe(3)\n\t\t})\n\t\ttest('min() creates MinOp from field ref', () => {\n\t\t\tconst op = min<typeof TestSchema>(TestSchema.fields.age, 10)\n\t\t\texpect(op).toBeInstanceOf(MinOp)\n\t\t\texpect(op.field).toBe('age')\n\t\t})\n\t\ttest('max() creates MaxOp from field ref', () => {\n\t\t\tconst op = max<typeof TestSchema>(TestSchema.fields.age, 99)\n\t\t\texpect(op).toBeInstanceOf(MaxOp)\n\t\t\texpect(op.field).toBe('age')\n\t\t})\n\t\ttest('unset() creates UnsetOp from field ref', () => {\n\t\t\tconst op = unset<typeof TestSchema>(TestSchema.fields.score)\n\t\t\texpect(op).toBeInstanceOf(UnsetOp)\n\t\t\texpect(op.field).toBe('score')\n\t\t})\n\t\ttest('push() creates PushOp from field ref', () => {\n\t\t\tconst op = push<typeof TestSchema>(TestSchema.fields.tags, 'new-tag')\n\t\t\texpect(op).toBeInstanceOf(PushOp)\n\t\t\texpect(op.field).toBe('tags')\n\t\t\texpect(op.value).toBe('new-tag')\n\t\t})\n\t\ttest('pull() creates PullOp from field ref', () => {\n\t\t\tconst op = pull<typeof TestSchema>(TestSchema.fields.tags, 'old-tag')\n\t\t\texpect(op).toBeInstanceOf(PullOp)\n\t\t\texpect(op.field).toBe('tags')\n\t\t})\n\t\ttest('patch() creates PatchOp from field ref', () => {\n\t\t\tconst op = patch<typeof TestSchema>(TestSchema.fields.meta, { a: 9 })\n\t\t\texpect(op).toBeInstanceOf(PatchOp)\n\t\t\texpect(op.field).toBe('meta')\n\t\t\texpect(op.value).toEqual({ a: 9 })\n\t\t})\n\t})\n\n\tdescribe('opTouchedFields', () => {\n\t\ttest('SetOp returns all keys from values', () => {\n\t\t\texpect(opTouchedFields(new SetOp({ name: 'A', age: 1 }))).toEqual(['name', 'age'])\n\t\t})\n\t\ttest('IncOp returns the field', () => {\n\t\t\texpect(opTouchedFields(new IncOp('age', 1))).toEqual(['age'])\n\t\t})\n\t\ttest('UnsetOp returns the field', () => {\n\t\t\texpect(opTouchedFields(new UnsetOp('score'))).toEqual(['score'])\n\t\t})\n\t})\n\n\tdescribe('type-level: field-category helpers', () => {\n\t\ttest('NumericFieldOf only matches numeric fields (excludes optional numeric)', () => {\n\t\t\ttype Numeric = NumericFieldOf<typeof TestSchema>\n\t\t\texpectTypeOf<Numeric['name']>().toEqualTypeOf<'age' | 'createdAt' | 'updatedAt'>()\n\t\t})\n\t\ttest('ComparableFieldOf matches numeric and string fields (excludes optional)', () => {\n\t\t\ttype Comparable = ComparableFieldOf<typeof TestSchema>\n\t\t\texpectTypeOf<Comparable['name']>().toEqualTypeOf<'id' | 'name' | 'age' | 'createdAt' | 'updatedAt'>()\n\t\t})\n\t\ttest('OptionalFieldOf only matches optional fields', () => {\n\t\t\ttype Optional = OptionalFieldOf<typeof TestSchema>\n\t\t\texpectTypeOf<Optional['name']>().toEqualTypeOf<'score'>()\n\t\t})\n\t\ttest('ArrayFieldOf only matches array fields', () => {\n\t\t\ttype Arr = ArrayFieldOf<typeof TestSchema>\n\t\t\texpectTypeOf<Arr['name']>().toEqualTypeOf<'tags'>()\n\t\t})\n\t\ttest('ObjectFieldOf only matches object fields (not arrays)', () => {\n\t\t\ttype Obj = ObjectFieldOf<typeof TestSchema>\n\t\t\texpectTypeOf<Obj['name']>().toEqualTypeOf<'meta'>()\n\t\t})\n\t})\n\n\tdescribe('type-level: inc on a string field is a TS error', () => {\n\t\ttest('inc rejects non-numeric fields', () => {\n\t\t\t// @ts-expect-error — name is a string field, not numeric\n\t\t\tinc<typeof TestSchema>(TestSchema.fields.name, 1)\n\t\t})\n\t})\n\n\tdescribe('type-level: push on a non-array field is a TS error', () => {\n\t\ttest('push rejects non-array fields', () => {\n\t\t\t// @ts-expect-error — name is a string field, not array\n\t\t\tpush<typeof TestSchema>(TestSchema.fields.name, 'val')\n\t\t})\n\t})\n\n\tdescribe('type-level: per-op gating via UpdateOp<S, A>', () => {\n\t\ttest('undeclared ops resolve to never', async () => {\n\t\t\tconst { OrmAdapter } = await import('./orm-adapter')\n\t\t\tclass LimitedAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly updateOps = ['set', 'inc'] as const\n\t\t\t}\n\n\t\t\ttype Limited = UpdateOp<typeof TestSchema, LimitedAdapter>\n\t\t\texpectTypeOf<Limited>().toEqualTypeOf<SetOp | IncOp>()\n\t\t})\n\n\t\ttest('adapter with no updateOps resolves all variants to never', async () => {\n\t\t\tconst { OrmAdapter } = await import('./orm-adapter')\n\t\t\tclass NoOpsAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly updateOps = [] as const\n\t\t\t}\n\n\t\t\ttype NoOps = UpdateOp<typeof TestSchema, NoOpsAdapter>\n\t\t\texpectTypeOf<NoOps>().toBeNever()\n\t\t})\n\n\t\ttest('adapter with all updateOps includes all variants', async () => {\n\t\t\tconst { OrmAdapter } = await import('./orm-adapter')\n\t\t\tclass FullAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly updateOps = ['set', 'inc', 'mul', 'min', 'max', 'unset', 'push', 'pull', 'patch'] as const\n\t\t\t}\n\n\t\t\ttype Full = UpdateOp<typeof TestSchema, FullAdapter>\n\t\t\ttype Expected =\n\t\t\t\t| SetOp\n\t\t\t\t| IncOp\n\t\t\t\t| MulOp\n\t\t\t\t| MinOp\n\t\t\t\t| MaxOp\n\t\t\t\t| UnsetOp\n\t\t\t\t| PushOp\n\t\t\t\t| PullOp\n\t\t\t\t| PatchOp\n\t\t\texpectTypeOf<Full>().toEqualTypeOf<Expected>()\n\t\t})\n\t})\n}\n","import { planSelection } from './computeds'\nimport { assertNormalisedFindReadShape, normaliseAllFindReadShape, normaliseAllIterateReadShape, normaliseAllPaginateReadShape, type ReadLimitSource, type ReadOffsetSource } from './query-shape'\nimport type { Paginated, SelectedWithPreloads } from './types'\nimport { assertNormalisedAggregate, assertNormalisedFilter, type FilterGroup } from '../../filter'\nimport type { AggregateSpec } from '../../orm-adapter'\nimport type { IterationOptions, OrderBy } from '../../query-options'\nimport type { AnyPreloadDef } from '../../relations'\nimport type { AnySchema } from '../../schema'\nimport { validateCreate, validateCreateMany, validateUpdate, validateUpsertConflicts, type SchemaCreateInput, type SchemaUpdateInput } from '../../schema-validations'\nimport { SetOp, isUpdateOp, type AnyUpdateOp } from '../../updates'\nimport type { SchemaContext, UpsertInput } from '../builders'\n\nexport async function runOneRead<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: {\n\t\twhere: FilterGroup\n\t\tselect: readonly Sel[] | undefined\n\t\tpreloads: P\n\t},\n): Promise<SelectedWithPreloads<S, Sel, P> | null> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tassertNormalisedFindReadShape(context.schema, 'findOne', state)\n\tconst row = await context.use.findOne(state.where)\n\tif (!row) return null\n\treturn context.shapeOneRow(state.select, state.preloads, row)\n}\n\nexport async function runAllRead<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: {\n\t\twhere: FilterGroup\n\t\tselect: readonly Sel[] | undefined\n\t\tpreloads: P\n\t\torderBy: readonly OrderBy[]\n\t\tlimitSource?: ReadLimitSource\n\t\toffsetSource?: ReadOffsetSource\n\t},\n): Promise<SelectedWithPreloads<S, Sel, P>[]> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst query = normaliseAllFindReadShape(context.schema, 'findMany', state)\n\tconst plan = planSelection(context.schema, state.select as readonly string[] | undefined)\n\tconst rows = await context.use.findMany(state.where, {\n\t\tselect: plan.adapterSelect,\n\t\torderBy: [...state.orderBy],\n\t\tlimit: query.limit,\n\t\toffset: query.offset,\n\t})\n\treturn context.shapeRows(state.select, state.preloads, rows)\n}\n\nexport async function runAllCount<S extends AnySchema>(\n\tcontext: SchemaContext<S>,\n\tstate: { where: FilterGroup },\n): Promise<number> {\n\tassertNormalisedFilter(context.schema, state.where)\n\treturn context.use.count(state.where)\n}\n\nfunction toPaginated<T>(items: T[], total: number, limit: number, current: number): Paginated<T> {\n\tconst start = 1\n\tconst last = Math.ceil(total / limit) || 1\n\tconst previous = current <= start || current > last ? null : current - 1\n\tconst next = current >= last ? null : current + 1\n\treturn {\n\t\tpages: { current, start, last, previous, next },\n\t\tdocs: { limit, total, count: items.length },\n\t\titems,\n\t}\n}\n\nexport async function runAllPaginate<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: {\n\t\twhere: FilterGroup\n\t\tselect: readonly Sel[] | undefined\n\t\tpreloads: P\n\t\torderBy: readonly OrderBy[]\n\t\tlimitSource?: ReadLimitSource\n\t\toffsetSource?: ReadOffsetSource\n\t},\n): Promise<Paginated<SelectedWithPreloads<S, Sel, P>>> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst query = normaliseAllPaginateReadShape(context.schema, 'paginate', state)\n\tconst plan = planSelection(context.schema, state.select as readonly string[] | undefined)\n\tconst use = context.use\n\tconst [rows, total] = await Promise.all([\n\t\tuse.findMany(state.where, {\n\t\t\tselect: plan.adapterSelect,\n\t\t\torderBy: [...state.orderBy],\n\t\t\tlimit: query.limit,\n\t\t\toffset: query.offset,\n\t\t}),\n\t\tuse.count(state.where),\n\t])\n\tconst items = await context.shapeRows(state.select, state.preloads, rows)\n\treturn toPaginated(items, total, query.limit, query.current)\n}\n\nexport async function* runAllIterate<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: {\n\t\twhere: FilterGroup\n\t\tselect: readonly Sel[] | undefined\n\t\tpreloads: P\n\t\torderBy: readonly OrderBy[]\n\t\tlimitSource?: ReadLimitSource\n\t\toffsetSource?: ReadOffsetSource\n\t},\n\toptions?: IterationOptions,\n): AsyncGenerator<SelectedWithPreloads<S, Sel, P>, void, void> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst query = normaliseAllIterateReadShape(context.schema, 'iterate', state, options)\n\tconst plan = planSelection(context.schema, state.select as readonly string[] | undefined)\n\n\tfor await (const row of context.use.iterateMany(state.where, {\n\t\tselect: plan.adapterSelect,\n\t\torderBy: [...state.orderBy],\n\t\tlimit: query.limit,\n\t\toffset: query.offset,\n\t\t...(query.batchSize === undefined ? {} : { batchSize: query.batchSize }),\n\t})) {\n\t\tconst shaped = await context.shapeRows(state.select, state.preloads, [row])\n\t\tconst first = shaped[0]\n\t\tif (first) yield first\n\t}\n}\n\nexport async function runOneCreate<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: { select: readonly Sel[] | undefined; preloads: P },\n\tdata: SchemaCreateInput<S>,\n): Promise<SelectedWithPreloads<S, Sel, P>> {\n\tconst validated = validateCreate(context.schema, data as any)\n\tconst row = await context.use.createOne(validated as any)\n\tconst [resolved] = await context.shapeRows(state.select, state.preloads, [row])\n\treturn resolved\n}\n\nexport async function runAllCreate<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: { select: readonly Sel[] | undefined; preloads: P },\n\tdata: SchemaCreateInput<S>[],\n): Promise<SelectedWithPreloads<S, Sel, P>[]> {\n\tconst validated = validateCreateMany(context.schema, data as any)\n\tconst rows = await context.use.createMany(validated as any)\n\treturn context.shapeRows(state.select, state.preloads, rows)\n}\n\nexport async function runOneUpdate<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: { where: FilterGroup; select: readonly Sel[] | undefined; preloads: P },\n\tdata: SchemaUpdateInput<S>,\n): Promise<SelectedWithPreloads<S, Sel, P> | null> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst validated = validateUpdate(context.schema, data as any)\n\tconst row = await context.use.updateOne(state.where, validated as any)\n\treturn context.shapeOneRow(state.select, state.preloads, row)\n}\n\nexport async function runAllUpdate<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: { where: FilterGroup; select: readonly Sel[] | undefined; preloads: P },\n\tdata: SchemaUpdateInput<S>,\n): Promise<SelectedWithPreloads<S, Sel, P>[]> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst validated = validateUpdate(context.schema, data as any)\n\tconst rows = await context.use.updateMany(state.where, validated as any)\n\treturn context.shapeRows(state.select, state.preloads, rows)\n}\n\nexport async function runOneUpsert<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: { where: FilterGroup; select: readonly Sel[] | undefined; preloads: P },\n\tdata: UpsertInput<S>,\n): Promise<SelectedWithPreloads<S, Sel, P>> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst create = validateCreate(context.schema, data.create as any)\n\tconst ops: AnyUpdateOp[] = []\n\tif ('update' in data) {\n\t\tconst validated = validateUpdate(context.schema, data.update as any)\n\t\tconst plainValues: Record<string, unknown> = {}\n\t\tfor (const [key, value] of Object.entries(validated)) {\n\t\t\tif (isUpdateOp(value)) {\n\t\t\t\tops.push(value)\n\t\t\t} else {\n\t\t\t\tplainValues[key] = value\n\t\t\t}\n\t\t}\n\t\tif (Object.keys(plainValues).length > 0) {\n\t\t\tops.unshift(new SetOp(plainValues))\n\t\t}\n\t}\n\tif (ops.length > 0) {\n\t\tvalidateUpsertConflicts(context.schema, data.create as Record<string, unknown>, ops)\n\t}\n\tconst row = await context.use.upsertOne(state.where, create as any, ops)\n\treturn (await context.shapeOneRow(state.select, state.preloads, row)) as SelectedWithPreloads<S, Sel, P>\n}\n\nexport async function runOneDelete<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: { where: FilterGroup; select: readonly Sel[] | undefined; preloads: P },\n): Promise<SelectedWithPreloads<S, Sel, P> | null> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst row = await context.use.deleteOne(state.where)\n\treturn context.shapeOneRow(state.select, state.preloads, row)\n}\n\nexport async function runAllDelete<S extends AnySchema, Sel extends string, P extends readonly AnyPreloadDef[]>(\n\tcontext: SchemaContext<S>,\n\tstate: { where: FilterGroup; select: readonly Sel[] | undefined; preloads: P },\n): Promise<SelectedWithPreloads<S, Sel, P>[]> {\n\tassertNormalisedFilter(context.schema, state.where)\n\tconst rows = await context.use.deleteMany(state.where)\n\treturn context.shapeRows(state.select, state.preloads, rows)\n}\n\nexport async function runAggregate<S extends AnySchema>(\n\tcontext: SchemaContext<S>,\n\tspec: AggregateSpec,\n): Promise<Array<Record<string, unknown>>> {\n\tassertNormalisedAggregate(context.schema, context.use, spec)\n\treturn context.use.aggregate(spec)\n}\n","import { EquippedError } from '../../../errors'\nimport type { OrmUse } from '../../adapters/base'\nimport { FilterGroup } from '../../filter'\nimport type { AnyPreloadDef, AnyRelDef, NestedPreloadDef } from '../../relations'\nimport { ManyRelation, OneRelation } from '../../relations'\nimport type { AnySchema } from '../../schema'\n\nconst MAX_PRELOAD_DEPTH = 5\n\ntype ResolvedPreloadDef = {\n\tdef: AnyRelDef\n\tpreloads: ResolvedPreloadDef[]\n}\n\nfunction isNestedPreloadDef(def: AnyPreloadDef): def is NestedPreloadDef {\n\treturn typeof def === 'object' && def != null && 'def' in def\n}\n\nfunction relationStep(def: AnyRelDef) {\n\treturn `${def.source.name}.${def.name}->${def.target.name}`\n}\n\nfunction uniqueDefinedValues(entities: readonly Record<string, unknown>[], key: string) {\n\treturn [...new Set(entities.map((entity) => entity[key]).filter((value) => value != null))]\n}\n\nfunction attachOneRelation(\n\tentities: readonly Record<string, unknown>[],\n\tname: string,\n\tlookupKey: string,\n\tlookup: ReadonlyMap<unknown, Record<string, unknown>>,\n) {\n\treturn entities.map((entity) => ({ ...entity, [name]: lookup.get(entity[lookupKey]) ?? null }))\n}\n\nfunction attachManyRelation(\n\tentities: readonly Record<string, unknown>[],\n\tname: string,\n\tlookupKey: string,\n\tlookup: ReadonlyMap<unknown, Record<string, unknown>[]>,\n) {\n\treturn entities.map((entity) => ({ ...entity, [name]: lookup.get(entity[lookupKey]) ?? [] }))\n}\n\nfunction normalizePreloads(defs: readonly AnyPreloadDef[]): ResolvedPreloadDef[] {\n\treturn defs.map((def) => {\n\t\tif (def instanceof ManyRelation || def instanceof OneRelation) {\n\t\t\treturn { def, preloads: [] }\n\t\t}\n\n\t\tif (!isNestedPreloadDef(def) || !(def.def instanceof ManyRelation || def.def instanceof OneRelation)) {\n\t\t\tthrow new Error('Invalid preload definition: nested preloads must include a relation definition in `def`')\n\t\t}\n\n\t\treturn {\n\t\t\tdef: def.def,\n\t\t\tpreloads: normalizePreloads(def.preloads ?? []),\n\t\t}\n\t})\n}\n\nexport async function resolvePreloads<T extends Record<string, unknown>>(\n\tentities: T[],\n\tdefs: readonly AnyPreloadDef[],\n\tgetUse: (s: AnySchema) => OrmUse,\n) {\n\treturn resolvePreloadNodes(entities, normalizePreloads(defs), getUse, 1, []) as unknown as T[]\n}\n\nasync function resolvePreloadNodes(\n\tentities: Record<string, unknown>[],\n\tdefs: readonly ResolvedPreloadDef[],\n\tgetUse: (s: AnySchema) => OrmUse,\n\tdepth: number,\n\tpath: readonly string[],\n) {\n\tfor (const def of defs) entities = await resolvePreload(entities, def, getUse, depth, path)\n\treturn entities\n}\n\nasync function resolvePreload(\n\tentities: Record<string, unknown>[],\n\tnode: ResolvedPreloadDef,\n\tgetUse: (s: AnySchema) => OrmUse,\n\tdepth: number,\n\tpath: readonly string[],\n): Promise<Record<string, unknown>[]> {\n\tif (depth > MAX_PRELOAD_DEPTH) {\n\t\tthrow new EquippedError(`Preload depth exceeded max depth ${MAX_PRELOAD_DEPTH}`, {\n\t\t\toperation: 'resolvePreload',\n\t\t\tdepth,\n\t\t\tmaxDepth: MAX_PRELOAD_DEPTH,\n\t\t})\n\t}\n\n\tconst { def } = node\n\tconst { target, name } = def\n\tconst step = relationStep(def)\n\tif (path.includes(step)) {\n\t\tthrow new EquippedError(`Preload cycle detected: ${[...path, step].join(' -> ')}`, {\n\t\t\toperation: 'resolvePreload',\n\t\t\tpath,\n\t\t\tstep,\n\t\t})\n\t}\n\tconst nextPath = [...path, step]\n\n\tif (def instanceof OneRelation) {\n\t\tif (def.fkOwner === 'source') {\n\t\t\tconst refCol = def.references.name\n\t\t\tconst fkValues = uniqueDefinedValues(entities, def.foreignKey.name)\n\t\t\tif (fkValues.length === 0) return entities.map((e) => ({ ...e, [name]: null }))\n\n\t\t\tlet related = await getUse(target).findMany(FilterGroup.create().in(refCol, fkValues))\n\t\t\tif (node.preloads.length > 0 && related.length > 0) {\n\t\t\t\trelated = await resolvePreloadNodes(related, node.preloads, getUse, depth + 1, nextPath)\n\t\t\t}\n\t\t\tconst lookup = new Map(related.map((r) => [r[refCol], r]))\n\t\t\treturn attachOneRelation(entities, name, def.foreignKey.name, lookup)\n\t\t}\n\n\t\tconst refCol = def.references.name\n\t\tconst refValues = uniqueDefinedValues(entities, refCol)\n\t\tif (refValues.length === 0) return entities.map((e) => ({ ...e, [name]: null }))\n\n\t\tlet related = await getUse(target).findMany(FilterGroup.create().in(def.foreignKey, refValues))\n\t\tif (node.preloads.length > 0 && related.length > 0) {\n\t\t\trelated = await resolvePreloadNodes(related, node.preloads, getUse, depth + 1, nextPath)\n\t\t}\n\t\tconst lookup = new Map(related.map((r) => [r[def.foreignKey.name], r]))\n\t\treturn attachOneRelation(entities, name, refCol, lookup)\n\t}\n\n\tif (def instanceof ManyRelation) {\n\t\tconst refCol = def.references.name\n\t\tconst refValues = uniqueDefinedValues(entities, refCol)\n\t\tif (refValues.length === 0) return entities.map((e) => ({ ...e, [name]: [] }))\n\n\t\tlet related = await getUse(target).findMany(FilterGroup.create().in(def.foreignKey, refValues))\n\t\tif (node.preloads.length > 0 && related.length > 0) {\n\t\t\trelated = await resolvePreloadNodes(related, node.preloads, getUse, depth + 1, nextPath)\n\t\t}\n\n\t\tconst grouped = new Map<unknown, Record<string, unknown>[]>()\n\t\tfor (const r of related) {\n\t\t\tconst fk = r[def.foreignKey.name]\n\t\t\tif (!grouped.has(fk)) grouped.set(fk, [])\n\t\t\tgrouped.get(fk)!.push(r)\n\t\t}\n\t\treturn attachManyRelation(entities, name, refCol, grouped)\n\t}\n\n\tthrow new Error(`Unknown relation kind: ${String(Reflect.get(def as object, 'kind'))}; expected OneRelation or ManyRelation`)\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../../adapters/in-memory')\n\tconst { OrmValidationError } = await import('../../errors')\n\tconst { Relations } = await import('../../relations')\n\tconst { Repo } = await import('../repo')\n\tconst { Schema } = await import('../../schema')\n\n\tdescribe('repo preload resolution', () => {\n\t\tlet userCounter = 0\n\t\tlet postCounter = 0\n\t\tlet profileCounter = 0\n\t\tlet orgCounter = 0\n\t\tlet aCounter = 0\n\t\tlet bCounter = 0\n\t\tlet cCounter = 0\n\t\tlet dCounter = 0\n\t\tlet eCounter = 0\n\t\tlet fCounter = 0\n\t\tlet gCounter = 0\n\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => `u${++userCounter}`)\n\t\t\t.field('email', v.string())\n\t\t\t.field('name', v.string())\n\t\t\t.field('orgId', v.optional(v.string()), { onCreate: () => undefined })\n\t\t\t.build()\n\n\t\tconst PostSchema = Schema.from('posts')\n\t\t\t.pk('id', v.string(), () => `p${++postCounter}`)\n\t\t\t.field('title', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\tconst ProfileSchema = Schema.from('profiles')\n\t\t\t.pk('id', v.string(), () => `pr${++profileCounter}`)\n\t\t\t.field('bio', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\tconst OrgSchema = Schema.from('orgs')\n\t\t\t.pk('id', v.string(), () => `o${++orgCounter}`)\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\n\t\tconst ASchema = Schema.from('as')\n\t\t\t.pk('id', v.string(), () => `a${++aCounter}`)\n\t\t\t.build()\n\t\tconst BSchema = Schema.from('bs')\n\t\t\t.pk('id', v.string(), () => `b${++bCounter}`)\n\t\t\t.field('aId', v.string())\n\t\t\t.build()\n\t\tconst CSchema = Schema.from('cs')\n\t\t\t.pk('id', v.string(), () => `c${++cCounter}`)\n\t\t\t.field('bId', v.string())\n\t\t\t.build()\n\t\tconst DSchema = Schema.from('ds')\n\t\t\t.pk('id', v.string(), () => `d${++dCounter}`)\n\t\t\t.field('cId', v.string())\n\t\t\t.build()\n\t\tconst ESchema = Schema.from('es')\n\t\t\t.pk('id', v.string(), () => `e${++eCounter}`)\n\t\t\t.field('dId', v.string())\n\t\t\t.build()\n\t\tconst FSchema = Schema.from('fs')\n\t\t\t.pk('id', v.string(), () => `f${++fCounter}`)\n\t\t\t.field('eId', v.string())\n\t\t\t.build()\n\t\tconst GSchema = Schema.from('gs')\n\t\t\t.pk('id', v.string(), () => `g${++gCounter}`)\n\t\t\t.field('fId', v.string())\n\t\t\t.build()\n\n\t\tconst UserRels = Relations.from(UserSchema)\n\t\t\t.hasMany('posts', PostSchema.fields.userId)\n\t\t\t.hasOne('profile', ProfileSchema.fields.userId)\n\t\t\t.belongsTo('org', UserSchema.fields.orgId, OrgSchema)\n\t\t\t.build()\n\n\t\tconst PostRels = Relations.from(PostSchema)\n\t\t\t.belongsTo('author', PostSchema.fields.userId, UserSchema)\n\t\t\t.build()\n\n\t\tconst ARels = Relations.from(ASchema).hasMany('bs', BSchema.fields.aId).build()\n\t\tconst BRels = Relations.from(BSchema).hasMany('cs', CSchema.fields.bId).build()\n\t\tconst CRels = Relations.from(CSchema).hasMany('ds', DSchema.fields.cId).build()\n\t\tconst DRels = Relations.from(DSchema).hasMany('es', ESchema.fields.dId).build()\n\t\tconst ERels = Relations.from(ESchema).hasMany('fs', FSchema.fields.eId).build()\n\t\tconst FRels = Relations.from(FSchema).hasMany('gs', GSchema.fields.fId).build()\n\n\t\tfunction makeRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\treturn Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t}\n\n\t\tasync function expectPreloadQueryShapeError(read: Promise<unknown>, cause: RegExp) {\n\t\t\ttry {\n\t\t\t\tawait read\n\t\t\t\texpect.unreachable('preload query-shape validation should have failed')\n\t\t\t} catch (error) {\n\t\t\t\texpect(error).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = error as InstanceType<typeof OrmValidationError>\n\t\t\t\texpect(err.kind).toBe('query-shape')\n\t\t\t\texpect(err.failures.some((failure) => cause.test(String(failure.cause)))).toBe(true)\n\t\t\t}\n\t\t}\n\n\t\ttest('hasMany preload resolves related entities', async () => {\n\t\t\tconst Repo = makeRepo()\n\t\t\tconst user = await Repo.on(UserSchema).one().create({ email: 'u@test.com', name: 'User' })\n\t\t\tawait Repo.on(PostSchema).one().create({ title: 'Post 1', userId: user.id })\n\t\t\tawait Repo.on(PostSchema).one().create({ title: 'Post 2', userId: user.id })\n\n\t\t\tconst users = await Repo.on(UserSchema).all().preload([UserRels.posts]).find()\n\t\t\texpect(users[0].posts).toHaveLength(2)\n\t\t})\n\n\t\ttest('hasOne and belongsTo preloads resolve null and non-null branches', async () => {\n\t\t\tconst Repo = makeRepo()\n\t\t\tconst user = await Repo.on(UserSchema).one().create({ email: 'x@test.com', name: 'X' })\n\t\t\tawait Repo.on(ProfileSchema).one().create({ bio: 'Hello', userId: user.id })\n\n\t\t\tconst users = await Repo.on(UserSchema).all().preload([UserRels.profile]).find()\n\t\t\texpect(users[0].profile?.bio).toBe('Hello')\n\n\t\t\tconst usersWithoutOrg = await Repo.on(UserSchema).all().preload([UserRels.org]).find()\n\t\t\texpect(usersWithoutOrg[0].org).toBeNull()\n\t\t})\n\n\t\ttest('nested preload resolves recursively', async () => {\n\t\t\tconst Repo = makeRepo()\n\t\t\tconst user = await Repo.on(UserSchema).one().create({ email: 'nested@test.com', name: 'Nested User' })\n\t\t\tawait Repo.on(ProfileSchema).one().create({ bio: 'Hello nested', userId: user.id })\n\t\t\tawait Repo.on(PostSchema).one().create({ title: 'Nested Post', userId: user.id })\n\n\t\t\tconst users = await Repo.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.preload([\n\t\t\t\t\t{\n\t\t\t\t\t\tdef: UserRels.posts,\n\t\t\t\t\t\tpreloads: [{ def: PostRels.author, preloads: [UserRels.profile] }],\n\t\t\t\t\t},\n\t\t\t\t])\n\t\t\t\t.find()\n\n\t\t\tconst author = users[0].posts[0].author\n\t\t\tconst profile = author?.profile\n\t\t\texpect(author?.id).toBe(user.id)\n\t\t\texpect(profile?.bio).toBe('Hello nested')\n\t\t})\n\n\t\ttest('cycle detection throws descriptive error', async () => {\n\t\t\tconst Repo = makeRepo()\n\t\t\tconst user = await Repo.on(UserSchema).one().create({ email: 'cycle@test.com', name: 'Cycle User' })\n\t\t\tawait Repo.on(PostSchema).one().create({ title: 'Cycle Post', userId: user.id })\n\n\t\t\tawait expectPreloadQueryShapeError(\n\t\t\t\tRepo.on(UserSchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.preload([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdef: UserRels.posts,\n\t\t\t\t\t\t\tpreloads: [{ def: PostRels.author, preloads: [UserRels.posts] }],\n\t\t\t\t\t\t},\n\t\t\t\t\t])\n\t\t\t\t\t.find(),\n\t\t\t\t/Preload cycle detected/,\n\t\t\t)\n\t\t})\n\n\t\ttest('depth limit throws when chain exceeds max depth', async () => {\n\t\t\tconst Repo = makeRepo()\n\t\t\tconst a = await Repo.on(ASchema).one().create({})\n\t\t\tconst b = await Repo.on(BSchema).one().create({ aId: a.id })\n\t\t\tconst c = await Repo.on(CSchema).one().create({ bId: b.id })\n\t\t\tconst d = await Repo.on(DSchema).one().create({ cId: c.id })\n\t\t\tconst e = await Repo.on(ESchema).one().create({ dId: d.id })\n\t\t\tconst f = await Repo.on(FSchema).one().create({ eId: e.id })\n\t\t\tawait Repo.on(GSchema).one().create({ fId: f.id })\n\n\t\t\tawait expectPreloadQueryShapeError(\n\t\t\t\tRepo.on(ASchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.preload([\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdef: ARels.bs,\n\t\t\t\t\t\t\tpreloads: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tdef: BRels.cs,\n\t\t\t\t\t\t\t\t\tpreloads: [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tdef: CRels.ds,\n\t\t\t\t\t\t\t\t\t\t\tpreloads: [\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tdef: DRels.es,\n\t\t\t\t\t\t\t\t\t\t\t\t\tpreloads: [\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdef: ERels.fs,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpreloads: [FRels.gs],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t])\n\t\t\t\t\t.find(),\n\t\t\t\t/Preload depth exceeded/,\n\t\t\t)\n\t\t})\n\n\t\ttest('invalid nested preload definition throws a validation error', async () => {\n\t\t\tconst Repo = makeRepo()\n\t\t\tawait Repo.on(UserSchema).one().create({ email: 'u@test.com', name: 'User' })\n\n\t\t\tawait expectPreloadQueryShapeError(\n\t\t\t\tRepo.on(UserSchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.preload([{ def: {} as any }])\n\t\t\t\t\t.find(),\n\t\t\t\t/Invalid preload definition/,\n\t\t\t)\n\t\t})\n\n\t\ttest('findOne with preloads resolves relations', async () => {\n\t\t\tconst Repo = makeRepo()\n\t\t\tconst user = await Repo.on(UserSchema).one().create({ email: 'u@test.com', name: 'User' })\n\t\t\tawait Repo.on(PostSchema).one().create({ title: 'Post', userId: user.id })\n\n\t\t\tconst found = await Repo.on(UserSchema).one().id(user.id).preload([UserRels.posts]).find()\n\t\t\texpect(found?.posts).toHaveLength(1)\n\t\t})\n\n\t\ttest('N+1 avoidance: N parents + children loaded in 2 queries, not N+1', async () => {\n\t\t\tconst { vi } = await import('vitest')\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tlet queryCount = 0\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((schema: any, config: any) => {\n\t\t\t\tconst use = origUse(schema, config)\n\t\t\t\treturn {\n\t\t\t\t\t...use,\n\t\t\t\t\tfindMany: async (...args: any[]) => {\n\t\t\t\t\t\tqueryCount++\n\t\t\t\t\t\treturn use.findMany(...(args as [any, any]))\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tconst repo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t\tconst u1 = await repo.on(UserSchema).one().create({ email: 'a@test.com', name: 'A' })\n\t\t\tconst u2 = await repo.on(UserSchema).one().create({ email: 'b@test.com', name: 'B' })\n\t\t\tconst u3 = await repo.on(UserSchema).one().create({ email: 'c@test.com', name: 'C' })\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'P1', userId: u1.id })\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'P2', userId: u1.id })\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'P3', userId: u2.id })\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'P4', userId: u3.id })\n\n\t\t\tqueryCount = 0\n\t\t\tconst users = await repo.on(UserSchema).all().preload([UserRels.posts]).find()\n\n\t\t\texpect(users).toHaveLength(3)\n\t\t\texpect(users.find((u) => u.id === u1.id)!.posts).toHaveLength(2)\n\t\t\texpect(users.find((u) => u.id === u2.id)!.posts).toHaveLength(1)\n\t\t\texpect(users.find((u) => u.id === u3.id)!.posts).toHaveLength(1)\n\t\t\texpect(queryCount).toBe(2)\n\t\t})\n\t})\n}\n","import type { InferRawArgs, InferRawReturn } from '../adapter'\nimport type { OrmUse } from '../adapters/base'\nimport { OrmNotFoundError, type OrmNotFoundOperation } from '../errors'\nimport { toFieldName, type AnyField, type Field } from '../fields'\nimport { FilterGroup, type FilterFactory } from '../filter'\nimport type { AggregateSpec } from '../orm-adapter'\nimport { OrderBy, type IterationOptions } from '../query-options'\nimport type { AnyPreloadDef } from '../relations'\nimport type { AnySchema, SchemaOutput } from '../schema'\nimport type { SchemaCreateInput, SchemaUpdateInput } from '../schema-validations'\nimport { applyComputedSelection, planSelection } from './internals/computeds'\nimport {\n\trunAggregate,\n\trunAllCount,\n\trunAllCreate,\n\trunAllDelete,\n\trunAllIterate,\n\trunAllPaginate,\n\trunAllRead,\n\trunAllUpdate,\n\trunOneCreate,\n\trunOneDelete,\n\trunOneRead,\n\trunOneUpdate,\n\trunOneUpsert,\n} from './internals/executors'\nimport { resolvePreloads } from './internals/preloads'\nimport type { ReadLimitSource, ReadOffsetSource } from './internals/query-shape'\nimport type { Paginated, SelectedWithPreloads } from './internals/types'\n\nexport type { Paginated }\n\ntype MaybeNull<T, Req extends boolean> = Req extends true ? T : T | null\ntype SchemaPrimaryKeyValue<S extends AnySchema> = SchemaOutput<S>[S['pkField']['name'] & keyof SchemaOutput<S>]\nexport type UpsertInput<S extends AnySchema> = { create: SchemaCreateInput<S> } | { create: SchemaCreateInput<S>; update: SchemaUpdateInput<S> }\ntype ReadState<Sel extends string, P extends readonly AnyPreloadDef[] = readonly AnyPreloadDef[]> = {\n\twhere?: FilterGroup\n\tselect?: readonly Sel[]\n\tpreloads?: P\n}\n\nexport type HasMethod<A, Method extends string> =\n\tMethod extends keyof A\n\t\t? A[Method] extends (...args: any) => any\n\t\t\t? true\n\t\t\t: false\n\t\t: false\n\nexport type OneBuilderSurface<S extends AnySchema, A = unknown, Sel extends string = never, P extends readonly AnyPreloadDef[] = [], Req extends boolean = false> =\n\tOneBuilder<S, A, Sel, P, Req> &\n\t(HasMethod<A, 'updateMany'> extends true ? {} : { update: never }) &\n\t(HasMethod<A, 'deleteMany'> extends true ? {} : { delete: never }) &\n\t(HasMethod<A, 'upsertOne'> extends true ? {} : { upsert: never })\n\nexport type AllBuilderSurface<S extends AnySchema, A = unknown, Sel extends string = never, P extends readonly AnyPreloadDef[] = []> =\n\tAllBuilder<S, A, Sel, P> &\n\t(HasMethod<A, 'iterateMany'> extends true ? {} : { iterate: never }) &\n\t(HasMethod<A, 'updateMany'> extends true ? {} : { update: never }) &\n\t(HasMethod<A, 'deleteMany'> extends true ? {} : { delete: never }) &\n\t(HasMethod<A, 'count'> extends true ? {} : { count: never }) &\n\t([HasMethod<A, 'findMany'>, HasMethod<A, 'count'>] extends [true, true] ? {} : { paginate: never })\n\ntype HasNonEmptyAggregateOps<A> = A extends { aggregateOps: readonly [any, ...any[]] } ? true : false\n\nexport type SchemaRefSurface<S extends AnySchema, A = unknown> =\n\tOmit<SchemaRef<S, A>, 'raw' | 'aggregate'> &\n\t(HasMethod<A, 'raw'> extends true\n\t\t? { raw: <T = InferRawReturn<A>>(...args: InferRawArgs<A>) => Promise<T> }\n\t\t: { raw: never }) &\n\t([HasMethod<A, 'aggregate'>, HasNonEmptyAggregateOps<A>] extends [true, true]\n\t\t? { aggregate: SchemaRef<S, A>['aggregate'] }\n\t\t: { aggregate: never })\n\ntype ReadBuilderFor<TBuilder, S extends AnySchema, A, Sel extends string, P extends readonly AnyPreloadDef[]> = TBuilder extends {\n\t_builderKind: 'one'\n\t_req: infer Req extends boolean\n}\n\t? OneBuilderSurface<S, A, Sel, P, Req>\n\t: TBuilder extends { _builderKind: 'all' }\n\t\t? AllBuilderSurface<S, A, Sel, P>\n\t\t: never\n\nexport class SchemaContext<S extends AnySchema> {\n\tconstructor(\n\t\treadonly schema: S,\n\t\tprivate readonly getUse: (target: AnySchema) => OrmUse,\n\t) {}\n\n\tasync shapeRows<Sel extends string, P extends readonly AnyPreloadDef[]>(\n\t\tselect: readonly Sel[] | undefined,\n\t\tpreloads: P,\n\t\trows: Record<string, unknown>[],\n\t): Promise<SelectedWithPreloads<S, Sel, P>[]> {\n\t\tconst plan = planSelection(this.schema, select)\n\t\tconst selected = applyComputedSelection(this.schema, rows, plan)\n\t\tif (preloads.length === 0) return selected as SelectedWithPreloads<S, Sel, P>[]\n\t\treturn (await resolvePreloads(selected, preloads, this.getUse)) as SelectedWithPreloads<S, Sel, P>[]\n\t}\n\n\tasync shapeOneRow<Sel extends string, P extends readonly AnyPreloadDef[]>(\n\t\tselect: readonly Sel[] | undefined,\n\t\tpreloads: P,\n\t\trow: Record<string, unknown> | null,\n\t): Promise<SelectedWithPreloads<S, Sel, P> | null> {\n\t\tif (!row) return null\n\t\tconst [resolved] = await this.shapeRows(select, preloads, [row])\n\t\treturn resolved ?? null\n\t}\n\n\tget use() {\n\t\treturn this.getUse(this.schema)\n\t}\n}\n\nabstract class ReadSelectState<S extends AnySchema, A = unknown, Sel extends string = never, P extends readonly AnyPreloadDef[] = []> {\n\tprotected readonly _context: SchemaContext<S>\n\tprotected _where: FilterGroup\n\tprotected _select: readonly Sel[] | undefined\n\tprotected _preloads: P\n\n\tconstructor(context: SchemaContext<S>, state?: ReadState<Sel, P>) {\n\t\tthis._context = context\n\t\tthis._where = state?.where ? state.where.clone() : FilterGroup.create()\n\t\tthis._select = state?.select\n\t\tthis._preloads = state?.preloads ?? ([] as unknown as P)\n\t}\n\n\twhere(factory: FilterFactory): this {\n\t\tconst nextGroup = factory(this._where.clone())\n\t\treturn this._clone<Sel, P>({\n\t\t\twhere: nextGroup,\n\t\t\tselect: this._select as readonly Sel[] | undefined,\n\t\t\tpreloads: this._preloads,\n\t\t}) as unknown as this\n\t}\n\n\tselect<NewSel extends keyof SchemaOutput<S> & string>(fields: readonly NewSel[]): ReadBuilderFor<this, S, A, NewSel, P> {\n\t\treturn this._clone<NewSel, P>({ select: fields })\n\t}\n\n\tpreload<NewP extends readonly AnyPreloadDef[]>(defs: NewP): ReadBuilderFor<this, S, A, Sel, NewP> {\n\t\treturn this._clone<Sel, NewP>({ preloads: defs })\n\t}\n\n\tprotected _readState<NewSel extends string = Sel, NewP extends readonly AnyPreloadDef[] = P>(\n\t\tnext: ReadState<NewSel, NewP> = {} as ReadState<NewSel, NewP>,\n\t): ReadState<NewSel, NewP> {\n\t\treturn {\n\t\t\twhere: next.where ?? this._where.clone(),\n\t\t\tselect: next.select ?? (this._select as unknown as NewSel[]),\n\t\t\tpreloads: next.preloads ?? ([...this._preloads] as unknown as NewP),\n\t\t}\n\t}\n\n\tprotected abstract _clone<NewSel extends string, NewP extends readonly AnyPreloadDef[]>(\n\t\t_next: ReadState<NewSel, NewP>,\n\t): ReadBuilderFor<this, S, A, NewSel, NewP>\n}\n\nexport class SchemaRef<S extends AnySchema, A = unknown> {\n\treadonly #context: SchemaContext<S>\n\n\tconstructor(context: SchemaContext<S>) {\n\t\tthis.#context = context\n\t}\n\n\tone(): OneBuilderSurface<S, A, never, []> {\n\t\treturn new OneBuilder<S, A, never, []>(this.#context) as OneBuilderSurface<S, A, never, []>\n\t}\n\n\tall(): AllBuilderSurface<S, A, never, []> {\n\t\treturn new AllBuilder<S, A, never, []>(this.#context) as AllBuilderSurface<S, A, never, []>\n\t}\n\n\taggregate(): AggregateBuilder<S, A, {}, {}, false, false, false> {\n\t\treturn new AggregateBuilder<S, A, {}, {}, false, false, false>(this.#context)\n\t}\n\n\traw(...args: any[]) {\n\t\treturn this.#context.use.raw(...args)\n\t}\n}\n\nexport class OneBuilder<S extends AnySchema, A = unknown, Sel extends string = never, P extends readonly AnyPreloadDef[] = [], Req extends boolean = false> extends ReadSelectState<S, A, Sel, P> {\n\tdeclare readonly _builderKind: 'one'\n\tdeclare readonly _req: Req\n\n\tprotected _required: boolean\n\tprotected _requiredMessage: string | undefined\n\n\tconstructor(context: SchemaContext<S>, state?: ReadState<Sel, P>, reqState?: { required: boolean; message?: string }) {\n\t\tsuper(context, state)\n\t\tthis._required = reqState?.required ?? false\n\t\tthis._requiredMessage = reqState?.message\n\t}\n\n\tid(value: SchemaPrimaryKeyValue<S>): this {\n\t\tconst nextGroup = this._where.clone().eq(this._context.schema.pkField, value)\n\t\treturn this._clone<Sel, P>({\n\t\t\twhere: nextGroup,\n\t\t\tselect: this._select as readonly Sel[] | undefined,\n\t\t\tpreloads: this._preloads,\n\t\t}) as this\n\t}\n\n\trequired(this: OneBuilder<S, A, Sel, P, false>, message?: string): OneBuilderSurface<S, A, Sel, P, true> {\n\t\treturn new OneBuilder<S, A, Sel, P, true>(this._context, this._readState(), { required: true, message }) as OneBuilderSurface<S, A, Sel, P, true>\n\t}\n\n\tprivate _assertFound(result: unknown, operation: OrmNotFoundOperation): void {\n\t\tif (this._required && result === null) {\n\t\t\tthrow new OrmNotFoundError({ schema: this._context.schema.name, operation, where: this._where, message: this._requiredMessage })\n\t\t}\n\t}\n\n\tprotected _clone<NewSel extends string, NewP extends readonly AnyPreloadDef[]>(next: ReadState<NewSel, NewP>) {\n\t\treturn new OneBuilder<S, A, NewSel, NewP, Req>(this._context, this._readState(next), { required: this._required, message: this._requiredMessage }) as any\n\t}\n\n\tcreate(data: SchemaCreateInput<S>) {\n\t\treturn runOneCreate(\n\t\t\tthis._context,\n\t\t\t{\n\t\t\t\tselect: this._select,\n\t\t\t\tpreloads: this._preloads,\n\t\t\t},\n\t\t\tdata,\n\t\t)\n\t}\n\n\tasync update(data: SchemaUpdateInput<S>): Promise<MaybeNull<SelectedWithPreloads<S, Sel, P>, Req>> {\n\t\tconst result = await runOneUpdate(\n\t\t\tthis._context,\n\t\t\t{\n\t\t\t\twhere: this._where,\n\t\t\t\tselect: this._select,\n\t\t\t\tpreloads: this._preloads,\n\t\t\t},\n\t\t\tdata,\n\t\t)\n\t\tthis._assertFound(result, 'updateOne')\n\t\treturn result as any\n\t}\n\n\tupsert(data: UpsertInput<S>) {\n\t\treturn runOneUpsert(\n\t\t\tthis._context,\n\t\t\t{\n\t\t\t\twhere: this._where,\n\t\t\t\tselect: this._select,\n\t\t\t\tpreloads: this._preloads,\n\t\t\t},\n\t\t\tdata,\n\t\t)\n\t}\n\n\tasync delete(): Promise<MaybeNull<SelectedWithPreloads<S, Sel, P>, Req>> {\n\t\tconst result = await runOneDelete(this._context, {\n\t\t\twhere: this._where,\n\t\t\tselect: this._select,\n\t\t\tpreloads: this._preloads,\n\t\t})\n\t\tthis._assertFound(result, 'deleteOne')\n\t\treturn result as any\n\t}\n\n\tasync find(): Promise<MaybeNull<SelectedWithPreloads<S, Sel, P>, Req>> {\n\t\tconst result = await runOneRead(this._context, {\n\t\t\twhere: this._where,\n\t\t\tselect: this._select,\n\t\t\tpreloads: this._preloads,\n\t\t})\n\t\tthis._assertFound(result, 'findOne')\n\t\treturn result as any\n\t}\n}\n\nexport class AllBuilder<S extends AnySchema, A = unknown, Sel extends string = never, P extends readonly AnyPreloadDef[] = []> extends ReadSelectState<S, A, Sel, P> {\n\tdeclare readonly _builderKind: 'all'\n\n\t#orderBy: OrderBy[]\n\t#limitSource: ReadLimitSource | undefined\n\t#offsetSource: ReadOffsetSource | undefined\n\n\tconstructor(\n\t\tcontext: SchemaContext<S>,\n\t\tstate?: ReadState<Sel, P>,\n\t\tqueryState?: { orderBy?: OrderBy[]; limitSource?: ReadLimitSource; offsetSource?: ReadOffsetSource },\n\t) {\n\t\tsuper(context, state)\n\t\tthis.#orderBy = queryState?.orderBy ?? []\n\t\tthis.#limitSource = queryState?.limitSource\n\t\tthis.#offsetSource = queryState?.offsetSource\n\t}\n\n\t#withQuery(queryOverride: Partial<{ orderBy: OrderBy[]; limitSource: ReadLimitSource; offsetSource: ReadOffsetSource }>) {\n\t\tconst has = (key: keyof typeof queryOverride) => Object.prototype.hasOwnProperty.call(queryOverride, key)\n\t\treturn new AllBuilder<S, A, Sel, P>(\n\t\t\tthis._context,\n\t\t\tthis._readState(),\n\t\t\t{\n\t\t\t\torderBy: has('orderBy') ? queryOverride.orderBy : [...this.#orderBy],\n\t\t\t\tlimitSource: has('limitSource') ? queryOverride.limitSource : this.#limitSource,\n\t\t\t\toffsetSource: has('offsetSource') ? queryOverride.offsetSource : this.#offsetSource,\n\t\t\t},\n\t\t) as this\n\t}\n\n\tprotected _clone<NewSel extends string, NewP extends readonly AnyPreloadDef[]>(next: ReadState<NewSel, NewP>) {\n\t\treturn new AllBuilder<S, A, NewSel, NewP>(\n\t\t\tthis._context,\n\t\t\tthis._readState({\n\t\t\t\twhere: next.where,\n\t\t\t\tselect: next.select,\n\t\t\t\tpreloads: next.preloads,\n\t\t\t}),\n\t\t\t{ orderBy: [...this.#orderBy], limitSource: this.#limitSource, offsetSource: this.#offsetSource },\n\t\t) as any\n\t}\n\n\torderBy(field: string | AnyField, direction: 'asc' | 'desc' = 'asc') {\n\t\treturn this.#withQuery({ orderBy: [...this.#orderBy, new OrderBy(field, direction)] })\n\t}\n\n\tlimit(limit: number) {\n\t\treturn this.#withQuery({ limitSource: { value: limit } })\n\t}\n\n\toffset(offset: number) {\n\t\treturn this.#withQuery({ offsetSource: { kind: 'offset', value: offset } })\n\t}\n\n\tpage(page: number) {\n\t\treturn this.#withQuery({ offsetSource: { kind: 'page', value: page } })\n\t}\n\n\tcreate(data: SchemaCreateInput<S>[]) {\n\t\treturn runAllCreate(\n\t\t\tthis._context,\n\t\t\t{\n\t\t\t\tselect: this._select,\n\t\t\t\tpreloads: this._preloads,\n\t\t\t},\n\t\t\tdata,\n\t\t)\n\t}\n\n\tupdate(data: SchemaUpdateInput<S>) {\n\t\treturn runAllUpdate(\n\t\t\tthis._context,\n\t\t\t{\n\t\t\t\twhere: this._where,\n\t\t\t\tselect: this._select,\n\t\t\t\tpreloads: this._preloads,\n\t\t\t},\n\t\t\tdata,\n\t\t)\n\t}\n\n\tdelete() {\n\t\treturn runAllDelete(this._context, {\n\t\t\twhere: this._where,\n\t\t\tselect: this._select,\n\t\t\tpreloads: this._preloads,\n\t\t})\n\t}\n\n\tfind() {\n\t\treturn runAllRead(this._context, {\n\t\t\twhere: this._where,\n\t\t\tselect: this._select,\n\t\t\tpreloads: this._preloads,\n\t\t\torderBy: this.#orderBy,\n\t\t\tlimitSource: this.#limitSource,\n\t\t\toffsetSource: this.#offsetSource,\n\t\t})\n\t}\n\n\tcount() {\n\t\treturn runAllCount(this._context, { where: this._where })\n\t}\n\n\tpaginate(): Promise<Paginated<SelectedWithPreloads<S, Sel, P>>> {\n\t\treturn runAllPaginate(this._context, {\n\t\t\twhere: this._where,\n\t\t\tselect: this._select,\n\t\t\tpreloads: this._preloads,\n\t\t\torderBy: this.#orderBy,\n\t\t\tlimitSource: this.#limitSource,\n\t\t\toffsetSource: this.#offsetSource,\n\t\t})\n\t}\n\n\titerate(options?: IterationOptions) {\n\t\treturn runAllIterate(this._context, {\n\t\t\twhere: this._where,\n\t\t\tselect: this._select,\n\t\t\tpreloads: this._preloads,\n\t\t\torderBy: this.#orderBy,\n\t\t\tlimitSource: this.#limitSource,\n\t\t\toffsetSource: this.#offsetSource,\n\t\t}, options)\n\t}\n}\n\ntype AggregateEntry = AggregateSpec['aggregates'][number]\n\ntype FieldsToGroupKeys<F extends readonly AnyField[]> = {\n\t[E in F[number] as E extends Field<any, infer N> ? N : never]: E extends Field<infer V> ? V : never\n}\n\nexport class AggregateBuilder<\n\tS extends AnySchema,\n\tA = unknown,\n\tAggs = {},\n\tGroupKeys = {},\n\tHasGroupBy extends boolean = false,\n\tHasWhere extends boolean = false,\n\tHasHaving extends boolean = false,\n> {\n\treadonly #context: SchemaContext<S>\n\treadonly #where: FilterGroup\n\treadonly #having: FilterGroup\n\treadonly #aggregates: readonly AggregateEntry[]\n\treadonly #groupBy: readonly string[]\n\n\tconstructor(\n\t\tcontext: SchemaContext<S>,\n\t\tstate?: { where?: FilterGroup; having?: FilterGroup; aggregates?: readonly AggregateEntry[]; groupBy?: readonly string[] },\n\t) {\n\t\tthis.#context = context\n\t\tthis.#where = state?.where ? state.where.clone() : FilterGroup.create()\n\t\tthis.#having = state?.having ? state.having.clone() : FilterGroup.create()\n\t\tthis.#aggregates = state?.aggregates ?? []\n\t\tthis.#groupBy = state?.groupBy ?? []\n\t}\n\n\tget #state() {\n\t\treturn { where: this.#where, having: this.#having, aggregates: this.#aggregates, groupBy: this.#groupBy }\n\t}\n\n\twhere(\n\t\t...args: HasWhere extends true ? [never] : [factory: FilterFactory]\n\t): AggregateBuilder<S, A, Aggs, GroupKeys, HasGroupBy, true, HasHaving> {\n\t\tconst factory = args[0] as FilterFactory\n\t\treturn new AggregateBuilder<S, A, Aggs, GroupKeys, HasGroupBy, true, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\twhere: factory(this.#where.clone()),\n\t\t})\n\t}\n\n\thaving(\n\t\t...args: HasHaving extends true ? [never] : [factory: FilterFactory]\n\t): AggregateBuilder<S, A, Aggs, GroupKeys, HasGroupBy, HasWhere, true> {\n\t\tconst factory = args[0] as FilterFactory\n\t\treturn new AggregateBuilder<S, A, Aggs, GroupKeys, HasGroupBy, HasWhere, true>(this.#context, {\n\t\t\t...this.#state,\n\t\t\thaving: factory(this.#having.clone()),\n\t\t})\n\t}\n\n\tgroupBy<F extends readonly Field<string | number | boolean | Date>[]>(\n\t\t...fields: HasGroupBy extends true ? [never] : [...F]\n\t): AggregateBuilder<S, A, Aggs, GroupKeys & FieldsToGroupKeys<F>, true, HasWhere, HasHaving> {\n\t\treturn new AggregateBuilder<S, A, Aggs, GroupKeys & FieldsToGroupKeys<F>, true, HasWhere, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\tgroupBy: (fields as readonly AnyField[]).map((f) => toFieldName(f)),\n\t\t})\n\t}\n\n\tcount<K extends string>(\n\t\t...[alias]: K extends keyof Aggs ? [never] : [alias: K]\n\t): AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving> {\n\t\treturn new AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\taggregates: [...this.#aggregates, { fn: 'count', alias: alias as string }],\n\t\t})\n\t}\n\n\tcountDistinct<K extends string>(\n\t\tfield: AnyField,\n\t\t...[alias]: K extends keyof Aggs ? [never] : [alias: K]\n\t): AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving> {\n\t\treturn new AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\taggregates: [...this.#aggregates, { fn: 'countDistinct', field: toFieldName(field), alias: alias as string }],\n\t\t})\n\t}\n\n\tsum<K extends string>(\n\t\tfield: Field<number>,\n\t\t...[alias]: K extends keyof Aggs ? [never] : [alias: K]\n\t): AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving> {\n\t\treturn new AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\taggregates: [...this.#aggregates, { fn: 'sum', field: toFieldName(field), alias: alias as string }],\n\t\t})\n\t}\n\n\tavg<K extends string>(\n\t\tfield: Field<number>,\n\t\t...[alias]: K extends keyof Aggs ? [never] : [alias: K]\n\t): AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving> {\n\t\treturn new AggregateBuilder<S, A, Aggs & Record<K, number>, GroupKeys, HasGroupBy, HasWhere, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\taggregates: [...this.#aggregates, { fn: 'avg', field: toFieldName(field), alias: alias as string }],\n\t\t})\n\t}\n\n\tmin<F extends Field<number | string | Date>, K extends string>(\n\t\tfield: F,\n\t\t...[alias]: K extends keyof Aggs ? [never] : [alias: K]\n\t): AggregateBuilder<S, A, Aggs & Record<K, F extends Field<infer V> ? V : never>, GroupKeys, HasGroupBy, HasWhere, HasHaving> {\n\t\treturn new AggregateBuilder<S, A, Aggs & Record<K, F extends Field<infer V> ? V : never>, GroupKeys, HasGroupBy, HasWhere, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\taggregates: [...this.#aggregates, { fn: 'min', field: toFieldName(field), alias: alias as string }],\n\t\t})\n\t}\n\n\tmax<F extends Field<number | string | Date>, K extends string>(\n\t\tfield: F,\n\t\t...[alias]: K extends keyof Aggs ? [never] : [alias: K]\n\t): AggregateBuilder<S, A, Aggs & Record<K, F extends Field<infer V> ? V : never>, GroupKeys, HasGroupBy, HasWhere, HasHaving> {\n\t\treturn new AggregateBuilder<S, A, Aggs & Record<K, F extends Field<infer V> ? V : never>, GroupKeys, HasGroupBy, HasWhere, HasHaving>(this.#context, {\n\t\t\t...this.#state,\n\t\t\taggregates: [...this.#aggregates, { fn: 'max', field: toFieldName(field), alias: alias as string }],\n\t\t})\n\t}\n\n\tasync run(\n\t\t..._: [keyof Aggs] extends [never] ? [never] : []\n\t): Promise<HasGroupBy extends true ? (Aggs & GroupKeys)[] : Aggs> {\n\t\tconst spec: AggregateSpec = {\n\t\t\taggregates: this.#aggregates,\n\t\t\tgroupBy: this.#groupBy,\n\t\t}\n\t\tif (this.#where.children.length > 0) {\n\t\t\tspec.where = this.#where\n\t\t}\n\t\tif (this.#having.children.length > 0) {\n\t\t\tspec.having = this.#having\n\t\t}\n\t\tconst rows = await runAggregate(this.#context, spec)\n\t\tif (this.#groupBy.length > 0) {\n\t\t\treturn rows as any\n\t\t}\n\t\treturn rows[0] as any\n\t}\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf, beforeEach, vi } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { Instance } = await import('../../instance')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { OrmValidationError } = await import('../errors')\n\tconst { OrmAdapter } = await import('../orm-adapter')\n\tconst { Relations } = await import('../relations')\n\tconst { Repo } = await import('./repo')\n\tconst { Schema } = await import('../schema')\n\n\tdescribe('builders', () => {\n\t\tlet repo: any\n\t\tbeforeEach(() => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\trepo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t})\n\n\t\ttest('update() executes and returns updated row', async () => {\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => `u-${Math.random().toString(36).slice(2, 8)}`)\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\n\t\t\tconst created = await repo.on(UserSchema).one().create({ email: 'up@test.com', name: 'Before' })\n\t\t\tconst updated = await repo.on(UserSchema).one().id(created.id).update({ name: 'After' })\n\t\t\texpect(updated?.name).toBe('After')\n\t\t\tconst found = await repo.on(UserSchema).one().id(created.id).find()\n\t\t\texpect(found?.name).toBe('After')\n\t\t})\n\n\t\ttest('find() returns rows', async () => {\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => `u-${Math.random().toString(36).slice(2, 8)}`)\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@x.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'b@x.com', name: 'Bob' },\n\t\t\t\t])\n\n\t\t\tconst rows = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.or([(g) => g.eq('name', 'Alice'), (g) => g.eq('name', 'Bob')]))\n\t\t\t\t.find()\n\t\t\texpect(rows).toHaveLength(2)\n\t\t})\n\n\t\ttest('write branches do not leak filters', async () => {\n\t\t\tconst UserSchema = Schema.from('users')\n\t\t\t\t.pk('id', v.string(), () => `u-${Math.random().toString(36).slice(2, 8)}`)\n\t\t\t\t.field('email', v.string())\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'alice@x.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'bob@x.com', name: 'Bob' },\n\t\t\t\t])\n\n\t\t\tconst base = repo.on(UserSchema).all()\n\t\t\tawait base.where((q) => q.eq('name', 'Alice')).update({ name: 'A Updated' })\n\t\t\tawait base.where((q) => q.eq('name', 'Bob')).update({ name: 'B Updated' })\n\n\t\t\tconst all = await repo.on(UserSchema).all().orderBy('name', 'asc').find()\n\t\t\texpect(all.map((r) => r.name)).toEqual(['A Updated', 'B Updated'])\n\t\t})\n\t})\n\n\tdescribe('.required() modifier', () => {\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => `u-${Math.random().toString(36).slice(2, 8)}`)\n\t\t\t.field('email', v.string())\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\n\t\tlet repo: any\n\t\tbeforeEach(() => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\trepo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t})\n\n\t\tdescribe('type narrowing', () => {\n\t\t\ttype IsExact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false\n\t\t\ttype TestA = { findOne: (...a: any[]) => any; createOne: (...a: any[]) => any; createMany: (...a: any[]) => any; updateMany: (...a: any[]) => any; deleteMany: (...a: any[]) => any; upsertOne: (...a: any[]) => any; findMany: (...a: any[]) => any; deleteOne: (...a: any[]) => any; updateOne: (...a: any[]) => any }\n\t\t\ttype S = typeof UserSchema\n\t\t\ttype Result = import('./internals/types').SelectedWithPreloads<S, never, []>\n\t\t\ttype NameOnly = import('./internals/types').SelectedWithPreloads<S, 'name', []>\n\n\t\t\ttest('find() returns T | null without .required()', () => {\n\t\t\t\ttype R = ReturnType<OneBuilderSurface<S, TestA, never, [], false>['find']>\n\t\t\t\tconst _: IsExact<R, Promise<Result | null>> = true\n\t\t\t\tvoid _\n\t\t\t})\n\n\t\t\ttest('find() returns T with .required()', () => {\n\t\t\t\ttype R = ReturnType<OneBuilderSurface<S, TestA, never, [], true>['find']>\n\t\t\t\tconst _: IsExact<R, Promise<Result>> = true\n\t\t\t\tvoid _\n\t\t\t})\n\n\t\t\ttest('update() returns T with .required()', () => {\n\t\t\t\ttype R = ReturnType<OneBuilderSurface<S, TestA, never, [], true>['update']>\n\t\t\t\tconst _: IsExact<R, Promise<Result>> = true\n\t\t\t\tvoid _\n\t\t\t})\n\n\t\t\ttest('delete() returns T with .required()', () => {\n\t\t\t\ttype R = ReturnType<OneBuilderSurface<S, TestA, never, [], true>['delete']>\n\t\t\t\tconst _: IsExact<R, Promise<Result>> = true\n\t\t\t\tvoid _\n\t\t\t})\n\n\t\t\ttest('create() return type unchanged regardless of Req', () => {\n\t\t\t\ttype WithReq = ReturnType<OneBuilderSurface<S, TestA, never, [], true>['create']>\n\t\t\t\ttype WithoutReq = ReturnType<OneBuilderSurface<S, TestA, never, [], false>['create']>\n\t\t\t\tconst _: IsExact<WithReq, WithoutReq> = true\n\t\t\t\tvoid _\n\t\t\t})\n\n\t\t\ttest('.required() preserves Req through select()', () => {\n\t\t\t\ttype R = ReturnType<OneBuilderSurface<S, TestA, 'name', [], true>['find']>\n\t\t\t\tconst _: IsExact<R, Promise<NameOnly>> = true\n\t\t\t\tvoid _\n\t\t\t})\n\n\t\t\ttest('.required() once-per-step: Req=true surface has uncallable required()', () => {\n\t\t\t\ttype ReqBuilder = OneBuilderSurface<S, TestA, never, [], true>\n\t\t\t\ttype RequiredMethod = ReqBuilder['required']\n\t\t\t\ttype ThisParam = ThisParameterType<RequiredMethod>\n\t\t\t\ttype _check = ThisParam extends OneBuilder<S, TestA, never, [], false> ? true : false\n\t\t\t\tconst _: _check = true\n\t\t\t\tvoid _\n\t\t\t})\n\t\t})\n\n\t\tdescribe('runtime throw behaviour', () => {\n\t\t\ttest('.required().find() throws OrmNotFoundError when no row matched', async () => {\n\t\t\t\tawait expect(\n\t\t\t\t\trepo.on(UserSchema).one().required().id('nonexistent').find(),\n\t\t\t\t).rejects.toThrow(OrmNotFoundError)\n\t\t\t})\n\n\t\t\ttest('.required().update() throws OrmNotFoundError when no row matched', async () => {\n\t\t\t\tawait expect(\n\t\t\t\t\trepo.on(UserSchema).one().required().id('nonexistent').update({ name: 'X' }),\n\t\t\t\t).rejects.toThrow(OrmNotFoundError)\n\t\t\t})\n\n\t\t\ttest('.required().delete() throws OrmNotFoundError when no row matched', async () => {\n\t\t\t\tawait expect(\n\t\t\t\t\trepo.on(UserSchema).one().required().id('nonexistent').delete(),\n\t\t\t\t).rejects.toThrow(OrmNotFoundError)\n\t\t\t})\n\n\t\t\ttest('thrown error carries schema, operation, and where', async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait repo.on(UserSchema).one().required().id('u-abc').find()\n\t\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect(e).toBeInstanceOf(OrmNotFoundError)\n\t\t\t\t\tconst err = e as InstanceType<typeof OrmNotFoundError>\n\t\t\t\t\texpect(err.schema).toBe('users')\n\t\t\t\t\texpect(err.operation).toBe('findOne')\n\t\t\t\t\texpect(err.where).toBeInstanceOf(FilterGroup)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\ttest('update throws with operation updateOne', async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait repo.on(UserSchema).one().required().id('u-abc').update({ name: 'X' })\n\t\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect((e as any).operation).toBe('updateOne')\n\t\t\t\t}\n\t\t\t})\n\n\t\t\ttest('delete throws with operation deleteOne', async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait repo.on(UserSchema).one().required().id('u-abc').delete()\n\t\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect((e as any).operation).toBe('deleteOne')\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tdescribe('no throw when row exists', () => {\n\t\t\ttest('.required().find() returns the row when found', async () => {\n\t\t\t\tconst created = await repo.on(UserSchema).one().create({ email: 'a@b.com', name: 'A' })\n\t\t\t\tconst found = await repo.on(UserSchema).one().required().id(created.id).find()\n\t\t\t\texpect(found.name).toBe('A')\n\t\t\t})\n\n\t\t\ttest('.required().update() returns the updated row', async () => {\n\t\t\t\tconst created = await repo.on(UserSchema).one().create({ email: 'a@b.com', name: 'A' })\n\t\t\t\tconst updated = await repo.on(UserSchema).one().required().id(created.id).update({ name: 'B' })\n\t\t\t\texpect(updated.name).toBe('B')\n\t\t\t})\n\n\t\t\ttest('.required().delete() returns the deleted row', async () => {\n\t\t\t\tconst created = await repo.on(UserSchema).one().create({ email: 'a@b.com', name: 'A' })\n\t\t\t\tconst deleted = await repo.on(UserSchema).one().required().id(created.id).delete()\n\t\t\t\texpect(deleted.name).toBe('A')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('custom message', () => {\n\t\t\ttest('.required(message) uses custom message on throw', async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait repo.on(UserSchema).one().required('user must exist').id('u-abc').find()\n\t\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect((e as any).message).toBe('user must exist')\n\t\t\t\t}\n\t\t\t})\n\n\t\t\ttest('default message for PK-keyed chain', async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait repo.on(UserSchema).one().required().id('u-abc').find()\n\t\t\t\t\texpect.unreachable('should have thrown')\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect((e as any).message).toBe('users.findOne: no row matched id=u-abc')\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tdescribe('default behaviour preserved', () => {\n\t\t\ttest('find() returns null without .required()', async () => {\n\t\t\t\tconst result = await repo.on(UserSchema).one().id('nonexistent').find()\n\t\t\t\texpect(result).toBeNull()\n\t\t\t})\n\n\t\t\ttest('update() returns null without .required()', async () => {\n\t\t\t\tconst result = await repo.on(UserSchema).one().id('nonexistent').update({ name: 'X' })\n\t\t\t\texpect(result).toBeNull()\n\t\t\t})\n\n\t\t\ttest('delete() returns null without .required()', async () => {\n\t\t\t\tconst result = await repo.on(UserSchema).one().id('nonexistent').delete()\n\t\t\t\texpect(result).toBeNull()\n\t\t\t})\n\t\t})\n\n\t\tdescribe('runtime no-op for create/upsert', () => {\n\t\t\ttest('.required() before create() returns the created row', async () => {\n\t\t\t\tconst created = await repo.on(UserSchema).one().required().create({ email: 'x@y.com', name: 'X' })\n\t\t\t\texpect(created.email).toBe('x@y.com')\n\t\t\t\texpect(created.name).toBe('X')\n\t\t\t})\n\n\t\t\ttest('.required() before upsert() returns the upserted row', async () => {\n\t\t\t\tconst upserted = await repo\n\t\t\t\t\t.on(UserSchema)\n\t\t\t\t\t.one()\n\t\t\t\t\t.required()\n\t\t\t\t\t.where((q: any) => q.eq('email', 'x@y.com'))\n\t\t\t\t\t.upsert({ create: { email: 'x@y.com', name: 'X' } })\n\t\t\t\texpect(upserted.email).toBe('x@y.com')\n\t\t\t\texpect(upserted.name).toBe('X')\n\t\t\t})\n\t\t})\n\t})\n\n\tdescribe('read query-shape validation and page state', () => {\n\t\tlet itemCounter = 0\n\t\tlet userCounter = 0\n\t\tlet postCounter = 0\n\n\t\tconst ItemSchema = Schema.from('paged_items')\n\t\t\t.pk('id', v.string(), () => `item-${++itemCounter}`)\n\t\t\t.field('position', v.number())\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\n\t\tconst UserSchema = Schema.from('query_shape_users')\n\t\t\t.pk('id', v.string(), () => `user-${++userCounter}`)\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\n\t\tconst PostSchema = Schema.from('query_shape_posts')\n\t\t\t.pk('id', v.string(), () => `post-${++postCounter}`)\n\t\t\t.field('userId', v.string())\n\t\t\t.field('title', v.string())\n\t\t\t.build()\n\n\t\tconst UserRels = Relations.from(UserSchema).hasMany('posts', PostSchema.fields.userId).build()\n\t\tconst PostRels = Relations.from(PostSchema).belongsTo('author', PostSchema.fields.userId, UserSchema).build()\n\n\t\tfunction makeRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\treturn Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t}\n\n\t\tasync function seedItems(repo: any, count: number) {\n\t\t\tawait repo.on(ItemSchema).all().create(\n\t\t\t\tArray.from({ length: count }, (_, index) => ({ position: index + 1, name: `Item ${index + 1}` })),\n\t\t\t)\n\t\t}\n\n\t\tfunction expectQueryShapeError(error: unknown, field?: string) {\n\t\t\texpect(error).toBeInstanceOf(OrmValidationError)\n\t\t\tconst err = error as InstanceType<typeof OrmValidationError>\n\t\t\texpect(err.kind).toBe('query-shape')\n\t\t\tif (field) expect(err.failures.some((failure) => failure.field === field)).toBe(true)\n\t\t}\n\n\t\ttest('.page(n) is 1-based offset sugar for .find()', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 10)\n\n\t\t\tconst rows = await repo.on(ItemSchema).all().orderBy('position', 'asc').limit(3).page(2).find()\n\n\t\t\texpect(rows.map((row) => row.position)).toEqual([4, 5, 6])\n\t\t})\n\n\t\ttest('.page(2).limit(50).find() uses the final effective limit for offset', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 125)\n\n\t\t\tconst rows = await repo.on(ItemSchema).all().orderBy('position', 'asc').page(2).limit(50).find()\n\n\t\t\texpect(rows).toHaveLength(50)\n\t\t\texpect(rows[0].position).toBe(51)\n\t\t\texpect(rows.at(-1)?.position).toBe(100)\n\t\t})\n\n\t\ttest('duplicate .limit(...) calls keep last-wins behavior', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 10)\n\n\t\t\tconst rows = await repo.on(ItemSchema).all().orderBy('position', 'asc').limit(7).limit(3).find()\n\n\t\t\texpect(rows.map((row) => row.position)).toEqual([1, 2, 3])\n\t\t})\n\n\t\ttest('.offset(...) and .page(...) are last-source-wins', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 20)\n\n\t\t\tconst offsetWins = await repo.on(ItemSchema).all().orderBy('position', 'asc').limit(5).page(2).offset(10).find()\n\t\t\tconst pageWins = await repo.on(ItemSchema).all().orderBy('position', 'asc').limit(5).offset(10).page(2).find()\n\n\t\t\texpect(offsetWins.map((row) => row.position)).toEqual([11, 12, 13, 14, 15])\n\t\t\texpect(pageWins.map((row) => row.position)).toEqual([6, 7, 8, 9, 10])\n\t\t})\n\n\t\ttest('invalid page, limit, and offset values fail as query-shape errors at find()', async () => {\n\t\t\tconst repo = makeRepo()\n\n\t\t\tfor (const [field, read] of [\n\t\t\t\t['page', () => repo.on(ItemSchema).all().page(0 as any).find()],\n\t\t\t\t['limit', () => repo.on(ItemSchema).all().limit(0 as any).find()],\n\t\t\t\t['limit', () => repo.on(ItemSchema).all().limit(undefined as any).page(2).find()],\n\t\t\t\t['offset', () => repo.on(ItemSchema).all().offset(-1 as any).find()],\n\t\t\t] as const) {\n\t\t\t\ttry {\n\t\t\t\t\tawait read()\n\t\t\t\t\texpect.unreachable(`${field} should have failed`)\n\t\t\t\t} catch (error) {\n\t\t\t\t\texpectQueryShapeError(error, field)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\ttest('unknown selected fields fail as query-shape errors on find reads before rows are loaded', async () => {\n\t\t\tconst repo = makeRepo()\n\n\t\t\ttry {\n\t\t\t\tawait repo.on(ItemSchema).one().select(['unknownField' as any]).find()\n\t\t\t\texpect.unreachable('one().find() should have failed')\n\t\t\t} catch (error) {\n\t\t\t\texpectQueryShapeError(error, 'unknownField')\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait repo.on(ItemSchema).all().select(['unknownField' as any]).find()\n\t\t\t\texpect.unreachable('all().find() should have failed')\n\t\t\t} catch (error) {\n\t\t\t\texpectQueryShapeError(error, 'unknownField')\n\t\t\t}\n\t\t})\n\n\t\ttest('invalid and coherency-broken preload definitions fail as query-shape errors on find reads', async () => {\n\t\t\tconst repo = makeRepo()\n\n\t\t\tfor (const read of [\n\t\t\t\t() => repo.on(UserSchema).all().preload([{ def: {} as any }]).find(),\n\t\t\t\t() => repo.on(UserSchema).all().preload([PostRels.author]).find(),\n\t\t\t\t() => repo.on(UserSchema).all().preload([{ def: UserRels.posts, preloads: [UserRels.posts] }]).find(),\n\t\t\t]) {\n\t\t\t\ttry {\n\t\t\t\t\tawait read()\n\t\t\t\t\texpect.unreachable('preload should have failed')\n\t\t\t\t} catch (error) {\n\t\t\t\t\texpectQueryShapeError(error)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\ttest('orderBy(string) remains an unvalidated raw-string escape hatch', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 2)\n\n\t\t\tconst rows = await repo.on(ItemSchema).all().orderBy('missingRawSortField', 'asc').find()\n\n\t\t\texpect(rows).toHaveLength(2)\n\t\t})\n\n\t\ttest('page-based reads without an initialized Instance use default limit 100', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 250)\n\n\t\t\tconst rows = await repo.on(ItemSchema).all().orderBy('position', 'asc').page(2).find()\n\n\t\t\texpect(rows).toHaveLength(100)\n\t\t\texpect(rows[0].position).toBe(101)\n\t\t\texpect(rows.at(-1)?.position).toBe(200)\n\t\t})\n\n\t\ttest('find() without page, offset, or limit keeps existing flat-array behavior', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 12)\n\n\t\t\tconst rows = await repo.on(ItemSchema).all().orderBy('position', 'asc').find()\n\n\t\t\texpect(rows).toHaveLength(12)\n\t\t\texpect(rows.map((row) => row.position)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])\n\t\t})\n\n\t\ttest('.paginate() returns a first-page envelope with docs computed from filter-only count', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 5)\n\n\t\t\tconst page = await repo.on(ItemSchema).all().orderBy('position', 'asc').limit(2).paginate()\n\n\t\t\texpect(page.items.map((row) => row.position)).toEqual([1, 2])\n\t\t\texpect(page.docs).toEqual({ limit: 2, total: 5, count: 2 })\n\t\t\texpect(page.pages).toEqual({ current: 1, start: 1, last: 3, previous: null, next: 2 })\n\t\t})\n\n\t\ttest('.paginate() reports pages.last = 1 for empty result sets', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 3)\n\n\t\t\tconst page = await repo.on(ItemSchema).all().where((q) => q.eq('name', 'missing')).limit(5).paginate()\n\n\t\t\texpect(page.items).toEqual([])\n\t\t\texpect(page.docs).toEqual({ limit: 5, total: 0, count: 0 })\n\t\t\texpect(page.pages).toEqual({ current: 1, start: 1, last: 1, previous: null, next: null })\n\t\t})\n\n\t\ttest('.paginate() preserves the previous neighbor on the last real page', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 5)\n\n\t\t\tconst page = await repo.on(ItemSchema).all().orderBy('position', 'asc').limit(2).page(3).paginate()\n\n\t\t\texpect(page.items.map((row) => row.position)).toEqual([5])\n\t\t\texpect(page.docs).toEqual({ limit: 2, total: 5, count: 1 })\n\t\t\texpect(page.pages).toEqual({ current: 3, start: 1, last: 3, previous: 2, next: null })\n\t\t})\n\n\t\ttest('.paginate() preserves past-last page requests with no navigation neighbors', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 5)\n\n\t\t\tconst page = await repo.on(ItemSchema).all().orderBy('position', 'asc').limit(2).page(4).paginate()\n\n\t\t\texpect(page.items).toEqual([])\n\t\t\texpect(page.docs).toEqual({ limit: 2, total: 5, count: 0 })\n\t\t\texpect(page.pages).toEqual({ current: 4, start: 1, last: 3, previous: null, next: null })\n\t\t})\n\n\t\ttest('.paginate() does not require orderBy', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 3)\n\n\t\t\tconst page = await repo.on(ItemSchema).all().limit(2).paginate()\n\n\t\t\texpect(page.items).toHaveLength(2)\n\t\t\texpect(page.docs).toEqual({ limit: 2, total: 3, count: 2 })\n\t\t\texpect(page.pages).toEqual({ current: 1, start: 1, last: 2, previous: null, next: 2 })\n\t\t})\n\n\t\ttest('.paginate() without an explicit limit uses the safe default page limit without Instance.get()', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 101)\n\t\t\tconst getSpy = vi.spyOn(Instance, 'get').mockImplementation(() => {\n\t\t\t\tthrow new Error('Instance.get() should not be required for pagination defaults')\n\t\t\t})\n\n\t\t\ttry {\n\t\t\t\tconst page = await repo.on(ItemSchema).all().orderBy('position', 'asc').paginate()\n\n\t\t\t\texpect(page.items).toHaveLength(100)\n\t\t\t\texpect(page.docs).toEqual({ limit: 100, total: 101, count: 100 })\n\t\t\t\texpect(page.pages).toEqual({ current: 1, start: 1, last: 2, previous: null, next: 2 })\n\t\t\t} finally {\n\t\t\t\tgetSpy.mockRestore()\n\t\t\t}\n\t\t})\n\n\t\ttest('.paginate() counts only the filter while page items honor limit and offset', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo.on(ItemSchema).all().create([\n\t\t\t\t{ position: 1, name: 'keep' },\n\t\t\t\t{ position: 2, name: 'drop' },\n\t\t\t\t{ position: 3, name: 'keep' },\n\t\t\t\t{ position: 4, name: 'keep' },\n\t\t\t])\n\n\t\t\tconst page = await repo\n\t\t\t\t.on(ItemSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('name', 'keep'))\n\t\t\t\t.orderBy('position', 'asc')\n\t\t\t\t.limit(1)\n\t\t\t\t.page(2)\n\t\t\t\t.paginate()\n\n\t\t\texpect(page.items.map((row) => row.position)).toEqual([3])\n\t\t\texpect(page.docs).toEqual({ limit: 1, total: 3, count: 1 })\n\t\t\texpect(page.pages).toEqual({ current: 2, start: 1, last: 3, previous: 1, next: 3 })\n\t\t})\n\n\t\ttest('.paginate() starts item and count reads in parallel after query-shape validation and config resolution', async () => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\tconst events: string[] = []\n\t\t\t;(adapter as any).use = vi.fn((schema: AnySchema, config: any) => {\n\t\t\t\tconst use = origUse(schema, config)\n\t\t\t\treturn {\n\t\t\t\t\t...use,\n\t\t\t\t\tfindMany: async (...args: any[]) => {\n\t\t\t\t\t\tevents.push('findMany:start')\n\t\t\t\t\t\tawait Promise.resolve()\n\t\t\t\t\t\tevents.push('findMany:end')\n\t\t\t\t\t\treturn use.findMany(...(args as [any, any]))\n\t\t\t\t\t},\n\t\t\t\t\tcount: async (...args: any[]) => {\n\t\t\t\t\t\tevents.push('count:start')\n\t\t\t\t\t\tawait Promise.resolve()\n\t\t\t\t\t\tevents.push('count:end')\n\t\t\t\t\t\treturn use.count(...(args as [any]))\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t})\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => {\n\t\t\t\t\tevents.push('resolve')\n\t\t\t\t\treturn { table: s.name }\n\t\t\t\t})\n\t\t\t\t.build()\n\t\t\tawait seedItems(repo, 3)\n\t\t\tevents.length = 0\n\n\t\t\tconst page = await repo.on(ItemSchema).all().where((q) => q.eq('name', 'Item 1')).limit(1).paginate()\n\n\t\t\texpect(events[0]).toBe('resolve')\n\t\t\texpect(events).toContain('count:start')\n\t\t\texpect(events).toContain('findMany:end')\n\t\t\texpect(events.indexOf('count:start')).toBeLessThan(events.indexOf('findMany:end'))\n\t\t\texpect(page.items.map((row) => row.position)).toEqual([1])\n\t\t\texpect(page.docs).toEqual({ limit: 1, total: 1, count: 1 })\n\t\t})\n\n\t\ttest('.paginate() items preserve selected row shape', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 2)\n\n\t\t\tconst page = await repo.on(ItemSchema).all().orderBy('position', 'asc').select(['name']).limit(1).paginate()\n\n\t\t\texpect(page.items).toEqual([{ name: 'Item 1' }])\n\t\t\texpect(page.docs).toMatchObject({ total: 2, count: 1 })\n\t\t})\n\n\t\ttest('.paginate() items preserve preloaded row shape', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst alice = await repo.on(UserSchema).one().create({ name: 'Alice' })\n\t\t\tconst bob = await repo.on(UserSchema).one().create({ name: 'Bob' })\n\t\t\tawait repo.on(PostSchema).all().create([\n\t\t\t\t{ userId: alice.id, title: 'A1' },\n\t\t\t\t{ userId: alice.id, title: 'A2' },\n\t\t\t\t{ userId: bob.id, title: 'B1' },\n\t\t\t])\n\n\t\t\tconst page = await repo.on(UserSchema).all().orderBy('name', 'asc').preload([UserRels.posts]).limit(1).paginate()\n\n\t\t\texpect(page.items).toHaveLength(1)\n\t\t\texpect(page.items[0].name).toBe('Alice')\n\t\t\texpect(page.items[0].posts.map((post: any) => post.title)).toEqual(['A1', 'A2'])\n\t\t\texpect(page.docs).toEqual({ limit: 1, total: 2, count: 1 })\n\t\t})\n\n\t\ttest('.paginate() uses offset-source last-wins and the final effective limit', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait seedItems(repo, 10)\n\n\t\t\tconst pageSource = await repo.on(ItemSchema).all().orderBy('position', 'asc').page(2).limit(3).paginate()\n\t\t\tconst offsetSource = await repo.on(ItemSchema).all().orderBy('position', 'asc').page(2).limit(2).offset(4).paginate()\n\n\t\t\texpect(pageSource.items.map((row) => row.position)).toEqual([4, 5, 6])\n\t\t\texpect(pageSource.pages.current).toBe(2)\n\t\t\texpect(pageSource.docs.limit).toBe(3)\n\t\t\texpect(offsetSource.items.map((row) => row.position)).toEqual([5, 6])\n\t\t\texpect(offsetSource.pages.current).toBe(3)\n\t\t\texpect(offsetSource.docs.limit).toBe(2)\n\t\t})\n\t})\n\n\tdescribe('type-level: paginate surface', () => {\n\t\tconst _TestSchema = Schema.from('paginate_type_users')\n\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\t\ttype S = typeof _TestSchema\n\n\t\ttest('Paginated<T> exposes pages, docs, and items', () => {\n\t\t\ttype Page = Paginated<{ id: string }>\n\t\t\texpectTypeOf<Page>().toHaveProperty('pages')\n\t\t\texpectTypeOf<Page>().toHaveProperty('docs')\n\t\t\texpectTypeOf<Page>().toHaveProperty('items')\n\t\t\texpectTypeOf<Page['items']>().toEqualTypeOf<{ id: string }[]>()\n\t\t})\n\n\t\ttest('.paginate() is a no-argument AllBuilder terminal', () => {\n\t\t\ttype Adapter = { findMany: (...a: any[]) => any; count: (...a: any[]) => any }\n\t\t\ttype All = AllBuilderSurface<S, Adapter>\n\t\t\ttype Result = import('./internals/types').SelectedWithPreloads<S, never, []>\n\n\t\t\texpectTypeOf<All['paginate']>().toBeFunction()\n\t\t\texpectTypeOf<Parameters<All['paginate']>>().toEqualTypeOf<[]>()\n\t\t\texpectTypeOf<ReturnType<All['paginate']>>().toEqualTypeOf<Promise<Paginated<Result>>>()\n\t\t})\n\n\t\ttest('.paginate() exists only on AllBuilder', () => {\n\t\t\ttype Adapter = { findMany: (...a: any[]) => any; count: (...a: any[]) => any }\n\t\t\ttype One = OneBuilderSurface<S, Adapter>\n\t\t\ttype OneHasPaginate = 'paginate' extends keyof One ? true : false\n\n\t\t\texpectTypeOf<OneHasPaginate>().toEqualTypeOf<false>()\n\t\t})\n\n\t\ttest('.paginate() is gated on both findMany and count', () => {\n\t\t\tclass FullAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t\tasync count() { return 0 }\n\t\t\t}\n\t\t\tclass CountOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync count() { return 0 }\n\t\t\t}\n\t\t\tclass FindOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype Full = AllBuilderSurface<S, FullAdapter>\n\t\t\ttype MissingFindMany = AllBuilderSurface<S, CountOnlyAdapter>\n\t\t\ttype MissingCount = AllBuilderSurface<S, FindOnlyAdapter>\n\n\t\t\texpectTypeOf<Full['paginate']>().toBeFunction()\n\t\t\texpectTypeOf<MissingFindMany['paginate']>().toBeNever()\n\t\t\texpectTypeOf<MissingCount['paginate']>().toBeNever()\n\t\t})\n\t})\n}\n","import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport type { AnySchema } from '../schema'\n\nexport type ConfigTransform<C> = (config: C, schema: AnySchema) => C\n\nconst store = new AsyncLocalStorage<ConfigTransform<any>[]>()\n\nexport function run<C, T>(transform: ConfigTransform<C>, fn: () => T): T {\n\tconst current = store.getStore() ?? []\n\treturn store.run([...current, transform], fn)\n}\n\nexport function currentTransforms<C>(): ConfigTransform<C>[] {\n\treturn store.getStore() ?? []\n}\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect } = import.meta.vitest\n\n\ttype TestConfig = { prefix: string }\n\tconst dummySchema = { name: 'test' } as AnySchema\n\n\tfunction applyTransforms(base: TestConfig): TestConfig {\n\t\tlet config = base\n\t\tfor (const t of currentTransforms<TestConfig>()) {\n\t\t\tconfig = t(config, dummySchema)\n\t\t}\n\t\treturn config\n\t}\n\n\tdescribe('ALS runtime', () => {\n\t\ttest('reads outside run() see no transforms (default config)', () => {\n\t\t\tconst transforms = currentTransforms<TestConfig>()\n\t\t\texpect(transforms).toEqual([])\n\t\t\texpect(applyTransforms({ prefix: 'base' })).toEqual({ prefix: 'base' })\n\t\t})\n\n\t\ttest('reads inside run() see the override', () => {\n\t\t\tconst result = run<TestConfig, TestConfig>(\n\t\t\t\t(config) => ({ prefix: `scoped_${config.prefix}` }),\n\t\t\t\t() => applyTransforms({ prefix: 'base' }),\n\t\t\t)\n\t\t\texpect(result).toEqual({ prefix: 'scoped_base' })\n\t\t})\n\n\t\ttest('reads after run() returns see no transforms', () => {\n\t\t\trun<TestConfig, void>(\n\t\t\t\t(config) => ({ prefix: `inner_${config.prefix}` }),\n\t\t\t\t() => {},\n\t\t\t)\n\t\t\texpect(applyTransforms({ prefix: 'base' })).toEqual({ prefix: 'base' })\n\t\t})\n\n\t\ttest('nested run() calls compose — inner derives from outer', () => {\n\t\t\tconst result = run<TestConfig, TestConfig>(\n\t\t\t\t(config) => ({ prefix: `a_${config.prefix}` }),\n\t\t\t\t() =>\n\t\t\t\t\trun<TestConfig, TestConfig>(\n\t\t\t\t\t\t(config) => ({ prefix: `b_${config.prefix}` }),\n\t\t\t\t\t\t() => applyTransforms({ prefix: 'base' }),\n\t\t\t\t\t),\n\t\t\t)\n\t\t\texpect(result).toEqual({ prefix: 'b_a_base' })\n\t\t})\n\n\t\ttest('two parallel run() calls do not bleed into each other', async () => {\n\t\t\tconst results = await Promise.all([\n\t\t\t\trun<TestConfig, Promise<TestConfig>>(\n\t\t\t\t\t(config) => ({ prefix: `tenant1_${config.prefix}` }),\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 10))\n\t\t\t\t\t\treturn applyTransforms({ prefix: 'base' })\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\trun<TestConfig, Promise<TestConfig>>(\n\t\t\t\t\t(config) => ({ prefix: `tenant2_${config.prefix}` }),\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 10))\n\t\t\t\t\t\treturn applyTransforms({ prefix: 'base' })\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t])\n\n\t\t\texpect(results[0]).toEqual({ prefix: 'tenant1_base' })\n\t\t\texpect(results[1]).toEqual({ prefix: 'tenant2_base' })\n\t\t})\n\n\t\ttest('overrides survive across awaits inside fn', async () => {\n\t\t\tconst result = await run<TestConfig, Promise<TestConfig>>(\n\t\t\t\t(config) => ({ prefix: `async_${config.prefix}` }),\n\t\t\t\tasync () => {\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, 5))\n\t\t\t\t\tconst mid = applyTransforms({ prefix: 'step1' })\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, 5))\n\t\t\t\t\tconst end = applyTransforms({ prefix: 'step2' })\n\t\t\t\t\treturn { prefix: `${mid.prefix}+${end.prefix}` }\n\t\t\t\t},\n\t\t\t)\n\t\t\texpect(result).toEqual({ prefix: 'async_step1+async_step2' })\n\t\t})\n\t})\n}\n","import type { Pipe } from 'valleyed'\n\nimport type { InferAdapterConfig } from '../adapter'\nimport { currentTransforms, run } from './als'\nimport { SchemaContext, SchemaRef, type HasMethod, type SchemaRefSurface } from './builders'\nimport type { OrmAdapterConfig, OrmAdapterLike } from '../adapters/base'\nimport type { IterationOptions } from '../query-options'\nimport type { AnySchema } from '../schema'\nimport { composeSchemaConfig } from '../schema-validations'\n\nexport type { ConfigTransform } from './als'\n\nexport class Repo<A extends OrmAdapterLike<any>> {\n\treadonly #adapter: A\n\treadonly #defaults: (schema: AnySchema) => InferAdapterConfig<A>\n\treadonly #schemaConfigPipe: Pipe<any, any> | undefined\n\n\tconstructor({ adapter, resolve }: { adapter: A; resolve: (schema: AnySchema) => InferAdapterConfig<A> }) {\n\t\tthis.#adapter = adapter\n\t\tthis.#defaults = resolve\n\t\tthis.#schemaConfigPipe = (adapter as any).schemaConfigPipe\n\t}\n\n\t#getConfig(s: AnySchema): InferAdapterConfig<A> {\n\t\tconst transforms = [...currentTransforms<InferAdapterConfig<A>>()]\n\t\tif (this.#schemaConfigPipe) {\n\t\t\treturn composeSchemaConfig(this.#defaults, transforms, s, this.#schemaConfigPipe) as InferAdapterConfig<A>\n\t\t}\n\t\tlet config = this.#defaults(s)\n\t\tfor (const transform of transforms) {\n\t\t\tconfig = transform(config, s)\n\t\t}\n\t\treturn config\n\t}\n\n\t#getUse(s: AnySchema) {\n\t\treturn this.#adapter.use(s, this.#getConfig(s))\n\t}\n\n\ton<S extends AnySchema>(schema: S): SchemaRefSurface<S, A> {\n\t\treturn new SchemaRef<S, A>(new SchemaContext(schema, (target) => this.#getUse(target))) as unknown as SchemaRefSurface<S, A>\n\t}\n\n\tstatic from<NewA extends OrmAdapterLike<any>>(adapter: NewA): RepoBuilder<NewA> {\n\t\treturn new RepoBuilder<NewA>(adapter)\n\t}\n\n\tasync session<T>(fn: () => Promise<T>): Promise<T> {\n\t\treturn this.#adapter.session?.(fn) ?? fn()\n\t}\n\n\tresolve<T>(resolver: (config: InferAdapterConfig<A>, schema: AnySchema) => InferAdapterConfig<A>, fn: () => T): T {\n\t\treturn run<InferAdapterConfig<A>, T>(resolver, fn)\n\t}\n}\n\nclass RepoBuilder<A extends OrmAdapterLike<any>> {\n\t#adapter: unknown\n\t#resolve: unknown\n\n\tconstructor(adapter?: unknown, resolve?: unknown) {\n\t\tthis.#adapter = adapter\n\t\tthis.#resolve = resolve\n\t}\n\n\tresolve(fn: (schema: AnySchema) => OrmAdapterConfig<A>): RepoBuilder<A> {\n\t\treturn new RepoBuilder<A>(this.#adapter, fn)\n\t}\n\n\tbuild(this: RepoBuilder<A>): RepoSurface<A> {\n\t\treturn new Repo<A>({\n\t\t\tadapter: this.#adapter as any,\n\t\t\tresolve: this.#resolve as any,\n\t\t}) as RepoSurface<A>\n\t}\n}\n\nexport type RepoSurface<A extends OrmAdapterLike<any>> = Repo<A> &\n\t(HasMethod<A, 'session'> extends true ? {} : { session: never })\n\nif (import.meta.vitest) {\n\tconst { describe, test, expect, expectTypeOf, vi } = import.meta.vitest\n\tconst { v } = await import('valleyed')\n\tconst { InMemoryAdapter } = await import('../adapters/in-memory')\n\tconst { OrmAdapter } = await import('../orm-adapter')\n\tconst { OrmValidationError } = await import('../errors')\n\tconst { Instance } = await import('../../instance')\n\tconst { Relations } = await import('../relations')\n\tconst { Schema } = await import('../schema')\n\n\tfunction mockInstance() {\n\t\treturn vi.spyOn(Instance, 'on').mockImplementation(() => {})\n\t}\n\n\tdescribe('Repo.from() and repo.on()', () => {\n\t\ttest('Repo.from(adapter).resolve(...).build() creates a working repo', async () => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst TestSchema = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t\tconst created = await repo.on(TestSchema).one().create({ name: 'Hello' })\n\t\t\texpect(created.name).toBe('Hello')\n\t\t\tconst found = await repo.on(TestSchema).one().id(created.id).find()\n\t\t\texpect(found?.name).toBe('Hello')\n\t\t})\n\n\t\ttest('repo.on(schema) returns a SchemaRef', async () => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst TestSchema = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t\tconst ref = repo.on(TestSchema)\n\t\t\texpect(ref).toBeInstanceOf(SchemaRef)\n\t\t})\n\t})\n\n\tdescribe('clone-on-step: RepoBuilder fan-out independence', () => {\n\t\ttest('.resolve() returns a new builder, not the same instance', () => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst base = Repo.from(adapter)\n\t\t\tconst a = base.resolve((s) => ({ table: s.name }))\n\t\t\texpect(a).not.toBe(base)\n\t\t})\n\t})\n\n\tdescribe('repo/Repo core behavior', () => {\n\t\tlet userCounter = 0\n\t\tlet postCounter = 0\n\t\tlet profileCounter = 0\n\t\tlet orgCounter = 0\n\n\t\tconst UserSchema = Schema.from('users')\n\t\t\t.pk('id', v.string(), () => `u${++userCounter}`)\n\t\t\t.field('email', v.string())\n\t\t\t.field('name', v.string())\n\t\t\t.field('orgId', v.optional(v.string()), { onCreate: () => undefined })\n\t\t\t.field('createdAt', v.number(), { onCreate: () => 1000 })\n\t\t\t.build()\n\n\t\tconst PostSchema = Schema.from('posts')\n\t\t\t.pk('id', v.string(), () => `p${++postCounter}`)\n\t\t\t.field('title', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\tconst ProfileSchema = Schema.from('profiles')\n\t\t\t.pk('id', v.string(), () => `pr${++profileCounter}`)\n\t\t\t.field('bio', v.string())\n\t\t\t.field('userId', v.string())\n\t\t\t.build()\n\n\t\tconst OrgSchema = Schema.from('orgs')\n\t\t\t.pk('id', v.string(), () => `o${++orgCounter}`)\n\t\t\t.field('name', v.string())\n\t\t\t.build()\n\n\t\tconst PersonSchema = Schema.from('people')\n\t\t\t.pk('id', v.string(), () => `person-${++userCounter}`)\n\t\t\t.field('firstName', v.string())\n\t\t\t.field('lastName', v.string())\n\t\t\t.computed('fullName', ['firstName', 'lastName'], v.string(), ({ firstName, lastName }) => `${firstName} ${lastName}`)\n\t\t\t.build()\n\n\t\tconst UserRels = Relations.from(UserSchema)\n\t\t\t.hasMany('posts', PostSchema.fields.userId)\n\t\t\t.hasOne('profile', ProfileSchema.fields.userId)\n\t\t\t.belongsTo('org', UserSchema.fields.orgId, OrgSchema)\n\t\t\t.build()\n\n\t\tfunction makeRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\treturn Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t}\n\n\t\ttest('fluent builders support one/all read chains', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst created = await repo.on(UserSchema).one().create({ email: 'fluent@test.com', name: 'Fluent User' })\n\n\t\t\tconst one = await repo.on(UserSchema).one().id(created.id).select(['id', 'name']).find()\n\t\t\texpect(one).toEqual({ id: created.id, name: 'Fluent User' })\n\n\t\t\tconst all = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('id', created.id))\n\t\t\t\t.orderBy('createdAt', 'desc')\n\t\t\t\t.limit(1)\n\t\t\t\t.select(['id'])\n\t\t\t\t.find()\n\n\t\t\texpect(all).toEqual([{ id: created.id }])\n\t\t})\n\n\t\ttest('all().count() returns filtered counts and ignores query-shape state', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'alice@count.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'bob@count.com', name: 'Bob' },\n\t\t\t\t\t{ email: 'alice2@count.com', name: 'Alice' },\n\t\t\t\t])\n\n\t\t\tconst count = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('name', 'Alice'))\n\t\t\t\t.select(['does_not_exist'] as any)\n\t\t\t\t.preload(['does_not_exist'] as any)\n\t\t\t\t.orderBy('does_not_exist')\n\t\t\t\t.limit(0)\n\t\t\t\t.page(0)\n\t\t\t\t.count()\n\n\t\t\texpect(count).toBe(2)\n\t\t})\n\n\t\ttest('all().count() validates only the filter', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo.on(UserSchema).one().create({ email: 'bad-filter-count@test.com', name: 'Alice' })\n\n\t\t\tawait expect(\n\t\t\t\trepo\n\t\t\t\t\t.on(UserSchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.where((q) => q.eq('does_not_exist' as any, 'x'))\n\t\t\t\t\t.select(['also_missing'] as any)\n\t\t\t\t\t.preload(['also_missing'] as any)\n\t\t\t\t\t.limit(0)\n\t\t\t\t\t.count(),\n\t\t\t).rejects.toBeInstanceOf(OrmValidationError)\n\t\t})\n\n\t\ttest('all().count() is distinct from aggregate count', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'one@agg-count.com', name: 'Same' },\n\t\t\t\t\t{ email: 'two@agg-count.com', name: 'Same' },\n\t\t\t\t])\n\n\t\t\tconst count = await repo.on(UserSchema).all().where((q) => q.eq('name', 'Same')).count()\n\t\t\tconst aggregate = await repo.on(UserSchema).aggregate().count('total').where((q) => q.eq('name', 'Same')).run()\n\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(aggregate).toEqual({ total: 2 })\n\t\t})\n\n\t\ttest('builder snapshots are immutable across chain branches', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'alice@branch.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'bob@branch.com', name: 'Bob' },\n\t\t\t\t])\n\n\t\t\tconst base = repo.on(UserSchema).all()\n\t\t\tconst branchA = base.where((q) => q.eq('name', 'Alice')).select(['id'])\n\t\t\tconst branchB = base.where((q) => q.eq('name', 'Bob')).select(['name'])\n\n\t\t\tconst rowsA = await branchA.find()\n\t\t\tconst rowsB = await branchB.find()\n\n\t\t\texpect(rowsA).toHaveLength(1)\n\t\t\texpect(rowsB).toHaveLength(1)\n\t\t\texpect(rowsA.every((r) => 'id' in r && !('name' in r))).toBe(true)\n\t\t\texpect(rowsB).toEqual([{ name: 'Bob' }])\n\t\t})\n\n\t\ttest('fluent builders support write chains with preloads', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst org = await repo.on(OrgSchema).one().create({ name: 'Fluent Org' })\n\n\t\t\tconst user = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.one()\n\t\t\t\t.preload([UserRels.org])\n\t\t\t\t.create({ email: 'writer@test.com', name: 'Writer', orgId: org.id })\n\n\t\t\texpect((user.org as any).name).toBe('Fluent Org')\n\n\t\t\tconst updated = await repo.on(UserSchema).one().id(user.id).select(['id', 'name']).update({ name: 'Updated Writer' })\n\n\t\t\texpect(updated).toEqual({ id: user.id, name: 'Updated Writer' })\n\n\t\t\tconst deleted = await repo.on(UserSchema).one().id(user.id).select(['id']).delete()\n\t\t\texpect(deleted).toEqual({ id: user.id })\n\t\t})\n\n\t\ttest('session supports multi-operation writes with fluent builders', async () => {\n\t\t\tconst repo = makeRepo()\n\n\t\t\tconst insertedId = await repo.session(async () => {\n\t\t\t\tconst created = await repo.on(UserSchema).one().create({ email: 'tx@fluent.com', name: 'Tx Fluent' })\n\t\t\t\tawait repo.on(UserSchema).one().id(created.id).update({ name: 'Tx Fluent Updated' })\n\t\t\t\treturn created.id\n\t\t\t})\n\n\t\t\tconst persisted = await repo.on(UserSchema).one().id(insertedId).select(['name']).find()\n\t\t\texpect(persisted).toEqual({ name: 'Tx Fluent Updated' })\n\t\t})\n\n\t\ttest('create/find/update/delete flows work', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst user = await repo.on(UserSchema).one().create({ email: 'a@b.com', name: 'Alice' })\n\t\t\texpect(user.id).toMatch(/^u\\d+$/)\n\t\t\texpect(user.createdAt).toBe(1000)\n\n\t\t\tconst found = await repo.on(UserSchema).one().id(user.id).find()\n\t\t\texpect(found?.id).toBe(user.id)\n\n\t\t\tconst updated = await repo.on(UserSchema).one().id(user.id).update({ name: 'Updated' })\n\t\t\texpect(updated?.name).toBe('Updated')\n\n\t\t\tconst deleted = await repo.on(UserSchema).one().id(user.id).delete()\n\t\t\texpect(deleted?.id).toBe(user.id)\n\t\t})\n\n\t\ttest('findById, updateById, and deleteById target the schema primary key', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst user = await repo.on(UserSchema).one().create({ email: 'id@test.com', name: 'ById' })\n\n\t\t\tconst found = await repo.on(UserSchema).one().id(user.id).find()\n\t\t\texpect(found?.id).toBe(user.id)\n\n\t\t\tconst updated = await repo.on(UserSchema).one().id(user.id).update({ name: 'Changed' })\n\t\t\texpect(updated?.name).toBe('Changed')\n\n\t\t\tconst deleted = await repo.on(UserSchema).one().id(user.id).delete()\n\t\t\texpect(deleted?.id).toBe(user.id)\n\t\t\texpect(await repo.on(UserSchema).one().id(user.id).find()).toBeNull()\n\t\t})\n\n\t\ttest('createMany, findMany and upsertOne work', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@b.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'b@c.com', name: 'Bob' },\n\t\t\t\t])\n\t\t\texpect(await repo.on(UserSchema).all().find()).toHaveLength(2)\n\n\t\t\tconst inserted = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('id', 'u-fixed'))\n\t\t\t\t.upsert({ create: { email: 'new@test.com', name: 'New' } })\n\t\t\texpect(inserted.name).toBe('New')\n\t\t})\n\n\t\ttest('accepts chainable where input for filters and options', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@b.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'b@c.com', name: 'Bob' },\n\t\t\t\t])\n\n\t\t\tconst rows = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.or([(g) => g.eq('name', 'Alice'), (g) => g.eq('name', 'Bob')]))\n\t\t\t\t.orderBy('name', 'desc')\n\t\t\t\t.limit(1)\n\t\t\t\t.find()\n\n\t\t\texpect(rows).toHaveLength(1)\n\t\t\texpect(rows[0].name).toBe('Bob')\n\t\t})\n\n\t\ttest('all().iterate uses adapter iterateMany without findMany emulation', async () => {\n\t\t\tconst TestSchema = Schema.from('iter_repo_users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.field('age', v.number())\n\t\t\t\t.build()\n\t\t\tlet findManyCalls = 0\n\t\t\tlet capturedOptions: unknown\n\n\t\t\tclass IterAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly queryableOps = ['gt'] as const\n\t\t\t\tasync findMany(): Promise<Record<string, unknown>[]> {\n\t\t\t\t\tfindManyCalls += 1\n\t\t\t\t\tthrow new Error('findMany should not be called')\n\t\t\t\t}\n\t\t\t\tasync *iterateMany(_s: AnySchema, _c: unknown, _f: unknown, options?: unknown) {\n\t\t\t\t\tcapturedOptions = options\n\t\t\t\t\tyield { id: 'u3', name: 'Carol', age: 40 }\n\t\t\t\t\tyield { id: 'u1', name: 'Alice', age: 30 }\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adapter = new (IterAdapter as any)() as IterAdapter\n\t\t\tconst repo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t\tconst rows: Array<{ id: string; name: string; age: number }> = []\n\t\t\tfor await (const row of repo.on(TestSchema).all().where((q) => q.gt('age', 19)).orderBy('age', 'desc').offset(1).limit(2).iterate()) {\n\t\t\t\trows.push(row)\n\t\t\t}\n\n\t\t\texpect(findManyCalls).toBe(0)\n\t\t\texpect(capturedOptions).toMatchObject({ orderBy: [{ field: 'age', direction: 'desc' }], offset: 1, limit: 2 })\n\t\t\texpect(rows.map((row) => row.name)).toEqual(['Carol', 'Alice'])\n\t\t})\n\n\t\ttest('all().iterate accepts batchSize and forwards it only to iterateMany', async () => {\n\t\t\tconst TestSchema = Schema.from('iter_batch_users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst events: unknown[] = []\n\n\t\t\tclass IterAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany(_s: AnySchema, _c: unknown, _f: unknown, options?: unknown) {\n\t\t\t\t\tevents.push({ method: 'findMany', options })\n\t\t\t\t\treturn [{ id: 'u1', name: 'Alice' }]\n\t\t\t\t}\n\t\t\t\tasync count(_s: AnySchema, _c: unknown, _f: unknown) {\n\t\t\t\t\tevents.push({ method: 'count' })\n\t\t\t\t\treturn 1\n\t\t\t\t}\n\t\t\t\tasync *iterateMany(_s: AnySchema, _c: unknown, _f: unknown, options?: unknown) {\n\t\t\t\t\tevents.push({ method: 'iterateMany', options })\n\t\t\t\t\tyield { id: 'u1', name: 'Alice' }\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adapter = new (IterAdapter as any)() as IterAdapter\n\t\t\tconst repo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\n\t\t\tconst iterated: Array<{ id: string; name: string }> = []\n\t\t\tfor await (const row of repo.on(TestSchema).all().limit(10).iterate({ batchSize: 3 })) {\n\t\t\t\titerated.push(row)\n\t\t\t}\n\t\t\tawait repo.on(TestSchema).all().limit(10).find()\n\t\t\tawait repo.on(TestSchema).all().limit(10).paginate()\n\n\t\t\texpect(iterated).toEqual([{ id: 'u1', name: 'Alice' }])\n\t\t\texpect(events).toEqual([\n\t\t\t\t{ method: 'iterateMany', options: expect.objectContaining({ limit: 10, batchSize: 3 }) },\n\t\t\t\t{ method: 'findMany', options: expect.not.objectContaining({ batchSize: expect.anything() }) },\n\t\t\t\t{ method: 'findMany', options: expect.not.objectContaining({ batchSize: expect.anything() }) },\n\t\t\t\t{ method: 'count' },\n\t\t\t])\n\t\t})\n\n\t\ttest('all().iterate validates batchSize before adapter iteration runs', async () => {\n\t\t\tconst TestSchema = Schema.from('iter_invalid_batch_users')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tlet iterateManyCalls = 0\n\n\t\t\tclass IterAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync *iterateMany() {\n\t\t\t\t\titerateManyCalls += 1\n\t\t\t\t\tyield { id: 'u1', name: 'Alice' }\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst adapter = new (IterAdapter as any)() as IterAdapter\n\t\t\tconst repo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\n\t\t\tfor (const batchSize of [0, -1, 1.5, Number.MAX_SAFE_INTEGER + 1, Number.POSITIVE_INFINITY, Number.NaN, '2'] as const) {\n\t\t\t\ttry {\n\t\t\t\t\tfor await (const _row of repo.on(TestSchema).all().iterate({ batchSize } as any)) {\n\t\t\t\t\t\tvoid _row\n\t\t\t\t\t}\n\t\t\t\t\texpect.unreachable(`batchSize ${String(batchSize)} should have failed`)\n\t\t\t\t} catch (error) {\n\t\t\t\t\texpect(error).toBeInstanceOf(OrmValidationError)\n\t\t\t\t\tconst err = error as InstanceType<typeof OrmValidationError>\n\t\t\t\t\texpect(err.kind).toBe('query-shape')\n\t\t\t\t\texpect(err.operation).toBe('iterate')\n\t\t\t\t\texpect(err.failures).toEqual([\n\t\t\t\t\t\texpect.objectContaining({ option: 'batchSize' }),\n\t\t\t\t\t])\n\t\t\t\t}\n\t\t\t}\n\t\t\texpect(iterateManyCalls).toBe(0)\n\t\t})\n\n\t\ttest('all().iterate yields selected and preloaded documents one at a time', async () => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tlet preloadReads = 0\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((schema: AnySchema, config: any) => {\n\t\t\t\tconst use = origUse(schema, config)\n\t\t\t\treturn {\n\t\t\t\t\t...use,\n\t\t\t\t\tfindMany: async (...args: any[]) => {\n\t\t\t\t\t\tif (schema === PostSchema) preloadReads += 1\n\t\t\t\t\t\treturn use.findMany(...(args as [any, any]))\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t})\n\t\t\tconst repo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t\tconst alice = await repo.on(UserSchema).one().create({ email: 'alice@iter.com', name: 'Alice' })\n\t\t\tconst bob = await repo.on(UserSchema).one().create({ email: 'bob@iter.com', name: 'Bob' })\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'A1', userId: alice.id })\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'A2', userId: alice.id })\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'B1', userId: bob.id })\n\n\t\t\tconst rows: any[] = []\n\t\t\tconst iterator = repo.on(UserSchema).all().orderBy('name', 'asc').select(['id', 'name']).preload([UserRels.posts]).iterate({ batchSize: 1 })\n\t\t\texpect(iterator[Symbol.asyncIterator]()).toBe(iterator)\n\t\t\tfor await (const row of iterator) rows.push(row)\n\n\t\t\texpect(rows).toHaveLength(2)\n\t\t\texpect(rows.map((row) => row.name)).toEqual(['Alice', 'Bob'])\n\t\t\texpect(rows[0]).not.toHaveProperty('email')\n\t\t\texpect(rows[0].posts.map((post) => post.title)).toEqual(['A1', 'A2'])\n\t\t\texpect(rows[1].posts.map((post) => post.title)).toEqual(['B1'])\n\t\t\texpect(preloadReads).toBe(2)\n\t\t})\n\n\t\ttest('all().iterate honors page-based offsets and validates read query shape', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo.on(UserSchema).all().create([\n\t\t\t\t{ email: 'u1@iter.com', name: 'A' },\n\t\t\t\t{ email: 'u2@iter.com', name: 'B' },\n\t\t\t\t{ email: 'u3@iter.com', name: 'C' },\n\t\t\t\t{ email: 'u4@iter.com', name: 'D' },\n\t\t\t\t{ email: 'u5@iter.com', name: 'E' },\n\t\t\t])\n\n\t\t\tconst paged: string[] = []\n\t\t\tfor await (const row of repo.on(UserSchema).all().orderBy('name', 'asc').limit(2).page(2).iterate()) {\n\t\t\t\tpaged.push(row.name)\n\t\t\t}\n\t\t\texpect(paged).toEqual(['C', 'D'])\n\n\t\t\ttry {\n\t\t\t\tfor await (const _row of repo.on(UserSchema).all().select(['missing' as any]).iterate()) {\n\t\t\t\t\tvoid _row\n\t\t\t\t}\n\t\t\t\texpect.unreachable('iterate should validate selected fields before reading')\n\t\t\t} catch (error) {\n\t\t\t\texpect(error).toBeInstanceOf(OrmValidationError)\n\t\t\t\texpect((error as InstanceType<typeof OrmValidationError>).kind).toBe('query-shape')\n\t\t\t}\n\t\t})\n\n\t\ttest('resolve chains adapter config transforms', async () => {\n\t\t\tconst seenConfigs: unknown[] = []\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((s: any, config: any) => {\n\t\t\t\tseenConfigs.push(config)\n\t\t\t\treturn origUse(s, config)\n\t\t\t})\n\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tawait repo.resolve(\n\t\t\t\t(config) => ({ table: `a_${config.table}` }),\n\t\t\t\tasync () => {\n\t\t\t\t\tawait repo.resolve(\n\t\t\t\t\t\t(config) => ({ table: `b_${config.table}` }),\n\t\t\t\t\t\tasync () => {\n\t\t\t\t\t\t\tawait repo.on(UserSchema).all().find()\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t},\n\t\t\t)\n\n\t\t\texpect(seenConfigs[0]).toEqual({ table: 'b_a_users' })\n\t\t})\n\n\t\ttest('session returns callback value and persists writes', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tlet insertedId = ''\n\t\t\tconst result = await repo.session(async () => {\n\t\t\t\tconst inserted = await repo.on(UserSchema).one().create({ email: 't@test.com', name: 'TxUser' })\n\t\t\t\tinsertedId = inserted.id\n\t\t\t\treturn 42\n\t\t\t})\n\n\t\t\texpect(result).toBe(42)\n\t\t\texpect(await repo.on(UserSchema).one().id(insertedId).find()).not.toBeNull()\n\t\t})\n\n\t\ttest('preloads can be resolved on mutation methods', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst org = await repo.on(OrgSchema).one().create({ name: 'Corp' })\n\t\t\tconst user = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.one()\n\t\t\t\t.preload([UserRels.org])\n\t\t\t\t.create({ email: 'u@test.com', name: 'User', orgId: org.id })\n\t\t\texpect((user.org as any).name).toBe('Corp')\n\n\t\t\tawait repo.on(PostSchema).one().create({ title: 'Post', userId: user.id })\n\t\t\tconst updated = await repo.on(UserSchema).one().id(user.id).preload([UserRels.posts]).update({ name: 'Updated' })\n\t\t\texpect(updated?.posts).toHaveLength(1)\n\t\t})\n\n\t\ttest('in-memory adapter without crud.raw collapses schemaRef.raw to never', () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst ref = repo.on(UserSchema)\n\t\t\texpectTypeOf(ref.raw).toBeNever()\n\t\t})\n\n\t\ttest('raw forwards user args through adapter.use to adapter.raw', async () => {\n\t\t\tlet capturedArgs: unknown[] = []\n\t\t\tconst spy = mockInstance()\n\t\t\tclass RawAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t\tasync raw(_s: any, _c: any, command: string, params: unknown[]) {\n\t\t\t\t\tcapturedArgs = [_s, _c, command, params]\n\t\t\t\t\treturn { rows: [{ id: '1' }] }\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst rawAdapter = new (RawAdapter as any)() as InstanceType<typeof RawAdapter>\n\t\t\tspy.mockRestore()\n\n\t\t\tconst repo = Repo.from(rawAdapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t\tconst TestSchema = Schema.from('raw_test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.build()\n\n\t\t\tconst result = await repo.on(TestSchema).raw('SELECT * FROM raw_test WHERE id = $1', ['abc'])\n\t\t\texpect(capturedArgs[0]).toBe(TestSchema)\n\t\t\texpect(capturedArgs[1]).toEqual({ table: 'raw_test' })\n\t\t\texpect(capturedArgs[2]).toBe('SELECT * FROM raw_test WHERE id = $1')\n\t\t\texpect(capturedArgs[3]).toEqual(['abc'])\n\t\t\texpect(result).toEqual({ rows: [{ id: '1' }] })\n\t\t})\n\n\t\ttest('raw with single-arg adapter (mongo-style) forwards correctly', async () => {\n\t\t\tlet capturedPipeline: unknown = undefined\n\t\t\tconst spy = mockInstance()\n\t\t\tclass MongoStyleAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ col: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync raw(_s: any, _c: any, pipeline: Record<string, unknown>[]) {\n\t\t\t\t\tcapturedPipeline = pipeline\n\t\t\t\t\treturn [{ total: 42 }]\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst mongoStyleAdapter = new (MongoStyleAdapter as any)() as InstanceType<typeof MongoStyleAdapter>\n\t\t\tspy.mockRestore()\n\n\t\t\tconst repo = Repo.from(mongoStyleAdapter)\n\t\t\t\t.resolve(() => ({ col: 'test' }))\n\t\t\t\t.build()\n\t\t\tconst TestSchema = Schema.from('mongo_test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.build()\n\n\t\t\tconst result = await repo.on(TestSchema).raw([{ $count: 'total' }])\n\t\t\texpect(capturedPipeline).toEqual([{ $count: 'total' }])\n\t\t\texpect(result).toEqual([{ total: 42 }])\n\t\t})\n\n\t\ttest('computed fields are derived and shaped correctly when selected', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst created = await repo.on(PersonSchema).one().create({ firstName: 'Ada', lastName: 'Lovelace' })\n\t\t\tconst rows = await repo.on(PersonSchema).all().select(['id', 'fullName']).find()\n\n\t\t\texpect(rows).toEqual([{ id: created.id, fullName: 'Ada Lovelace' }])\n\t\t})\n\n\t\ttest('computed field selection auto-includes dependencies for adapter reads', async () => {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\tlet seenSelect: string[] | undefined\n\t\t\t;(adapter as any).use = vi.fn((schema: any, config: any) => {\n\t\t\t\tconst use = origUse(schema, config)\n\t\t\t\treturn {\n\t\t\t\t\t...use,\n\t\t\t\t\tfindMany: async (filter: any, options: any) => {\n\t\t\t\t\t\tseenSelect = options?.select as string[] | undefined\n\t\t\t\t\t\treturn use.findMany(filter, options)\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t\tawait repo.on(PersonSchema).one().create({ firstName: 'Grace', lastName: 'Hopper' })\n\t\t\tawait repo.on(PersonSchema).all().select(['id', 'fullName']).find()\n\n\t\t\texpect(seenSelect).toEqual(expect.arrayContaining(['id', 'firstName', 'lastName']))\n\t\t})\n\n\t\ttest('unknown selected fields fail fast', async () => {\n\t\t\tconst { EquippedError } = await import('../../errors')\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo.on(PersonSchema).one().create({ firstName: 'Ada', lastName: 'Lovelace' })\n\n\t\t\tawait expect(\n\t\t\t\trepo\n\t\t\t\t\t.on(PersonSchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.select(['unknownField' as any])\n\t\t\t\t\t.find(),\n\t\t\t).rejects.toBeInstanceOf(EquippedError)\n\t\t})\n\n\t\ttest('missing computed dependencies in adapter output fail fast', async () => {\n\t\t\tconst { EquippedError } = await import('../../errors')\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((schema: any, config: any) => {\n\t\t\t\tconst use = origUse(schema, config)\n\t\t\t\treturn {\n\t\t\t\t\t...use,\n\t\t\t\t\tfindMany: async (filter: any, options: any) => {\n\t\t\t\t\t\tconst rows = await use.findMany(filter, options)\n\t\t\t\t\t\treturn rows.map((row: any) => {\n\t\t\t\t\t\t\tconst next = { ...row }\n\t\t\t\t\t\t\tdelete (next as any).lastName\n\t\t\t\t\t\t\treturn next\n\t\t\t\t\t\t})\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t\tawait repo.on(PersonSchema).one().create({ firstName: 'Katherine', lastName: 'Johnson' })\n\n\t\t\tawait expect(repo.on(PersonSchema).all().select(['fullName']).find()).rejects.toBeInstanceOf(EquippedError)\n\t\t})\n\n\t\ttest('one().id().find() returns seeded document and null for missing', async () => {\n\t\t\tconst TestSchema = Schema.from('findbytest')\n\t\t\t\t.pk('id', v.string(), () => 'gen')\n\t\t\t\t.build()\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tconst use = adapter.use(TestSchema, { table: 'findbytest' })\n\t\t\tawait use.createOne({ id: 'x' })\n\n\t\t\tconst found = await repo.on(TestSchema).one().id('x').find()\n\t\t\texpect(found).toEqual({ id: 'x' })\n\n\t\t\tconst missing = await repo.on(TestSchema).one().id('missing').find()\n\t\t\texpect(missing).toBeNull()\n\t\t})\n\n\t\tdescribe('schema-per-call create path', () => {\n\t\t\ttest('createOne round-trip via one().id().find()', async () => {\n\t\t\t\tconst repo = makeRepo()\n\t\t\t\tconst inserted = await repo.on(UserSchema).one().create({ email: 'rt@test.com', name: 'RoundTrip' })\n\t\t\t\texpect(inserted.email).toBe('rt@test.com')\n\t\t\t\texpect(inserted.name).toBe('RoundTrip')\n\t\t\t\texpect(inserted.id).toBeDefined()\n\n\t\t\t\tconst found = await repo.on(UserSchema).one().id(inserted.id).find()\n\t\t\t\texpect(found).not.toBeNull()\n\t\t\t\texpect(found!.id).toBe(inserted.id)\n\t\t\t\texpect(found!.email).toBe('rt@test.com')\n\t\t\t})\n\n\t\t\ttest('createOne injects onCreate defaults for missing fields', async () => {\n\t\t\t\tconst repo = makeRepo()\n\t\t\t\tconst inserted = await repo.on(UserSchema).one().create({ email: 'defaults@test.com', name: 'Defaults' })\n\t\t\t\texpect(inserted.createdAt).toBe(1000)\n\t\t\t\texpect(inserted.id).toBeDefined()\n\t\t\t})\n\n\t\t\ttest('createOne throws OrmValidationError with kind validation and field populated', async () => {\n\t\t\t\tconst { OrmValidationError } = await import('../errors')\n\t\t\t\tconst repo = makeRepo()\n\t\t\t\ttry {\n\t\t\t\t\tawait repo.on(UserSchema).one().create({ email: 123 as any, name: 'Bad' })\n\t\t\t\t\texpect.unreachable()\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\t\texpect(err.operation).toBe('createOne')\n\t\t\t\t\texpect(err.schema).toBe('users')\n\t\t\t\t\texpect(err.failures.length).toBeGreaterThan(0)\n\t\t\t\t\texpect(err.failures[0].field).toBe('email')\n\t\t\t\t}\n\t\t\t})\n\n\t\t\ttest('createMany round-trip via one().id().find()', async () => {\n\t\t\t\tconst repo = makeRepo()\n\t\t\t\tconst inserted = await repo.on(UserSchema).all().create([\n\t\t\t\t\t{ email: 'a@test.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'b@test.com', name: 'Bob' },\n\t\t\t\t])\n\t\t\t\texpect(inserted).toHaveLength(2)\n\n\t\t\t\tconst foundA = await repo.on(UserSchema).one().id(inserted[0].id).find()\n\t\t\t\tconst foundB = await repo.on(UserSchema).one().id(inserted[1].id).find()\n\t\t\t\texpect(foundA!.email).toBe('a@test.com')\n\t\t\t\texpect(foundB!.email).toBe('b@test.com')\n\t\t\t})\n\n\t\t\ttest('createMany collects all failures with rowIndex and throws single OrmValidationError', async () => {\n\t\t\t\tconst { OrmValidationError } = await import('../errors')\n\t\t\t\tconst repo = makeRepo()\n\t\t\t\ttry {\n\t\t\t\t\tawait repo.on(UserSchema).all().create([\n\t\t\t\t\t\t{ email: 'good@test.com', name: 'Good' },\n\t\t\t\t\t\t{ email: 123 as any, name: 'Bad' },\n\t\t\t\t\t\t{ email: 'also-bad' as any, name: 456 as any },\n\t\t\t\t\t])\n\t\t\t\t\texpect.unreachable()\n\t\t\t\t} catch (e) {\n\t\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\t\texpect(err.kind).toBe('validation')\n\t\t\t\t\texpect(err.operation).toBe('createMany')\n\t\t\t\t\texpect(err.schema).toBe('users')\n\t\t\t\t\tconst rowIndices = err.failures.map((f) => f.rowIndex)\n\t\t\t\t\texpect(rowIndices).toContain(1)\n\t\t\t\t\texpect(rowIndices).toContain(2)\n\t\t\t\t\texpect(rowIndices).not.toContain(0)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\ttest('one().id().find() returns null for non-existent pk', async () => {\n\t\t\t\tconst repo = makeRepo()\n\t\t\t\tconst result = await repo.on(UserSchema).one().id('nonexistent').find()\n\t\t\t\texpect(result).toBeNull()\n\t\t\t})\n\n\t\t\ttest('createMany with onCreate defaults applied to all rows', async () => {\n\t\t\t\tconst repo = makeRepo()\n\t\t\t\tconst inserted = await repo.on(UserSchema).all().create([\n\t\t\t\t\t{ email: 'x@test.com', name: 'X' },\n\t\t\t\t\t{ email: 'y@test.com', name: 'Y' },\n\t\t\t\t])\n\t\t\t\texpect(inserted[0].createdAt).toBe(1000)\n\t\t\t\texpect(inserted[1].createdAt).toBe(1000)\n\t\t\t\texpect(inserted[0].id).toBeDefined()\n\t\t\t\texpect(inserted[1].id).toBeDefined()\n\t\t\t})\n\t\t})\n\n\t\ttest('one().id().delete() removes and returns document, null for missing', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst user = await repo.on(UserSchema).one().create({ email: 'del@test.com', name: 'ToDelete' })\n\n\t\t\tconst deleted = await repo.on(UserSchema).one().id(user.id).delete()\n\t\t\texpect(deleted).toEqual(expect.objectContaining({ id: user.id, name: 'ToDelete' }))\n\n\t\t\tconst found = await repo.on(UserSchema).one().id(user.id).find()\n\t\t\texpect(found).toBeNull()\n\n\t\t\tconst missing = await repo.on(UserSchema).one().id('nonexistent').delete()\n\t\t\texpect(missing).toBeNull()\n\t\t})\n\n\t\ttest('one().where().update() updates first matching document via filter', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@test.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'b@test.com', name: 'Bob' },\n\t\t\t\t])\n\n\t\t\tconst updated = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('name', 'Alice'))\n\t\t\t\t.update({ name: 'Alicia' })\n\t\t\texpect(updated?.name).toBe('Alicia')\n\t\t})\n\n\t\ttest('one().where().update() with non-unique filter selects first match', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@test.com', name: 'Same' },\n\t\t\t\t\t{ email: 'b@test.com', name: 'Same' },\n\t\t\t\t])\n\n\t\t\tconst updated = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('name', 'Same'))\n\t\t\t\t.update({ name: 'Changed' })\n\t\t\texpect(updated?.name).toBe('Changed')\n\n\t\t\tconst all = await repo.on(UserSchema).all().find()\n\t\t\tconst changedCount = all.filter((u) => u.name === 'Changed').length\n\t\t\texpect(changedCount).toBe(1)\n\t\t})\n\n\t\ttest('all().where().update() updates all matching documents', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@test.com', name: 'Same' },\n\t\t\t\t\t{ email: 'b@test.com', name: 'Same' },\n\t\t\t\t\t{ email: 'c@test.com', name: 'Different' },\n\t\t\t\t])\n\n\t\t\tconst updated = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('name', 'Same'))\n\t\t\t\t.update({ name: 'Updated' })\n\t\t\texpect(updated).toHaveLength(2)\n\t\t\texpect(updated.every((u) => u.name === 'Updated')).toBe(true)\n\t\t})\n\n\t\ttest('all().where().update() applies auto-bump for onUpdate fields', async () => {\n\t\t\tconst AutoSchema = Schema.from('auto')\n\t\t\t\t.pk('id', v.string(), () => `a${++userCounter}`)\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.field('updatedAt', v.number(), { onCreate: () => 0, onUpdate: () => 9999 })\n\t\t\t\t.build()\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo.on(AutoSchema).one().create({ name: 'A' })\n\n\t\t\tconst updated = await repo\n\t\t\t\t.on(AutoSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('name', 'A'))\n\t\t\t\t.update({ name: 'B' })\n\t\t\texpect(updated[0].updatedAt).toBe(9999)\n\t\t})\n\n\t\ttest('one().where().delete() removes first matching document', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@test.com', name: 'Alice' },\n\t\t\t\t\t{ email: 'b@test.com', name: 'Bob' },\n\t\t\t\t])\n\n\t\t\tconst deleted = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('name', 'Alice'))\n\t\t\t\t.delete()\n\t\t\texpect(deleted?.name).toBe('Alice')\n\n\t\t\tconst remaining = await repo.on(UserSchema).all().find()\n\t\t\texpect(remaining).toHaveLength(1)\n\t\t})\n\n\t\ttest('all().where().delete() removes all matching and returns them', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ email: 'a@test.com', name: 'ToDelete' },\n\t\t\t\t\t{ email: 'b@test.com', name: 'ToDelete' },\n\t\t\t\t\t{ email: 'c@test.com', name: 'Keep' },\n\t\t\t\t])\n\n\t\t\tconst deleted = await repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('name', 'ToDelete'))\n\t\t\t\t.delete()\n\t\t\texpect(deleted).toHaveLength(2)\n\n\t\t\tconst remaining = await repo.on(UserSchema).all().find()\n\t\t\texpect(remaining).toHaveLength(1)\n\t\t\texpect(remaining[0].name).toBe('Keep')\n\t\t})\n\n\t\ttest('round-trip update via filter preserves data integrity', async () => {\n\t\t\tconst repo = makeRepo()\n\t\t\tconst user = await repo.on(UserSchema).one().create({ email: 'rt@test.com', name: 'Original' })\n\n\t\t\tawait repo\n\t\t\t\t.on(UserSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('id', user.id))\n\t\t\t\t.update({ name: 'Modified' })\n\n\t\t\tconst found = await repo.on(UserSchema).one().id(user.id).find()\n\t\t\texpect(found?.name).toBe('Modified')\n\t\t\texpect(found?.email).toBe('rt@test.com')\n\t\t})\n\t})\n\n\tdescribe('type-level: per-op gating on FilterGroup', () => {\n\t\ttest('undeclared filter op is never on GatedFilterGroup', () => {\n\t\t\tconst { GatedFilterGroup: _type } = {} as any\n\t\t\ttype EqOnlyOps = readonly ['eq']\n\t\t\ttype Gated = import('../filter').GatedFilterGroup<EqOnlyOps>\n\t\t\texpectTypeOf<Gated['eq']>().not.toBeNever()\n\t\t\texpectTypeOf<Gated['ne']>().toBeNever()\n\t\t\texpectTypeOf<Gated['gt']>().toBeNever()\n\t\t\texpectTypeOf<Gated['and']>().not.toBeNever()\n\t\t\texpectTypeOf<Gated['or']>().not.toBeNever()\n\t\t})\n\n\t\ttest('all declared ops are available, undeclared are never', () => {\n\t\t\ttype TwoOps = readonly ['eq', 'gt']\n\t\t\ttype Gated = import('../filter').GatedFilterGroup<TwoOps>\n\t\t\texpectTypeOf<Gated['eq']>().not.toBeNever()\n\t\t\texpectTypeOf<Gated['gt']>().not.toBeNever()\n\t\t\texpectTypeOf<Gated['ne']>().toBeNever()\n\t\t\texpectTypeOf<Gated['lt']>().toBeNever()\n\t\t\texpectTypeOf<Gated['in']>().toBeNever()\n\t\t})\n\t})\n\n\tdescribe('type-level: missing queryableOps yields empty ops', () => {\n\t\ttest('adapter without queryableOps yields empty InferAdapterQueryableOps', () => {\n\t\t\tclass MinimalAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = [] as const\n\t\t\t}\n\t\t\ttype Ops = import('../adapter').InferAdapterQueryableOps<MinimalAdapter>\n\t\t\texpectTypeOf<Ops>().toEqualTypeOf<readonly []>()\n\t\t})\n\t})\n\n\tdescribe('type-level: method gating via class-based adapter', () => {\n\t\ttest('adapter with findMany enables all().find()', () => {\n\t\t\tclass FindAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = FindAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<All['find']>().toBeFunction()\n\t\t})\n\n\t\ttest('adapter with iterateMany enables all().iterate(options?)', () => {\n\t\t\tclass IterateAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync *iterateMany() {}\n\t\t\t}\n\t\t\ttype A = IterateAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<All['iterate']>().toBeFunction()\n\t\t\texpectTypeOf<Parameters<All['iterate']>>().toEqualTypeOf<[options?: IterationOptions]>()\n\t\t\texpectTypeOf<ReturnType<All['iterate']>>().toEqualTypeOf<AsyncGenerator<import('./internals/types').SelectedWithPreloads<S, never, []>, void, void>>()\n\t\t})\n\n\t\ttest('iterate exists only on AllBuilder and has no CDC-style sibling methods', () => {\n\t\t\tclass IterateAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync *iterateMany() {}\n\t\t\t}\n\t\t\ttype A = IterateAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\ttype OneHasIterate = 'iterate' extends keyof One ? true : false\n\t\t\ttype CdcKeys = Extract<'stream' | 'watch' | 'subscribe' | 'changeFeed', keyof All>\n\t\t\texpectTypeOf<OneHasIterate>().toEqualTypeOf<false>()\n\t\t\texpectTypeOf<CdcKeys>().toBeNever()\n\t\t})\n\n\t\ttest('missing iterateMany collapses all().iterate to never', () => {\n\t\t\tclass FindOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = FindOnlyAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<All['iterate']>().toBeNever()\n\t\t})\n\n\t\ttest('missing updateMany collapses one().update and all().update to never', () => {\n\t\t\tclass ReadOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = ReadOnlyAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['update']>().toBeNever()\n\t\t\texpectTypeOf<All['update']>().toBeNever()\n\t\t})\n\n\t\ttest('missing deleteMany collapses one().delete and all().delete to never', () => {\n\t\t\tclass NoDeleteAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = NoDeleteAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['delete']>().toBeNever()\n\t\t\texpectTypeOf<All['delete']>().toBeNever()\n\t\t})\n\n\t\ttest('missing raw collapses schemaRef.raw to never', () => {\n\t\t\tclass NoRawAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = NoRawAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['raw']>().toBeNever()\n\t\t})\n\n\t\ttest('missing upsertOne collapses one().upsert to never', () => {\n\t\t\tclass NoUpsertAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = NoUpsertAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['upsert']>().toBeNever()\n\t\t})\n\n\t\ttest('adapter with updateMany enables one().update and all().update', () => {\n\t\t\tclass WithUpdateAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t\tasync updateMany() { return [] }\n\t\t\t}\n\t\t\ttype A = WithUpdateAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['update']>().toBeFunction()\n\t\t\texpectTypeOf<All['update']>().toBeFunction()\n\t\t})\n\n\t\ttest('adapter with deleteMany enables one().delete and all().delete', () => {\n\t\t\tclass WithDeleteAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t\tasync deleteMany() { return [] }\n\t\t\t}\n\t\t\ttype A = WithDeleteAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['delete']>().toBeFunction()\n\t\t\texpectTypeOf<All['delete']>().toBeFunction()\n\t\t})\n\n\t\ttest('missing count collapses all().count to never', () => {\n\t\t\tclass NoCountAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = NoCountAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<All['count']>().toBeNever()\n\t\t})\n\n\t\ttest('adapter with count enables all().count', () => {\n\t\t\tclass WithCountAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t\tasync count() { return 0 }\n\t\t\t}\n\t\t\ttype A = WithCountAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A>\n\t\t\texpectTypeOf<All['count']>().toBeFunction()\n\t\t})\n\n\t\ttest('adapter with raw enables schemaRef.raw', () => {\n\t\t\tclass WithRawAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync raw(_s: any, _c: any, _command: string) { return { rows: [] } }\n\t\t\t}\n\t\t\ttype A = WithRawAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['raw']>().toBeFunction()\n\t\t})\n\n\t\ttest('adapter raw signature drives arg-tuple inference on schemaRef.raw', () => {\n\t\t\tclass TypedRawAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync raw(_s: any, _c: any, _sql: string, _params: number[]) { return [42] }\n\t\t\t}\n\t\t\ttype A = TypedRawAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['raw']>().toBeFunction()\n\t\t\texpectTypeOf<Ref['raw']>().parameters.toEqualTypeOf<[sql: string, params: number[]]>()\n\t\t})\n\n\t\ttest('per-call <T> override narrows raw return type', () => {\n\t\t\tclass DefaultRawAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync raw(_s: any, _c: any, _sql: string) { return { rows: [] as unknown[] } }\n\t\t\t}\n\t\t\ttype A = DefaultRawAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['raw']>().toBeFunction()\n\t\t\texpectTypeOf<Ref['raw']>().parameters.toEqualTypeOf<[sql: string]>()\n\t\t})\n\n\t\ttest('adapter with zero-arg raw infers empty arg tuple', () => {\n\t\t\tclass ZeroArgRawAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync raw(_s: any, _c: any) { return 'pong' }\n\t\t\t}\n\t\t\ttype A = ZeroArgRawAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['raw']>().parameters.toEqualTypeOf<[]>()\n\t\t\texpectTypeOf<Ref['raw']>().toBeFunction()\n\t\t})\n\n\t\ttest('gating survives through select() chains', () => {\n\t\t\tclass MinimalAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t}\n\t\t\ttype A = MinimalAdapter\n\t\t\tconst _TestSchema = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\ttype S = typeof _TestSchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A, 'id'>\n\t\t\ttype All = import('./builders').AllBuilderSurface<S, A, 'id'>\n\t\t\texpectTypeOf<One['update']>().toBeNever()\n\t\t\texpectTypeOf<One['delete']>().toBeNever()\n\t\t\texpectTypeOf<All['update']>().toBeNever()\n\t\t\texpectTypeOf<All['delete']>().toBeNever()\n\t\t})\n\t})\n\n\tdescribe('type-level: aggregate gating', () => {\n\t\ttest('adapter with aggregateOps = [] narrows repo.on(S).aggregate to never', () => {\n\t\t\tclass EmptyAggAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly aggregateOps = [] as const\n\t\t\t}\n\t\t\ttype A = EmptyAggAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['aggregate']>().toBeNever()\n\t\t})\n\n\t\ttest('adapter with aggregate method and non-empty aggregateOps narrows to callable', () => {\n\t\t\tclass FullAggAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly aggregateOps = ['count', 'sum'] as const\n\t\t\t\tasync aggregate() { return [] }\n\t\t\t}\n\t\t\ttype A = FullAggAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['aggregate']>().toBeFunction()\n\t\t})\n\n\t\ttest('adapter with aggregate method but empty aggregateOps narrows to never', () => {\n\t\t\tclass MethodOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly aggregateOps = [] as const\n\t\t\t\tasync aggregate() { return [] }\n\t\t\t}\n\t\t\ttype A = MethodOnlyAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['aggregate']>().toBeNever()\n\t\t})\n\n\t\ttest('adapter with non-empty aggregateOps but no aggregate method narrows to never', () => {\n\t\t\tclass OpsOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\treadonly aggregateOps = ['count'] as const\n\t\t\t}\n\t\t\ttype A = OpsOnlyAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['aggregate']>().toBeNever()\n\t\t})\n\n\t\ttest('default aggregateOps on OrmAdapter is empty, so aggregate is never by default', () => {\n\t\t\tclass DefaultAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t}\n\t\t\ttype A = DefaultAdapter\n\t\t\ttype Ref = import('./builders').SchemaRefSurface<import('../schema').AnySchema, A>\n\t\t\texpectTypeOf<Ref['aggregate']>().toBeNever()\n\t\t})\n\t})\n\n\tdescribe('type-level: AggregateBuilder', () => {\n\t\tclass AggAdapter extends OrmAdapter {\n\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\treadonly supportedFieldTypes = ['string', 'number', 'boolean', 'object', 'array'] as const\n\t\t\treadonly aggregateOps = ['count', 'countDistinct', 'sum', 'avg', 'min', 'max'] as const\n\t\t\tasync aggregate() { return [] }\n\t\t}\n\t\tconst _S = Schema.from('items')\n\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t.field('name', v.string())\n\t\t\t.field('amount', v.number())\n\t\t\t.field('active', v.boolean())\n\t\t\t.field('tags', v.array(v.string()))\n\t\t\t.field('meta', v.object({ x: v.number() }))\n\t\t\t.build()\n\t\ttype S = typeof _S\n\t\ttype A = AggAdapter\n\t\ttype Builder = import('./builders').AggregateBuilder<S, A, {}, {}, false>\n\n\t\ttest('.count(alias) adds alias to result type as number', () => {\n\t\t\ttype AfterCount = ReturnType<Builder['count']>\n\t\t\ttype Result = Awaited<ReturnType<AfterCount['run']>>\n\t\t\texpectTypeOf<Result>().toHaveProperty('total')\n\t\t})\n\n\t\ttest('duplicate alias: \"a\" extends keyof Aggs triggers never guard', () => {\n\t\t\ttype AggsWithA = { a: number }\n\t\t\ttype DuplicateGuard = 'a' extends keyof AggsWithA ? [never] : [alias: 'a']\n\t\t\texpectTypeOf<DuplicateGuard>().toEqualTypeOf<[never]>()\n\t\t})\n\n\t\ttest('.run() on empty Aggs requires never arg (compile error gate)', () => {\n\t\t\ttype RunParams = Parameters<Builder['run']>\n\t\t\texpectTypeOf<RunParams>().toEqualTypeOf<[never]>()\n\t\t})\n\n\t\ttest('.run() on non-empty Aggs takes no args', () => {\n\t\t\ttype WithCount = import('./builders').AggregateBuilder<S, A, { total: number }, {}, false>\n\t\t\ttype RunParams = Parameters<WithCount['run']>\n\t\t\texpectTypeOf<RunParams>().toEqualTypeOf<[]>()\n\t\t})\n\n\t\ttest('.run() returns single object when HasGroupBy = false', () => {\n\t\t\ttype WithCount = import('./builders').AggregateBuilder<S, A, { total: number }, {}, false>\n\t\t\ttype Result = Awaited<ReturnType<WithCount['run']>>\n\t\t\texpectTypeOf<Result>().toEqualTypeOf<{ total: number }>()\n\t\t})\n\n\t\ttest('.run() returns array when HasGroupBy = true', () => {\n\t\t\ttype WithGroupBy = import('./builders').AggregateBuilder<S, A, { total: number }, { name: string }, true>\n\t\t\ttype Result = Awaited<ReturnType<WithGroupBy['run']>>\n\t\t\texpectTypeOf<Result>().toEqualTypeOf<({ total: number } & { name: string })[]>()\n\t\t})\n\n\t\ttest('group-key fields appear in result row with schema-declared types', () => {\n\t\t\ttype WithGroupBy = import('./builders').AggregateBuilder<S, A, { total: number }, { name: string; amount: number }, true>\n\t\t\ttype Result = Awaited<ReturnType<WithGroupBy['run']>>\n\t\t\ttype Row = Result[number]\n\t\t\texpectTypeOf<Row>().toHaveProperty('total')\n\t\t\texpectTypeOf<Row>().toHaveProperty('name')\n\t\t\texpectTypeOf<Row>().toHaveProperty('amount')\n\t\t})\n\n\t\ttest('.sum rejects string field (field-type constraint)', () => {\n\t\t\ttype SumFieldParam = Parameters<Builder['sum']>[0]\n\t\t\ttype IsAccepted = typeof _S.fields.name extends SumFieldParam ? true : false\n\t\t\texpectTypeOf<IsAccepted>().toEqualTypeOf<false>()\n\t\t})\n\n\t\ttest('.avg rejects string field (field-type constraint)', () => {\n\t\t\ttype AvgFieldParam = Parameters<Builder['avg']>[0]\n\t\t\ttype IsAccepted = typeof _S.fields.name extends AvgFieldParam ? true : false\n\t\t\texpectTypeOf<IsAccepted>().toEqualTypeOf<false>()\n\t\t})\n\n\t\ttest('.min rejects array field', () => {\n\t\t\ttype MinFieldParam = Parameters<Builder['min']>[0]\n\t\t\ttype IsAccepted = typeof _S.fields.tags extends MinFieldParam ? true : false\n\t\t\texpectTypeOf<IsAccepted>().toEqualTypeOf<false>()\n\t\t})\n\n\t\ttest('.max rejects object field', () => {\n\t\t\ttype MaxFieldParam = Parameters<Builder['max']>[0]\n\t\t\ttype IsAccepted = typeof _S.fields.meta extends MaxFieldParam ? true : false\n\t\t\texpectTypeOf<IsAccepted>().toEqualTypeOf<false>()\n\t\t})\n\n\t\ttest('.where().where() is a compile error', () => {\n\t\t\ttype AfterWhere = import('./builders').AggregateBuilder<S, A, {}, {}, false, true, false>\n\t\t\ttype WhereParams = Parameters<AfterWhere['where']>\n\t\t\texpectTypeOf<WhereParams>().toEqualTypeOf<[never]>()\n\t\t})\n\n\t\ttest('.having().having() is a compile error', () => {\n\t\t\ttype AfterHaving = import('./builders').AggregateBuilder<S, A, {}, {}, false, false, true>\n\t\t\ttype HavingParams = Parameters<AfterHaving['having']>\n\t\t\texpectTypeOf<HavingParams>().toEqualTypeOf<[never]>()\n\t\t})\n\n\t\ttest('.groupBy().groupBy() is a compile error', () => {\n\t\t\ttype AfterGroupBy = import('./builders').AggregateBuilder<S, A, {}, { name: string }, true, false, false>\n\t\t\ttype GroupByParams = Parameters<AfterGroupBy['groupBy']>\n\t\t\texpectTypeOf<GroupByParams>().toEqualTypeOf<[never]>()\n\t\t})\n\n\t\ttest('clone-on-step: .where() returns a new builder', () => {\n\t\t\ttype After = ReturnType<Builder['where']>\n\t\t\texpectTypeOf<After>().toMatchTypeOf<import('./builders').AggregateBuilder<S, A, {}, {}, false, true, false>>()\n\t\t})\n\t})\n\n\tdescribe('repo.on().all().where().find() end-to-end', () => {\n\t\tconst TestSchema = Schema.from('e2e')\n\t\t\t.pk('id', v.string(), () => `e-${Math.random().toString(36).slice(2)}`)\n\t\t\t.field('name', v.string())\n\t\t\t.field('age', v.number())\n\t\t\t.field('active', v.boolean())\n\t\t\t.field('tags', v.array(v.string()))\n\t\t\t.field('score', v.optional(v.number()), { onCreate: () => undefined })\n\t\t\t.build()\n\n\t\tfunction makeE2eRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t\treturn { repo, adapter }\n\t\t}\n\n\t\tasync function seedData(repo: any) {\n\t\t\tawait repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.create([\n\t\t\t\t\t{ name: 'Alice', age: 30, active: true, tags: ['admin', 'user'] },\n\t\t\t\t\t{ name: 'Bob', age: 20, active: false, tags: ['user'] },\n\t\t\t\t\t{ name: 'Carol', age: 40, active: true, tags: ['admin'] },\n\t\t\t\t\t{ name: 'Dave', age: 25, active: true, tags: ['user', 'guest'], score: 100 },\n\t\t\t\t])\n\t\t}\n\n\t\ttest('all().where().find() returns matching documents', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq(TestSchema.fields.active, true))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(3)\n\t\t\texpect(results.every((r) => r.active === true)).toBe(true)\n\t\t})\n\n\t\ttest('one().where().find() returns first matching document', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq(TestSchema.fields.name, 'Bob'))\n\t\t\t\t.find()\n\t\t\texpect(result).not.toBeNull()\n\t\t\texpect(result!.name).toBe('Bob')\n\t\t})\n\n\t\ttest('one().where().find() returns null when no match', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq(TestSchema.fields.name, 'Nobody'))\n\t\t\t\t.find()\n\t\t\texpect(result).toBeNull()\n\t\t})\n\n\t\ttest('eq filter op matches exact value', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq(TestSchema.fields.age, 30))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(1)\n\t\t\texpect(results[0].name).toBe('Alice')\n\t\t})\n\n\t\ttest('ne filter op excludes matching values', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.ne(TestSchema.fields.name, 'Alice'))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(3)\n\t\t\texpect(results.every((r) => r.name !== 'Alice')).toBe(true)\n\t\t})\n\n\t\ttest('gt filter op matches values greater than', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.gt(TestSchema.fields.age, 25))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Alice', 'Carol'])\n\t\t})\n\n\t\ttest('gte filter op matches values greater than or equal', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.gte(TestSchema.fields.age, 30))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Alice', 'Carol'])\n\t\t})\n\n\t\ttest('lt filter op matches values less than', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.lt(TestSchema.fields.age, 25))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(1)\n\t\t\texpect(results[0].name).toBe('Bob')\n\t\t})\n\n\t\ttest('lte filter op matches values less than or equal', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.lte(TestSchema.fields.age, 25))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Bob', 'Dave'])\n\t\t})\n\n\t\ttest('in filter op matches values in array', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.in(TestSchema.fields.name, ['Alice', 'Carol']))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Alice', 'Carol'])\n\t\t})\n\n\t\ttest('notIn filter op excludes values in array', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.notIn(TestSchema.fields.name, ['Alice', 'Carol']))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Bob', 'Dave'])\n\t\t})\n\n\t\ttest('like filter op matches substring case-insensitively', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.like(TestSchema.fields.name, 'ali'))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(1)\n\t\t\texpect(results[0].name).toBe('Alice')\n\t\t})\n\n\t\ttest('exists filter op matches non-null values', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.exists(TestSchema.fields.score))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(1)\n\t\t\texpect(results[0].name).toBe('Dave')\n\t\t})\n\n\t\ttest('notExists filter op matches null/undefined values', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.notExists(TestSchema.fields.score))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(3)\n\t\t})\n\n\t\ttest('contains filter op matches array subset', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.contains('tags', ['admin']))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Alice', 'Carol'])\n\t\t})\n\n\t\ttest('notContains filter op excludes array subset', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.notContains('tags', ['admin']))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Bob', 'Dave'])\n\t\t})\n\n\t\ttest('and combinator requires all conditions', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.and([(g) => g.gt(TestSchema.fields.age, 20), (g) => g.eq(TestSchema.fields.active, true)]))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Alice', 'Carol', 'Dave'])\n\t\t})\n\n\t\ttest('or combinator matches any condition', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.or([(g) => g.eq(TestSchema.fields.name, 'Alice'), (g) => g.eq(TestSchema.fields.name, 'Bob')]))\n\t\t\t\t.find()\n\t\t\texpect(results.map((r) => r.name).sort()).toEqual(['Alice', 'Bob'])\n\t\t})\n\n\t\ttest('raw-string field overload works end-to-end', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tconst results = await repo\n\t\t\t\t.on(TestSchema)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('name', 'Alice'))\n\t\t\t\t.find()\n\t\t\texpect(results).toHaveLength(1)\n\t\t\texpect(results[0].name).toBe('Alice')\n\t\t})\n\n\t\ttest('empty and([]) throws at builder time', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\texpect(() =>\n\t\t\t\trepo\n\t\t\t\t\t.on(TestSchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.where((q) => q.and([]))\n\t\t\t\t\t.find(),\n\t\t\t).toThrow()\n\t\t})\n\n\t\ttest('empty or([]) throws at builder time', async () => {\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\texpect(() =>\n\t\t\t\trepo\n\t\t\t\t\t.on(TestSchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.where((q) => q.or([]))\n\t\t\t\t\t.find(),\n\t\t\t).toThrow()\n\t\t})\n\n\t\ttest('unknown field in filter throws OrmValidationError at boundary', async () => {\n\t\t\tconst { OrmValidationError: OrmValErr } = await import('../errors')\n\t\t\tconst { repo } = makeE2eRepo()\n\t\t\tawait seedData(repo)\n\t\t\tawait expect(\n\t\t\t\trepo\n\t\t\t\t\t.on(TestSchema)\n\t\t\t\t\t.all()\n\t\t\t\t\t.where((q) => q.eq('nonexistentField', 42))\n\t\t\t\t\t.find(),\n\t\t\t).rejects.toBeInstanceOf(OrmValErr)\n\t\t})\n\t})\n\n\tdescribe('one().id().update() (replaces updateByPk)', () => {\n\t\tlet viewCounter = 0\n\t\tconst ItemSchema = Schema.from('items')\n\t\t\t.pk('id', v.string(), () => `item-${++viewCounter}`)\n\t\t\t.field('title', v.string())\n\t\t\t.field('views', v.number())\n\t\t\t.field('updatedAt', v.number(), { onCreate: () => 1000, onUpdate: () => Date.now() })\n\t\t\t.build()\n\n\t\tfunction makeUpdateRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\treturn Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t}\n\n\t\ttest('round-trip update via one().id().update()', async () => {\n\t\t\tconst repo = makeUpdateRepo()\n\t\t\tconst item = await repo.on(ItemSchema).one().create({ title: 'Hello', views: 0 })\n\t\t\tconst updated = await repo.on(ItemSchema).one().id(item.id).update({ title: 'Updated' })\n\t\t\texpect(updated).not.toBeNull()\n\t\t\texpect(updated!.title).toBe('Updated')\n\t\t\texpect(updated!.views).toBe(0)\n\n\t\t\tconst found = await repo.on(ItemSchema).one().id(item.id).find()\n\t\t\texpect(found!.title).toBe('Updated')\n\t\t})\n\n\t\ttest('auto-bump injects updatedAt on un-touched onUpdate field', async () => {\n\t\t\tconst repo = makeUpdateRepo()\n\t\t\tconst item = await repo.on(ItemSchema).one().create({ title: 'Hello', views: 0 })\n\t\t\tconst before = item.updatedAt\n\n\t\t\tconst updated = await repo.on(ItemSchema).one().id(item.id).update({ title: 'Changed' })\n\t\t\texpect(updated!.updatedAt).not.toBe(before)\n\t\t\texpect(updated!.updatedAt).toBeGreaterThanOrEqual(before)\n\t\t})\n\n\t\ttest('user set({updatedAt:X}) suppresses auto-bump', async () => {\n\t\t\tconst repo = makeUpdateRepo()\n\t\t\tconst item = await repo.on(ItemSchema).one().create({ title: 'Hello', views: 0 })\n\n\t\t\tconst updated = await repo.on(ItemSchema).one().id(item.id).update({ updatedAt: 9999 })\n\t\t\texpect(updated!.updatedAt).toBe(9999)\n\t\t})\n\n\t\ttest('update with inc op via SchemaUpdateInput', async () => {\n\t\t\tconst { IncOp } = await import('../updates')\n\t\t\tconst repo = makeUpdateRepo()\n\t\t\tconst item = await repo.on(ItemSchema).one().create({ title: 'Hello', views: 10 })\n\n\t\t\tconst updated = await repo.on(ItemSchema).one().id(item.id).update({ views: new IncOp('views', 5) })\n\t\t\texpect(updated!.views).toBe(15)\n\t\t})\n\n\t\ttest('update returns null for missing pk', async () => {\n\t\t\tconst repo = makeUpdateRepo()\n\t\t\tconst result = await repo.on(ItemSchema).one().id('nonexistent').update({ title: 'X' })\n\t\t\texpect(result).toBeNull()\n\t\t})\n\t})\n\n\tdescribe('one().where().upsert() (replaces upsertOne)', () => {\n\t\tlet upsertCounter = 0\n\n\t\tconst UpsertSchema = Schema.from('upserts')\n\t\t\t.pk('id', v.string(), () => `up-${++upsertCounter}`)\n\t\t\t.field('email', v.string())\n\t\t\t.field('name', v.string())\n\t\t\t.field('views', v.number())\n\t\t\t.field('createdAt', v.number(), { onCreate: () => 1000 })\n\t\t\t.field('updatedAt', v.number(), { onCreate: () => 1000, onUpdate: () => 9999 })\n\t\t\t.build()\n\n\t\tfunction makeUpsertRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\treturn Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t}\n\n\t\ttest('create-then-update path: row missing → inserts and applies update', async () => {\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tconst result = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'alice@test.com'))\n\t\t\t\t.upsert({\n\t\t\t\t\tcreate: { email: 'alice@test.com', name: 'Alice', views: 0 },\n\t\t\t\t\tupdate: { name: 'Alice Updated' },\n\t\t\t\t})\n\n\t\t\texpect(result.email).toBe('alice@test.com')\n\t\t\texpect(result.name).toBe('Alice Updated')\n\t\t\texpect(result.createdAt).toBe(1000)\n\t\t})\n\n\t\ttest('create path with set value overriding create field is allowed', async () => {\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tconst result = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'override@test.com'))\n\t\t\t\t.upsert({\n\t\t\t\t\tcreate: { email: 'override@test.com', name: 'Original', views: 0 },\n\t\t\t\t\tupdate: { views: 42 },\n\t\t\t\t})\n\n\t\t\texpect(result.views).toBe(42)\n\t\t\texpect(result.name).toBe('Original')\n\t\t})\n\n\t\ttest('update-only-on-exists path: row exists → ignores create, applies update + auto-bump', async () => {\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tawait repo.on(UpsertSchema).one().create({ email: 'bob@test.com', name: 'Bob', views: 42 })\n\n\t\t\tconst result = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'bob@test.com'))\n\t\t\t\t.upsert({\n\t\t\t\t\tcreate: { email: 'bob@test.com', name: 'IGNORED', views: 0 },\n\t\t\t\t\tupdate: { name: 'Bob Updated' },\n\t\t\t\t})\n\n\t\t\texpect(result.name).toBe('Bob Updated')\n\t\t\texpect(result.views).toBe(42)\n\t\t\texpect(result.updatedAt).toBe(9999)\n\t\t})\n\n\t\ttest('create-vs-op conflict throws OrmValidationError with kind conflicting-ops', async () => {\n\t\t\tconst { IncOp } = await import('../updates')\n\t\t\tconst { OrmValidationError } = await import('../errors')\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\ttry {\n\t\t\t\tawait repo\n\t\t\t\t\t.on(UpsertSchema)\n\t\t\t\t\t.one()\n\t\t\t\t\t.where((q) => q.eq('email', 'conflict@test.com'))\n\t\t\t\t\t.upsert({\n\t\t\t\t\t\tcreate: { email: 'conflict@test.com', name: 'Conflict', views: 0 },\n\t\t\t\t\t\tupdate: { views: new IncOp('views', 1) },\n\t\t\t\t\t})\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\texpect(err.kind).toBe('conflicting-ops')\n\t\t\t\texpect(err.operation).toBe('upsertOne')\n\t\t\t\texpect(err.failures[0].field).toBe('views')\n\t\t\t}\n\t\t})\n\n\t\ttest('empty-update-allowed: create with no update is allowed', async () => {\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tconst result = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'noops@test.com'))\n\t\t\t\t.upsert({ create: { email: 'noops@test.com', name: 'NoOps', views: 0 } })\n\n\t\t\texpect(result.email).toBe('noops@test.com')\n\t\t\texpect(result.name).toBe('NoOps')\n\t\t})\n\n\t\ttest('empty-update-allowed: if exists do nothing', async () => {\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tawait repo.on(UpsertSchema).one().create({ email: 'exists@test.com', name: 'Original', views: 5 })\n\n\t\t\tconst result = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'exists@test.com'))\n\t\t\t\t.upsert({ create: { email: 'exists@test.com', name: 'IGNORED', views: 0 } })\n\n\t\t\texpect(result.name).toBe('Original')\n\t\t\texpect(result.views).toBe(5)\n\t\t})\n\n\t\ttest('upsert-returns-document rule: returns resulting document', async () => {\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tconst insertResult = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'doc@test.com'))\n\t\t\t\t.upsert({ create: { email: 'doc@test.com', name: 'Doc', views: 0 } })\n\t\t\texpect(insertResult.id).toBeDefined()\n\t\t\texpect(insertResult.email).toBe('doc@test.com')\n\n\t\t\tconst updateResult = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'doc@test.com'))\n\t\t\t\t.upsert({\n\t\t\t\t\tcreate: { email: 'doc@test.com', name: 'IGNORED', views: 0 },\n\t\t\t\t\tupdate: { name: 'DocUpdated' },\n\t\t\t\t})\n\t\t\texpect(updateResult.id).toBe(insertResult.id)\n\t\t\texpect(updateResult.name).toBe('DocUpdated')\n\t\t})\n\n\t\ttest('validates create payload via validateCreate (onCreate defaults injected)', async () => {\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tconst result = await repo\n\t\t\t\t.on(UpsertSchema)\n\t\t\t\t.one()\n\t\t\t\t.where((q) => q.eq('email', 'defaults@test.com'))\n\t\t\t\t.upsert({ create: { email: 'defaults@test.com', name: 'Defaults', views: 0 } })\n\n\t\t\texpect(result.id).toBeDefined()\n\t\t\texpect(result.createdAt).toBe(1000)\n\t\t})\n\n\t\ttest('validates create payload and throws OrmValidationError on invalid', async () => {\n\t\t\tconst { OrmValidationError } = await import('../errors')\n\t\t\tconst repo = makeUpsertRepo()\n\n\t\t\tawait expect(\n\t\t\t\trepo\n\t\t\t\t\t.on(UpsertSchema)\n\t\t\t\t\t.one()\n\t\t\t\t\t.where((q) => q.eq('email', 'bad@test.com'))\n\t\t\t\t\t.upsert({ create: { email: 123 as any, name: 'Bad', views: 0 } }),\n\t\t\t).rejects.toBeInstanceOf(OrmValidationError)\n\t\t})\n\n\t\ttest('upsert-filter-incompatible error from adapter boundary', async () => {\n\t\t\tconst { OrmValidationError } = await import('../errors')\n\n\t\t\tconst spy = mockInstance()\n\t\t\tclass RestrictedAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number', 'boolean'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\treadonly updateOps = ['set'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t\tasync upsertOne(_schema: any, _config: any, _filter: any, _create: any, _ops: any): Promise<Record<string, unknown>> {\n\t\t\t\t\tthrow new OrmValidationError('upsert-filter-incompatible', 'test', 'upsertOne', [\n\t\t\t\t\t\t{ cause: 'adapter requires single eq filter on unique column, received complex filter' },\n\t\t\t\t\t])\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst restrictedAdapter = new (RestrictedAdapter as any)() as InstanceType<typeof RestrictedAdapter>\n\t\t\tspy.mockRestore()\n\n\t\t\tconst repo = Repo.from(restrictedAdapter)\n\t\t\t\t.resolve(() => ({ table: 'test' }))\n\t\t\t\t.build()\n\n\t\t\ttry {\n\t\t\t\tawait repo\n\t\t\t\t\t.on(UpsertSchema)\n\t\t\t\t\t.one()\n\t\t\t\t\t.where((q: any) => q.eq('email', 'x@test.com'))\n\t\t\t\t\t.upsert({ create: { email: 'x@test.com', name: 'X', views: 0 } })\n\t\t\t\texpect.unreachable()\n\t\t\t} catch (e) {\n\t\t\t\texpect(e).toBeInstanceOf(OrmValidationError)\n\t\t\t\tconst err = e as InstanceType<typeof OrmValidationError>\n\t\t\t\texpect(err.kind).toBe('upsert-filter-incompatible')\n\t\t\t}\n\t\t})\n\t})\n\n\tdescribe('type-level: upsert gating', () => {\n\t\ttest('adapter with upsertOne enables one().upsert', () => {\n\t\t\tclass WithUpsertAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\treadonly updateOps = ['set'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t\tasync upsertOne() { return {} }\n\t\t\t}\n\t\t\ttype A = WithUpsertAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['upsert']>().toBeFunction()\n\t\t})\n\n\t\ttest('adapter without upsertOne collapses one().upsert to never', () => {\n\t\t\tclass NoUpsertAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\treadonly updateOps = ['set'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = NoUpsertAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['upsert']>().toBeNever()\n\t\t})\n\n\t\ttest('adapter without upsertOne (crud-only) collapses one().upsert to never', () => {\n\t\t\tclass CrudOnlyAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly updateOps = ['set'] as const\n\t\t\t\tasync findByPk() { return null }\n\t\t\t}\n\t\t\ttype A = CrudOnlyAdapter\n\t\t\ttype S = import('../schema').AnySchema\n\t\t\ttype One = import('./builders').OneBuilderSurface<S, A>\n\t\t\texpectTypeOf<One['upsert']>().toBeNever()\n\t\t})\n\t})\n\n\tdescribe('repo.session — transactional behaviour', () => {\n\t\tconst SchemaA = Schema.from('accounts')\n\t\t\t.pk('id', v.string(), () => `a-${Math.random().toString(36).slice(2)}`)\n\t\t\t.field('balance', v.number())\n\t\t\t.build()\n\t\tconst SchemaB = Schema.from('ledger')\n\t\t\t.pk('id', v.string(), () => `l-${Math.random().toString(36).slice(2)}`)\n\t\t\t.field('amount', v.number())\n\t\t\t.field('accountId', v.string())\n\t\t\t.build()\n\n\t\tfunction makeSessionRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\treturn Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\t\t}\n\n\t\ttest('throw-to-rollback: uncaught throw rolls back all writes and rejects with same error', async () => {\n\t\t\tconst repo = makeSessionRepo()\n\t\t\tawait repo.on(SchemaA).one().create({ balance: 100 })\n\n\t\t\tconst err = new Error('boom')\n\t\t\tawait expect(\n\t\t\t\trepo.session(async () => {\n\t\t\t\t\tawait repo.on(SchemaA).one().create({ balance: 200 })\n\t\t\t\t\tthrow err\n\t\t\t\t}),\n\t\t\t).rejects.toBe(err)\n\n\t\t\tconst all = await repo\n\t\t\t\t.on(SchemaA)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.gte('balance', 0))\n\t\t\t\t.find()\n\t\t\texpect(all).toHaveLength(1)\n\t\t\texpect(all[0].balance).toBe(100)\n\t\t})\n\n\t\ttest('return-to-commit: successful return commits and resolves with callback value', async () => {\n\t\t\tconst repo = makeSessionRepo()\n\t\t\tconst result = await repo.session(async () => {\n\t\t\t\tawait repo.on(SchemaA).one().create({ balance: 500 })\n\t\t\t\treturn 'committed'\n\t\t\t})\n\n\t\t\texpect(result).toBe('committed')\n\t\t\tconst all = await repo\n\t\t\t\t.on(SchemaA)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.gte('balance', 0))\n\t\t\t\t.find()\n\t\t\texpect(all).toHaveLength(1)\n\t\t\texpect(all[0].balance).toBe(500)\n\t\t})\n\n\t\ttest('cross-schema session: multiple schemas share one tx; on throw both roll back', async () => {\n\t\t\tconst repo = makeSessionRepo()\n\t\t\tconst account = await repo.on(SchemaA).one().create({ balance: 1000 })\n\n\t\t\tawait expect(\n\t\t\t\trepo.session(async () => {\n\t\t\t\t\tawait repo\n\t\t\t\t\t\t.on(SchemaA)\n\t\t\t\t\t\t.all()\n\t\t\t\t\t\t.where((q) => q.eq('id', account.id))\n\t\t\t\t\t\t.update({ balance: 900 })\n\t\t\t\t\tawait repo.on(SchemaB).one().create({ amount: 100, accountId: account.id })\n\t\t\t\t\tthrow new Error('tx failure')\n\t\t\t\t}),\n\t\t\t).rejects.toThrow('tx failure')\n\n\t\t\tconst acct = await repo.on(SchemaA).one().id(account.id).find()\n\t\t\texpect(acct!.balance).toBe(1000)\n\n\t\t\tconst entries = await repo\n\t\t\t\t.on(SchemaB)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.eq('accountId', account.id))\n\t\t\t\t.find()\n\t\t\texpect(entries).toHaveLength(0)\n\t\t})\n\n\t\ttest('nested session: inner repo.session delegates to adapter (flat in in-memory)', async () => {\n\t\t\tconst repo = makeSessionRepo()\n\n\t\t\tconst result = await repo.session(async () => {\n\t\t\t\tawait repo.on(SchemaA).one().create({ balance: 100 })\n\t\t\t\tconst inner = await repo.session(async () => {\n\t\t\t\t\tawait repo.on(SchemaA).one().create({ balance: 200 })\n\t\t\t\t\treturn 'inner'\n\t\t\t\t})\n\t\t\t\treturn inner\n\t\t\t})\n\n\t\t\texpect(result).toBe('inner')\n\t\t\tconst all = await repo\n\t\t\t\t.on(SchemaA)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.gte('balance', 0))\n\t\t\t\t.find()\n\t\t\texpect(all).toHaveLength(2)\n\t\t})\n\n\t\ttest('nested session: throw in inner rolls back entire outer tx (flat nesting)', async () => {\n\t\t\tconst repo = makeSessionRepo()\n\t\t\tawait repo.on(SchemaA).one().create({ balance: 50 })\n\n\t\t\tawait expect(\n\t\t\t\trepo.session(async () => {\n\t\t\t\t\tawait repo.on(SchemaA).one().create({ balance: 100 })\n\t\t\t\t\tawait repo.session(async () => {\n\t\t\t\t\t\tawait repo.on(SchemaA).one().create({ balance: 200 })\n\t\t\t\t\t\tthrow new Error('inner boom')\n\t\t\t\t\t})\n\t\t\t\t}),\n\t\t\t).rejects.toThrow('inner boom')\n\n\t\t\tconst all = await repo\n\t\t\t\t.on(SchemaA)\n\t\t\t\t.all()\n\t\t\t\t.where((q) => q.gte('balance', 0))\n\t\t\t\t.find()\n\t\t\texpect(all).toHaveLength(1)\n\t\t\texpect(all[0].balance).toBe(50)\n\t\t})\n\n\t\ttest('no-tx-argument rule: session callback receives no arguments', async () => {\n\t\t\tconst repo = makeSessionRepo()\n\t\t\tlet argCount = -1\n\t\t\tawait repo.session(async function () {\n\t\t\t\targCount = arguments.length\n\t\t\t})\n\t\t\texpect(argCount).toBe(0)\n\t\t})\n\t})\n\n\tdescribe('type-level: session gating', () => {\n\t\ttest('missing session collapses repo.session to never', () => {\n\t\t\tclass NoSessionAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync findMany() { return [] }\n\t\t\t}\n\t\t\ttype A = NoSessionAdapter\n\t\t\tconst _repo = {} as import('./repo').RepoSurface<A>\n\t\t\texpectTypeOf(_repo.session).toBeNever()\n\t\t})\n\n\t\ttest('adapter with session enables repo.session', () => {\n\t\t\tclass WithSessionAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({})\n\t\t\t\treadonly supportedFieldTypes = ['string'] as const\n\t\t\t\tasync session<T>(fn: () => Promise<T>): Promise<T> { return fn() }\n\t\t\t}\n\t\t\ttype A = WithSessionAdapter\n\t\t\tconst _repo = {} as import('./repo').RepoSurface<A>\n\t\t\texpectTypeOf(_repo.session).toBeFunction()\n\t\t})\n\t})\n\n\tdescribe('ALS-backed resolve', () => {\n\t\ttest('reads inside repo.resolve see the override', async () => {\n\t\t\tconst seenConfigs: unknown[] = []\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((s: any, config: any) => {\n\t\t\t\tseenConfigs.push(config)\n\t\t\t\treturn origUse(s, config)\n\t\t\t})\n\n\t\t\tconst TestSchema = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tawait repo.resolve(\n\t\t\t\t(config) => ({ table: `override_${config.table}` }),\n\t\t\t\tasync () => {\n\t\t\t\t\tawait repo.on(TestSchema).all().find()\n\t\t\t\t},\n\t\t\t)\n\n\t\t\texpect(seenConfigs[0]).toEqual({ table: 'override_test' })\n\t\t})\n\n\t\ttest('reads outside repo.resolve see the default config', async () => {\n\t\t\tconst seenConfigs: unknown[] = []\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((s: any, config: any) => {\n\t\t\t\tseenConfigs.push(config)\n\t\t\t\treturn origUse(s, config)\n\t\t\t})\n\n\t\t\tconst TestSchema = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tawait repo.resolve(\n\t\t\t\t(config) => ({ table: `scoped_${config.table}` }),\n\t\t\t\tasync () => {},\n\t\t\t)\n\n\t\t\tawait repo.on(TestSchema).all().find()\n\t\t\texpect(seenConfigs[0]).toEqual({ table: 'test' })\n\t\t})\n\n\t\ttest('two parallel repo.resolve calls do not bleed into each other', async () => {\n\t\t\tconst seenConfigs: unknown[] = []\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((s: any, config: any) => {\n\t\t\t\tseenConfigs.push(config)\n\t\t\t\treturn origUse(s, config)\n\t\t\t})\n\n\t\t\tconst TestSchema = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tawait Promise.all([\n\t\t\t\trepo.resolve(\n\t\t\t\t\t(config) => ({ table: `tenant1_${config.table}` }),\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 10))\n\t\t\t\t\t\tawait repo.on(TestSchema).all().find()\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t\trepo.resolve(\n\t\t\t\t\t(config) => ({ table: `tenant2_${config.table}` }),\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tawait new Promise((r) => setTimeout(r, 10))\n\t\t\t\t\t\tawait repo.on(TestSchema).all().find()\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t])\n\n\t\t\texpect(seenConfigs).toContainEqual({ table: 'tenant1_test' })\n\t\t\texpect(seenConfigs).toContainEqual({ table: 'tenant2_test' })\n\t\t\texpect(seenConfigs).not.toContainEqual({ table: 'tenant2_tenant1_test' })\n\t\t\texpect(seenConfigs).not.toContainEqual({ table: 'tenant1_tenant2_test' })\n\t\t})\n\n\t\ttest('overrides survive across awaits inside fn', async () => {\n\t\t\tconst seenConfigs: unknown[] = []\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst origUse = adapter.use.bind(adapter)\n\t\t\t;(adapter as any).use = vi.fn((s: any, config: any) => {\n\t\t\t\tseenConfigs.push(config)\n\t\t\t\treturn origUse(s, config)\n\t\t\t})\n\n\t\t\tconst TestSchema = Schema.from('test')\n\t\t\t\t.pk('id', v.string(), () => 'x')\n\t\t\t\t.field('name', v.string())\n\t\t\t\t.build()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tawait repo.resolve(\n\t\t\t\t(config) => ({ table: `async_${config.table}` }),\n\t\t\t\tasync () => {\n\t\t\t\t\tawait repo.on(TestSchema).all().find()\n\t\t\t\t\tawait new Promise((r) => setTimeout(r, 10))\n\t\t\t\t\tawait repo.on(TestSchema).all().find()\n\t\t\t\t},\n\t\t\t)\n\n\t\t\texpect(seenConfigs).toHaveLength(2)\n\t\t\texpect(seenConfigs[0]).toEqual({ table: 'async_test' })\n\t\t\texpect(seenConfigs[1]).toEqual({ table: 'async_test' })\n\t\t})\n\t})\n\n\tdescribe('class-based OrmAdapter with Repo', async () => {\n\t\tconst { OrmAdapter } = await import('../orm-adapter')\n\t\tconst { FilterGroup } = await import('../filter')\n\t\tconst { OrmValidationError } = await import('../errors')\n\n\t\tfunction makeClassAdapter() {\n\t\t\tconst stores = new Map<string, Map<string, Record<string, unknown>>>()\n\t\t\tfunction getStore(name: string) {\n\t\t\t\tif (!stores.has(name)) stores.set(name, new Map())\n\t\t\t\treturn stores.get(name)!\n\t\t\t}\n\n\t\t\tclass TestClassAdapter extends OrmAdapter {\n\t\t\t\treadonly schemaConfigPipe = v.object({ table: v.string() })\n\t\t\t\treadonly supportedFieldTypes = ['string', 'number'] as const\n\t\t\t\treadonly queryableOps = ['eq'] as const\n\t\t\t\treadonly updateOps = ['set'] as const\n\n\t\t\t\tasync findMany(_schema: import('../schema').AnySchema, config: unknown, group: import('../filter').FilterGroup) {\n\t\t\t\t\tconst cfg = config as { table: string }\n\t\t\t\t\tconst store = getStore(cfg.table)\n\t\t\t\t\treturn [...store.values()].filter((doc) => {\n\t\t\t\t\t\tfor (const child of group.children) {\n\t\t\t\t\t\t\tif (child instanceof (FilterGroup as any).constructor) continue\n\t\t\t\t\t\t\tconst f = child as any\n\t\t\t\t\t\t\tif (f.op === 'eq' && doc[f.field] !== f.value) return false\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tasync createMany(s: import('../schema').AnySchema, config: unknown, data: Record<string, unknown>[]) {\n\t\t\t\t\tconst cfg = config as { table: string }\n\t\t\t\t\tconst store = getStore(cfg.table)\n\t\t\t\t\tconst pk = s.pkField.name\n\t\t\t\t\tfor (const d of data) store.set(String(d[pk]), { ...d })\n\t\t\t\t\treturn data.map((d) => ({ ...d }))\n\t\t\t\t}\n\n\t\t\t\tasync updateMany(s: import('../schema').AnySchema, config: unknown, group: import('../filter').FilterGroup, data: Record<string, unknown>) {\n\t\t\t\t\tconst rows = await this.findMany(s, config, group)\n\t\t\t\t\tconst cfg = config as { table: string }\n\t\t\t\t\tconst store = getStore(cfg.table)\n\t\t\t\t\tconst pk = s.pkField.name\n\t\t\t\t\treturn rows.map((row) => {\n\t\t\t\t\t\tconst updated = { ...row, ...data }\n\t\t\t\t\t\tstore.set(String(updated[pk]), updated)\n\t\t\t\t\t\treturn { ...updated }\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tasync deleteMany(s: import('../schema').AnySchema, config: unknown, group: import('../filter').FilterGroup) {\n\t\t\t\t\tconst rows = await this.findMany(s, config, group)\n\t\t\t\t\tconst cfg = config as { table: string }\n\t\t\t\t\tconst store = getStore(cfg.table)\n\t\t\t\t\tconst pk = s.pkField.name\n\t\t\t\t\tfor (const row of rows) store.delete(String(row[pk]))\n\t\t\t\t\treturn rows\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst spy = mockInstance()\n\t\t\tconst adapter = new (TestClassAdapter as any)() as InstanceType<typeof TestClassAdapter>\n\t\t\tspy.mockRestore()\n\t\t\treturn { adapter, stores }\n\t\t}\n\n\t\tconst TestSchema = Schema.from('class_test')\n\t\t\t.pk('id', v.string(), () => `ct-${Math.random().toString(36).slice(2, 8)}`)\n\t\t\t.field('name', v.string())\n\t\t\t.field('age', v.number())\n\t\t\t.build()\n\n\t\ttest('Repo.from(classAdapter).resolve(...).build() creates a working repo', async () => {\n\t\t\tconst { adapter } = makeClassAdapter()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tconst created = await repo.on(TestSchema).one().create({ name: 'Alice', age: 30 })\n\t\t\texpect(created.name).toBe('Alice')\n\t\t\texpect(created.age).toBe(30)\n\n\t\t\tconst found = await repo.on(TestSchema).one().id(created.id).find()\n\t\t\texpect(found).not.toBeNull()\n\t\t\texpect(found!.name).toBe('Alice')\n\t\t})\n\n\t\ttest('dispatch validator validates config against schemaConfigPipe', async () => {\n\t\t\tconst { adapter } = makeClassAdapter()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve(() => ({ table: 123 }) as any)\n\t\t\t\t.build()\n\n\t\t\tawait expect(\n\t\t\t\trepo.on(TestSchema).all().find(),\n\t\t\t).rejects.toBeInstanceOf(OrmValidationError)\n\t\t})\n\n\t\ttest('dispatch validator validates after transforms applied', async () => {\n\t\t\tconst { adapter } = makeClassAdapter()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tawait expect(\n\t\t\t\trepo.resolve(\n\t\t\t\t\t() => ({ table: 42 }) as any,\n\t\t\t\t\tasync () => repo.on(TestSchema).all().find(),\n\t\t\t\t),\n\t\t\t).rejects.toBeInstanceOf(OrmValidationError)\n\t\t})\n\n\t\ttest('createMany + findMany round-trip works', async () => {\n\t\t\tconst { adapter } = makeClassAdapter()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tconst created = await repo.on(TestSchema).all().create([\n\t\t\t\t{ name: 'Alice', age: 30 },\n\t\t\t\t{ name: 'Bob', age: 25 },\n\t\t\t])\n\t\t\texpect(created).toHaveLength(2)\n\n\t\t\tconst all = await repo.on(TestSchema).all().find()\n\t\t\texpect(all).toHaveLength(2)\n\t\t})\n\n\t\ttest('update and delete work on class-based adapter', async () => {\n\t\t\tconst { adapter } = makeClassAdapter()\n\t\t\tconst repo = Repo.from(adapter)\n\t\t\t\t.resolve((s) => ({ table: s.name }))\n\t\t\t\t.build()\n\n\t\t\tconst user = await repo.on(TestSchema).one().create({ name: 'Alice', age: 30 })\n\t\t\tconst updated = await repo.on(TestSchema).one().id(user.id).update({ name: 'Alicia' })\n\t\t\texpect(updated?.name).toBe('Alicia')\n\n\t\t\tconst deleted = await repo.on(TestSchema).one().id(user.id).delete()\n\t\t\texpect(deleted?.id).toBe(user.id)\n\n\t\t\tconst found = await repo.on(TestSchema).one().id(user.id).find()\n\t\t\texpect(found).toBeNull()\n\t\t})\n\t})\n\n\tdescribe('repo.on().aggregate() end-to-end', () => {\n\t\tconst OrderSchema = Schema.from('orders')\n\t\t\t.pk('id', v.string(), () => `o-${Math.random().toString(36).slice(2, 8)}`)\n\t\t\t.field('product', v.string())\n\t\t\t.field('amount', v.number())\n\t\t\t.field('region', v.string())\n\t\t\t.build()\n\n\t\tfunction makeAggRepo() {\n\t\t\tconst adapter = InMemoryAdapter.create({})\n\t\t\tconst repo = Repo.from(adapter).resolve((s) => ({ table: s.name })).build()\n\t\t\treturn { repo, adapter }\n\t\t}\n\n\t\tasync function seedOrders(repo: any) {\n\t\t\tawait repo.on(OrderSchema).all().create([\n\t\t\t\t{ product: 'Widget', amount: 10, region: 'US' },\n\t\t\t\t{ product: 'Gadget', amount: 20, region: 'EU' },\n\t\t\t\t{ product: 'Widget', amount: 30, region: 'US' },\n\t\t\t\t{ product: 'Gadget', amount: 40, region: 'EU' },\n\t\t\t\t{ product: 'Widget', amount: 50, region: 'US' },\n\t\t\t])\n\t\t}\n\n\t\ttest('count returns total row count', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().count('total').run()\n\t\t\texpect(result).toEqual({ total: 5 })\n\t\t})\n\n\t\ttest('count with where filter returns filtered count', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.where((q) => q.eq('region', 'US'))\n\t\t\t\t.count('total')\n\t\t\t\t.run()\n\t\t\texpect(result).toEqual({ total: 3 })\n\t\t})\n\n\t\ttest('count on empty result returns zero', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().count('total').run()\n\t\t\texpect(result).toEqual({ total: 0 })\n\t\t})\n\n\t\ttest('clone-on-step: base builder is not mutated by fan-out', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst base = repo.on(OrderSchema).aggregate().count('a')\n\t\t\tconst withFilter = base.where((q) => q.eq('region', 'US'))\n\t\t\tconst resultAll = await base.count('b').run()\n\t\t\tconst resultFiltered = await withFilter.count('c').run()\n\t\t\texpect(resultAll.a).toBe(5)\n\t\t\texpect(resultFiltered.a).toBe(3)\n\t\t})\n\n\t\ttest('where before count produces correct result', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.count('total')\n\t\t\t\t.where((q) => q.eq('product', 'Gadget'))\n\t\t\t\t.run()\n\t\t\texpect(result).toEqual({ total: 2 })\n\t\t})\n\n\t\ttest('boundary validation rejects unknown field in where filter', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait expect(\n\t\t\t\trepo.on(OrderSchema).aggregate().count('total').where((q) => q.eq('nonexistent', 'x')).run(),\n\t\t\t).rejects.toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('sum returns total of numeric field', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().sum(OrderSchema.fields.amount, 'revenue').run()\n\t\t\texpect(result).toEqual({ revenue: 150 })\n\t\t})\n\n\t\ttest('avg returns average of numeric field', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().avg(OrderSchema.fields.amount, 'avgAmount').run()\n\t\t\texpect(result).toEqual({ avgAmount: 30 })\n\t\t})\n\n\t\ttest('min returns minimum value', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().min(OrderSchema.fields.amount, 'lowest').run()\n\t\t\texpect(result).toEqual({ lowest: 10 })\n\t\t})\n\n\t\ttest('max returns maximum value', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().max(OrderSchema.fields.amount, 'highest').run()\n\t\t\texpect(result).toEqual({ highest: 50 })\n\t\t})\n\n\t\ttest('countDistinct returns unique value count', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().countDistinct(OrderSchema.fields.product, 'unique').run()\n\t\t\texpect(result).toEqual({ unique: 2 })\n\t\t})\n\n\t\ttest('min on string field returns lexicographic minimum', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().min(OrderSchema.fields.region, 'firstRegion').run()\n\t\t\texpect(result).toEqual({ firstRegion: 'EU' })\n\t\t})\n\n\t\ttest('max on string field returns lexicographic maximum', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().max(OrderSchema.fields.region, 'lastRegion').run()\n\t\t\texpect(result).toEqual({ lastRegion: 'US' })\n\t\t})\n\n\t\ttest('combined aggregators in single chain', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.count('total')\n\t\t\t\t.sum(OrderSchema.fields.amount, 'revenue')\n\t\t\t\t.avg(OrderSchema.fields.amount, 'avgAmount')\n\t\t\t\t.min(OrderSchema.fields.amount, 'lowest')\n\t\t\t\t.max(OrderSchema.fields.amount, 'highest')\n\t\t\t\t.run()\n\t\t\texpect(result).toEqual({ total: 5, revenue: 150, avgAmount: 30, lowest: 10, highest: 50 })\n\t\t})\n\n\t\ttest('groupBy single column returns per-group results', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t.count('cnt')\n\t\t\t\t.sum(OrderSchema.fields.amount, 'total')\n\t\t\t\t.run()\n\t\t\texpect(result).toHaveLength(2)\n\t\t\tconst us = result.find((r) => r.region === 'US')\n\t\t\tconst eu = result.find((r) => r.region === 'EU')\n\t\t\texpect(us).toEqual({ region: 'US', cnt: 3, total: 90 })\n\t\t\texpect(eu).toEqual({ region: 'EU', cnt: 2, total: 60 })\n\t\t})\n\n\t\ttest('groupBy multi-column returns per-combo results', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.groupBy(OrderSchema.fields.region, OrderSchema.fields.product)\n\t\t\t\t.count('cnt')\n\t\t\t\t.run()\n\t\t\texpect(result).toHaveLength(2)\n\t\t\tconst usWidget = result.find((r) => r.region === 'US' && r.product === 'Widget')\n\t\t\tconst euGadget = result.find((r) => r.region === 'EU' && r.product === 'Gadget')\n\t\t\texpect(usWidget?.cnt).toBe(3)\n\t\t\texpect(euGadget?.cnt).toBe(2)\n\t\t})\n\n\t\ttest('where + groupBy filters before grouping', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.where((q) => q.gt('amount', 15))\n\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t.count('cnt')\n\t\t\t\t.run()\n\t\t\tconst us = result.find((r) => r.region === 'US')\n\t\t\tconst eu = result.find((r) => r.region === 'EU')\n\t\t\texpect(us?.cnt).toBe(2)\n\t\t\texpect(eu?.cnt).toBe(2)\n\t\t})\n\n\t\ttest('having filters after aggregation', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t.count('cnt')\n\t\t\t\t.sum(OrderSchema.fields.amount, 'total')\n\t\t\t\t.having((q) => q.gt('total', 80))\n\t\t\t\t.run()\n\t\t\texpect(result).toHaveLength(1)\n\t\t\texpect(result[0].region).toBe('US')\n\t\t\texpect(result[0].total).toBe(90)\n\t\t})\n\n\t\ttest('where + having work together', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.where((q) => q.gt('amount', 15))\n\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t.count('cnt')\n\t\t\t\t.having((q) => q.gte('cnt', 2))\n\t\t\t\t.run()\n\t\t\texpect(result).toHaveLength(2)\n\t\t})\n\n\t\ttest('countDistinct with groupBy', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t.countDistinct(OrderSchema.fields.product, 'uniqueProducts')\n\t\t\t\t.run()\n\t\t\tconst us = result.find((r) => r.region === 'US')\n\t\t\tconst eu = result.find((r) => r.region === 'EU')\n\t\t\texpect(us?.uniqueProducts).toBe(1)\n\t\t\texpect(eu?.uniqueProducts).toBe(1)\n\t\t})\n\n\t\ttest('step order is irrelevant: groupBy before or after aggregators', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait seedOrders(repo)\n\t\t\tconst resultA = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t.count('cnt')\n\t\t\t\t.run()\n\t\t\tconst resultB = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.count('cnt')\n\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t.run()\n\t\t\texpect(resultA.sort((a, b) => a.region.localeCompare(b.region)))\n\t\t\t\t.toEqual(resultB.sort((a, b) => a.region.localeCompare(b.region)))\n\t\t})\n\n\t\ttest('boundary validation rejects unknown groupBy field', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait expect(\n\t\t\t\t(repo.on(OrderSchema).aggregate() as any).groupBy({ name: 'nonexistent', path: ['nonexistent'] }).count('total').run(),\n\t\t\t).rejects.toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('boundary validation rejects unknown having field', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tawait expect(\n\t\t\t\trepo\n\t\t\t\t\t.on(OrderSchema)\n\t\t\t\t\t.aggregate()\n\t\t\t\t\t.groupBy(OrderSchema.fields.region)\n\t\t\t\t\t.count('cnt')\n\t\t\t\t\t.having((q) => q.gt('nonexistent', 0))\n\t\t\t\t\t.run(),\n\t\t\t).rejects.toThrow(OrmValidationError)\n\t\t})\n\n\t\ttest('min/max on empty store returns null', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tconst result = await repo\n\t\t\t\t.on(OrderSchema)\n\t\t\t\t.aggregate()\n\t\t\t\t.min(OrderSchema.fields.amount, 'lowest')\n\t\t\t\t.max(OrderSchema.fields.amount, 'highest')\n\t\t\t\t.run()\n\t\t\texpect(result).toEqual({ lowest: null, highest: null })\n\t\t})\n\n\t\ttest('avg on empty store returns zero', async () => {\n\t\t\tconst { repo } = makeAggRepo()\n\t\t\tconst result = await repo.on(OrderSchema).aggregate().avg(OrderSchema.fields.amount, 'avgAmount').run()\n\t\t\texpect(result).toEqual({ avgAmount: 0 })\n\t\t})\n\t})\n\n}\n"],"mappings":"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","names":["orm_exports","__export","AggregateBuilder","AllBuilder","ComputedField","EventLog","EventLogSchema","Field","Filter","FilterGroup","IncOp","ManyRelation","MaxOp","MigrationCodegen","Migrator","MinOp","MulOp","OneBuilder","OneRelation","OrderBy","OrmAdapter","OrmIntrospectionError","OrmMigrationError","OrmNotFoundError","OrmReplayError","OrmValidationError","PatchOp","PullOp","PushOp","Relations","Repo","Schema","SchemaBuilder","SchemaContext","SchemaField","SchemaRef","SetOp","UnsetOp","assertNormalisedAggregate","assertNormalisedFilter","composeSchemaConfig","computePending","diffSchemas","flattenOps","inc","isUpdateOp","max","min","mul","opTouchedFields","patch","pull","push","set","toFieldName","tryValidateCreateRow","unset","validateCreate","validateCreateMany","validateUpdate","validateUpdateOps","validateUpsertConflicts","__toCommonJS","EquippedError","message","context","cause","import_pino","import_ulid","import_valleyed","registerHook","hooks","record","dep","h","depName","ownerName","tentative","detectCycle","classToAfter","existing","visited","stack","visit","cls","path","cycleStart","names","c","resolveHookDAG","invert","classToHooks","anonymous","list","classes","inDegree","graph","from","to","edges","layers","queue","layer","next","neighbor","d","anonWithDeps","anonWithoutDeps","maxLayer","depLayer","l","r","insertAt","runHooks","onError","error","import_valleyed","instanceSettingsPipe","Instance","_Instance","#id","#instance","#hooks","settings","pino","#registerOnExitHandler","id","EquippedError","name","key","runHooks","error","envsPipe","envValidity","settingsValidity","instanceSettingsPipe","event","cb","options","record","registerHook","signal","code","value","opts","OrmIntrospectionError","EquippedError","opts","OrmMigrationError","EquippedError","opts","renderFilter","group","Filter","f","renderFilterTree","node","parts","c","OrmNotFoundError","EquippedError","opts","msg","OrmReplayError","EquippedError","opts","OrmValidationError","EquippedError","kind","schema","operation","failures","import_valleyed","Field","name","path","toFieldName","field","SchemaField","pipe","opts","ComputedField","deps","compute","Filter","field","op","value","toFieldName","FilterGroup","_FilterGroup","children","#withChild","child","facFns","EquippedError","group","fn","c","assertNormalisedAggregate","schema","adapter","spec","failures","seenAliases","agg","allFields","fieldNames","OrmValidationError","assertNormalisedFilter","walkHaving","node","validHavingFields","havingErrors","errors","walk","e","OrmAdapter","err","wrapped","EquippedError","Instance","self","schema","config","emptyIterator","use","filter","opts","d","match","pk","pkFilter","FilterGroup","create","ops","row","args","spec","import_ulid","import_valleyed","import_ulid","import_valleyed","import_valleyed","SchemaBuilder","_SchemaBuilder","#name","#pkField","#fieldDefs","#computedDefs","name","pkField","fieldDefs","computedDefs","pipe","generate","SchemaField","opts","nextFields","deps","compute","nextComputed","ComputedField","self","Schema","field","EventLogSchema","Schema","fire","repo","name","def","payload","ctx","validated","OrmValidationError","at","ts","key","by","EventLogSchema","evCtx","HandlerRegistry","#handlers","#order","name","def","EquippedError","executeReplay","repo","def","row","evCtx","cause","OrmReplayError","replay","registry","opts","query","EventLogSchema","q","rows","rerun","key","EventLog","_EventLog","#repo","#registry","repo","registry","name","def","payload","ctx","fire","opts","replay","key","rerun","HandlerRegistry","EMISSION_ORDER","adapterSupports","adapter","kind","method","fieldsEqual","target","discovered","toFieldPayload","f","extractSchemaInfo","schema","pkField","pkType","inferFieldType","fields","field","isNullable","KNOWN_FIELD_TYPES","pipe","std","diffSchemas","current","changes","currentByName","targetByName","disc","fk","idx","info","discFieldsByName","targetFieldsByName","discField","tField","existing","c","a","b","MigrationCodegen","_MigrationCodegen","#adapter","#target","_repo","adapter","target","current","changes","diffSchemas","repo","schemas","applyChange","adapter","repo","change","methodName","method","computePending","declared","applied","opts","appliedIds","a","declaredIds","orphans","OrmMigrationError","sorted","b","pending","skipped","m","toIdx","isEmpty","s","validateFieldSpec","ctx","fieldSpec","fieldLabel","fail","field","cause","validateChange","adapter","change","migrationId","changeIndex","failures","methodName","fieldNames","f","assertNormalisedChanges","migrations","seenIds","m","i","OrmValidationError","Migrator","_Migrator","#repo","#adapter","#migrations","#withoutLock","repo","adapter","migrations","withoutLock","#requireMethod","name","phase","OrmMigrationError","opts","#runPending","err","applied","appliedMap","a","b","m","appliedAt","pending","computePending","skipped","ran","execute","c","applyChange","assertNormalisedChanges","OrderBy","field","direction","toFieldName","ManyRelation","name","source","target","foreignKey","references","OneRelation","fkOwner","RelationsBuilder","_RelationsBuilder","#source","#defs","defs","fk","nextDefs","ref","Relations","import_valleyed","planSelection","schema","select","computedDefs","computedNames","persistedNames","requestedSelect","adapterSelect","selectedComputeNames","key","dep","EquippedError","applyComputedSelection","rows","plan","row","enriched","computeName","def","depInput","r","shaped","FALLBACK_PAGINATION_DEFAULT_LIMIT","MAX_PRELOAD_DEPTH","isRelDef","def","ManyRelation","OneRelation","isNestedPreloadDef","relationStep","isPositiveInteger","value","isNonNegativeInteger","getPaginationDefaultLimit","Instance","collectSelectFailures","schema","select","failures","persistedNames","computedNames","field","collectPreloadFailures","defs","depth","path","preload","rawDef","step","nested","collectLimitFailure","collectOffsetFailure","collectPageFailure","collectBatchSizeFailure","collectIterationOptionFailures","options","throwIfFailures","operation","OrmValidationError","assertNormalisedFindReadShape","state","normaliseAllFindReadShape","limit","offset","page","resolvedOffset","normaliseAllIterateReadShape","batchSize","normaliseAllPaginateReadShape","current","import_valleyed","SetOp","values","IncOp","field","value","MulOp","MinOp","MaxOp","UnsetOp","PushOp","PullOp","PatchOp","isUpdateOp","v","set","inc","toFieldName","mul","min","max","unset","push","pull","patch","opTouchedFields","op","flattenOps","ops","data","tryValidateCreateRow","s","data","failures","result","key","entry","pipe","fieldValue","validated","validateCreate","OrmValidationError","validateCreateMany","rows","allFailures","i","failure","validateUpdate","pipes","ops","value","isUpdateOp","r","validateUpdateOps","schema","operation","touched","field","opTouchedFields","autoBumped","bumpOp","SetOp","allOps","conflicts","indices","op","fieldDef","composeSchemaConfig","resolve","transforms","config","transform","validateUpsertConflicts","rawCreate","createFields","runOneRead","context","state","assertNormalisedFilter","assertNormalisedFindReadShape","row","runAllRead","query","normaliseAllFindReadShape","plan","planSelection","rows","runAllCount","toPaginated","items","total","limit","current","last","previous","next","runAllPaginate","normaliseAllPaginateReadShape","use","runAllIterate","options","normaliseAllIterateReadShape","first","runOneCreate","data","validated","validateCreate","resolved","runAllCreate","validateCreateMany","runOneUpdate","validateUpdate","runAllUpdate","runOneUpsert","create","ops","plainValues","key","value","isUpdateOp","SetOp","validateUpsertConflicts","runOneDelete","runAllDelete","runAggregate","spec","assertNormalisedAggregate","MAX_PRELOAD_DEPTH","isNestedPreloadDef","def","relationStep","uniqueDefinedValues","entities","key","entity","value","attachOneRelation","name","lookupKey","lookup","attachManyRelation","normalizePreloads","defs","ManyRelation","OneRelation","resolvePreloads","getUse","resolvePreloadNodes","depth","path","resolvePreload","node","EquippedError","target","step","nextPath","refCol","fkValues","e","related","FilterGroup","r","refValues","grouped","fk","SchemaContext","schema","getUse","select","preloads","rows","plan","planSelection","selected","applyComputedSelection","resolvePreloads","row","resolved","ReadSelectState","context","state","FilterGroup","factory","nextGroup","fields","defs","next","SchemaRef","#context","OneBuilder","AllBuilder","AggregateBuilder","args","_OneBuilder","reqState","value","message","result","operation","OrmNotFoundError","data","runOneCreate","runOneUpdate","runOneUpsert","runOneDelete","runOneRead","_AllBuilder","#orderBy","#limitSource","#offsetSource","queryState","#withQuery","queryOverride","has","key","field","direction","OrderBy","limit","offset","page","runAllCreate","runAllUpdate","runAllDelete","runAllRead","runAllCount","runAllPaginate","options","runAllIterate","_AggregateBuilder","#where","#having","#aggregates","#groupBy","#state","f","toFieldName","alias","_","spec","runAggregate","import_node_async_hooks","store","run","transform","fn","current","currentTransforms","Repo","#adapter","#defaults","#schemaConfigPipe","adapter","resolve","#getConfig","s","transforms","currentTransforms","composeSchemaConfig","config","transform","#getUse","schema","SchemaRef","SchemaContext","target","RepoBuilder","fn","resolver","run","_RepoBuilder","#resolve"]}