import type { BrowserDataResult, IndexedDbRepositoryPort, } from "../../../../src/application/ports/browser-file-storage/index.ts"; export type LocalDraft = Readonly<{ draftId: string; title: string; body: string; }>; export type SaveLocalDraftCommand = Readonly<{ draft: LocalDraft; expectedRevision: number | null; idempotencyKey: string; }>; export type RemoveLocalDraftCommand = Readonly<{ draftId: string; expectedRevision: number; idempotencyKey: string; }>; export type LocalDraftRecord = Readonly<{ draft: LocalDraft; revision: number; }>; export interface LocalDraftStore { save( command: SaveLocalDraftCommand, signal?: AbortSignal, ): Promise>>; find( draftId: string, signal?: AbortSignal, ): Promise>; remove( command: RemoveLocalDraftCommand, signal?: AbortSignal, ): Promise>; } /** * Feature-owned binding over the technology-neutral IndexedDB application port. * * The feature knows its domain type and optimistic concurrency inputs. It does * not know database names, stores, transactions, native IDB objects, codecs, * migrations, quota handling or connection lifecycle. */ export function createLocalDraftStore( repository: IndexedDbRepositoryPort, ): LocalDraftStore { const store: LocalDraftStore = { async save(command, signal) { const result = await repository.compareAndSwap({ key: command.draft.draftId, value: command.draft, expectedRevision: command.expectedRevision, idempotencyKey: command.idempotencyKey, signal, }); if (!result.ok) return result; return Object.freeze({ ok: true as const, value: Object.freeze({ revision: result.value.revision }), }); }, async find(draftId, signal) { const result = await repository.read(draftId, signal); if (!result.ok) return result; return Object.freeze({ ok: true as const, value: result.value === null ? null : Object.freeze({ draft: result.value.value, revision: result.value.revision, }), }); }, async remove(command, signal) { const result = await repository.remove({ key: command.draftId, expectedRevision: command.expectedRevision, idempotencyKey: command.idempotencyKey, signal, }); if (!result.ok) return result; return Object.freeze({ ok: true as const, value: undefined }); }, }; return Object.freeze(store); }