import type { DocumentStoreKind } from "@/lib/app/documentStoreProvider"; import { prepareDocumentStoreWriteDocument, stringifyDocumentStoreValue, type DocumentStoreIdentityPlan } from "@/lib/app/documentJsonValues"; export type DocumentStoreWriteApis = { insert: (docJson: string, routing?: string) => Promise; update: (id: string, docJson: string, routing?: string) => Promise; delete: (id: string, routing?: string) => Promise; }; /** * Write a document body under a known identity. * - `put`: Elasticsearch index-by-id / Mongo update-by-id (identity via path, not body). * - `insert`: Mongo insert (and ES auto-id when no explicit id); routing is always an API arg. */ export async function writeDocumentStoreDocument(options: { kind: DocumentStoreKind; op: "put" | "insert"; id?: string; routing?: string; document: Record; apis: Pick }): Promise { const prepared = prepareDocumentStoreWriteDocument(options.document, { kind: options.kind, mode: options.op === "put" ? "update" : "insert", }); const body = stringifyDocumentStoreValue(prepared, options.kind); if (options.op === "put") { if (!options.id) throw new Error("Document write requires an id"); await options.apis.update(options.id, body, options.routing); return; } await options.apis.insert(body, options.routing); } /** * Apply an identity plan for an existing document save. * Rekey always writes first, then deletes the old identity — a failed write never deletes. * Plan coordinates are assumed distinct for rekey (same identity is always `replace`). */ export async function applyDocumentStoreIdentityPlan(options: { kind: DocumentStoreKind; plan: DocumentStoreIdentityPlan; document: Record; apis: DocumentStoreWriteApis }): Promise { const { kind, plan, document, apis } = options; if (plan.action === "replace") { await writeDocumentStoreDocument({ kind, op: "put", id: plan.writeId, routing: plan.writeRouting, document, apis, }); return; } // Rekey write: ES uses put under the new id/routing; Mongo inserts a new document then deletes the old id. if (kind === "elasticsearch") { await writeDocumentStoreDocument({ kind, op: "put", id: plan.writeId, routing: plan.writeRouting, document, apis, }); } else { await writeDocumentStoreDocument({ kind, op: "insert", document, apis, }); } // Only reached after a successful write — preserves the old document when write fails. await apis.delete(plan.deleteId, plan.deleteRouting); } /** Insert a new document (optional explicit ES id uses put). */ export async function insertDocumentStoreDocument(options: { kind: DocumentStoreKind; document: Record; explicitId?: string | null; routing?: string; apis: Pick }): Promise { const { kind, document, explicitId, routing, apis } = options; if (kind === "elasticsearch" && explicitId) { await writeDocumentStoreDocument({ kind, op: "put", id: explicitId, routing, document, apis, }); return; } await writeDocumentStoreDocument({ kind, op: "insert", routing, document, apis, }); }