ocean-brain 0.7.4 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +3 -3
- package/dist/mcp.js +180 -736
- package/package.json +4 -1
- package/server/client/dist/assets/ArrowRight.es-C4zlc54Z.js +1 -0
- package/server/client/dist/assets/{Calendar-Bz7jheg6.js → Calendar-COrN6Hjv.js} +2 -2
- package/server/client/dist/assets/Callout-BK0K9E74.js +1 -0
- package/server/client/dist/assets/Copy.es-zm8f_XXX.js +1 -0
- package/server/client/dist/assets/Graph-BH1fzhue.js +1 -0
- package/server/client/dist/assets/{Image-DxWhlIDG.js → Image-KXuHOG4b.js} +1 -1
- package/server/client/dist/assets/Image.es-BKahRbIm.js +1 -0
- package/server/client/dist/assets/Note-C143cKKw.js +12 -0
- package/server/client/dist/assets/Plus.es-BqeC8T8h.js +1 -0
- package/server/client/dist/assets/Reminders-BYG5rz1M.js +1 -0
- package/server/client/dist/assets/Search-DBWsIMDP.js +1 -0
- package/server/client/dist/assets/{SurfaceCard-CfL1jD-O.js → SurfaceCard-CNSQfYfM.js} +1 -1
- package/server/client/dist/assets/Tag-5aCfSO8Y.js +1 -0
- package/server/client/dist/assets/TagNotes-BPZjN-6l.js +1 -0
- package/server/client/dist/assets/Trash.es-CRQkTnXZ.js +1 -0
- package/server/client/dist/assets/ViewNotes-DGLNF96x.js +1 -0
- package/server/client/dist/assets/ViewSectionDialog-CzskgmMF.js +102 -0
- package/server/client/dist/assets/Views-DR5jT23G.js +1 -0
- package/server/client/dist/assets/{graph-vendor-CUxe67Lr.js → graph-vendor-UTvF2NS_.js} +2 -2
- package/server/client/dist/assets/image.api-8dlMwzMd.js +17 -0
- package/server/client/dist/assets/{index-C5BGzDgt.css → index-BEUqDuAZ.css} +1 -1
- package/server/client/dist/assets/{index-DiMt3Dz4.js → index-DGIAh3ZY.js} +1 -1
- package/server/client/dist/assets/index-VHp17GQX.js +342 -0
- package/server/client/dist/assets/manage-image-RqtWKas2.js +1 -0
- package/server/client/dist/assets/{manage-image-detail-Bqw8NURO.js → manage-image-detail-BTSIbLlQ.js} +1 -1
- package/server/client/dist/assets/mcp-EDurbggR.js +4 -0
- package/server/client/dist/assets/mcp-admin-adapter-DmGUo5G8.js +1 -0
- package/server/client/dist/assets/{note-core-Z6kqulGB.js → note-core-BklwyZHk.js} +1 -1
- package/server/client/dist/assets/{note-vendor-i1M2FSI2.js → note-vendor-DkYKUmLo.js} +1 -1
- package/server/client/dist/assets/{placeholder-Ro00byT7.js → placeholder-DeKgSBnC.js} +4 -4
- package/server/client/dist/assets/{properties-Tid6-ghT.js → properties-BjrZvOYn.js} +1 -1
- package/server/client/dist/assets/tag.api-Dl8DQd0m.js +21 -0
- package/server/client/dist/assets/trash-C5ntddLu.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-BWymIujb.js → useReminderMutate-CoXXqJY2.js} +1 -1
- package/server/client/dist/index.html +5 -5
- package/server/dist/app.js +2 -2
- package/server/dist/app.js.map +1 -1
- package/server/dist/features/auth/http/api.js +5 -1
- package/server/dist/features/auth/http/api.js.map +1 -1
- package/server/dist/features/auth/http/login-page.js +1 -1
- package/server/dist/features/auth/http/pages.js +8 -1
- package/server/dist/features/auth/http/pages.js.map +1 -1
- package/server/dist/features/auth/service.js +16 -7
- package/server/dist/features/auth/service.js.map +1 -1
- package/server/dist/features/image/http/upload.js +34 -23
- package/server/dist/features/image/http/upload.js.map +1 -1
- package/server/dist/features/image/services/validation.js +111 -0
- package/server/dist/features/image/services/validation.js.map +1 -0
- package/server/dist/features/mcp-admin/http/handlers.js +11 -3
- package/server/dist/features/mcp-admin/http/handlers.js.map +1 -1
- package/server/dist/features/note/http/mcp.js +8 -43
- package/server/dist/features/note/http/mcp.js.map +1 -1
- package/server/dist/features/note/services/cleanup.js +2 -2
- package/server/dist/features/note/services/cleanup.js.map +1 -1
- package/server/dist/features/note/services/markdown-intent-write.js +11 -43
- package/server/dist/features/note/services/markdown-intent-write.js.map +1 -1
- package/server/dist/features/note/services/markdown-patch.js +4 -123
- package/server/dist/features/note/services/markdown-patch.js.map +1 -1
- package/server/dist/features/note/services/write.js +13 -13
- package/server/dist/features/note/services/write.js.map +1 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js +16 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js.map +1 -1
- package/server/dist/features/tag/graphql/tag.type-defs.js +2 -0
- package/server/dist/features/tag/graphql/tag.type-defs.js.map +1 -1
- package/server/dist/modules/app-version.js +105 -0
- package/server/dist/modules/app-version.js.map +1 -0
- package/server/dist/modules/auth-guard.js +21 -2
- package/server/dist/modules/auth-guard.js.map +1 -1
- package/server/dist/modules/auth-redirect.js +10 -0
- package/server/dist/modules/auth-redirect.js.map +1 -0
- package/server/dist/modules/mcp-auth.js +62 -1
- package/server/dist/modules/mcp-auth.js.map +1 -1
- package/server/dist/modules/rate-limit.js +15 -1
- package/server/dist/modules/rate-limit.js.map +1 -1
- package/server/dist/modules/server-events-handler.js +17 -0
- package/server/dist/modules/server-events-handler.js.map +1 -1
- package/server/dist/modules/session-store.js +149 -0
- package/server/dist/modules/session-store.js.map +1 -0
- package/server/dist/paths.js +1 -0
- package/server/dist/paths.js.map +1 -1
- package/server/dist/routes/api.js +39 -20
- package/server/dist/routes/api.js.map +1 -1
- package/server/dist/routes/auth-pages.js +13 -2
- package/server/dist/routes/auth-pages.js.map +1 -1
- package/server/dist/routes/client.js +63 -3
- package/server/dist/routes/client.js.map +1 -1
- package/server/dist/routes/graphql.js +23 -19
- package/server/dist/routes/graphql.js.map +1 -1
- package/server/client/dist/assets/Callout-DlFLkTCG.js +0 -1
- package/server/client/dist/assets/Graph-BdxtJvog.js +0 -1
- package/server/client/dist/assets/Image.es-BFodqKS5.js +0 -1
- package/server/client/dist/assets/Note-BgMxsWZT.js +0 -22
- package/server/client/dist/assets/Plus.es-Yq7J1Ur4.js +0 -1
- package/server/client/dist/assets/Reminders-CUsR56ff.js +0 -1
- package/server/client/dist/assets/Search-DncrGgOZ.js +0 -1
- package/server/client/dist/assets/Tag-CKDLs2Y6.js +0 -1
- package/server/client/dist/assets/TagNotes-ByX_Qias.js +0 -1
- package/server/client/dist/assets/Trash.es-BPrF6TPV.js +0 -1
- package/server/client/dist/assets/ViewNotes-r1kxKZ8O.js +0 -1
- package/server/client/dist/assets/ViewSectionTableRenderer-DQ4UeCnm.js +0 -102
- package/server/client/dist/assets/Views-FzFBtNT2.js +0 -1
- package/server/client/dist/assets/image.api-Bcg3TFCG.js +0 -17
- package/server/client/dist/assets/index-BFfQKAUY.js +0 -361
- package/server/client/dist/assets/manage-image-DzDlncCM.js +0 -1
- package/server/client/dist/assets/mcp-BqasqTWC.js +0 -1
- package/server/client/dist/assets/trash-bHK02-Up.js +0 -1
- package/server/dist/features/image/services/remote-fetch.js +0 -176
- package/server/dist/features/image/services/remote-fetch.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/markdown-intent-write.ts"],"sourcesContent":["import models, { type NoteLayout } from '~/models.js';\nimport {\n blocksToMarkdown,\n countReferenceInlinesFromContentJson,\n extractLiteralAngleBracketTextTokens,\n extractTagIdsFromContentJson,\n hasLiteralAngleBracketTextTokenLoss,\n hasNumericTildeRangeMarkerLoss,\n hasNumericTildeRangeMarkers,\n hasUnsupportedMarkdownBlocks,\n markdownToBlocksJson,\n} from '~/modules/blocknote.js';\nimport type {\n MarkdownAppendPlanResult,\n MarkdownChangeDryRun,\n MarkdownChangeFailure,\n MarkdownChangePlan,\n MarkdownPatchOperation,\n MarkdownPatchPlanResult,\n MarkdownPatchSelector,\n MarkdownReplacePlanResult,\n} from './markdown-patch.js';\nimport {\n buildMarkdownAppendPlan,\n buildMarkdownPatchPlan,\n buildMarkdownReplacePlan,\n calculateMarkdownSha256,\n} from './markdown-patch.js';\nimport {\n InvalidNotePropertyInputError,\n type NotePropertiesByKeyPatchInput,\n type NotePropertiesPatchInput,\n resolveNotePropertiesPatchValueTypes,\n updateNotePropertiesWithVersionGuardAndSnapshot,\n validateNotePropertiesPatchValues,\n} from './properties.js';\nimport { MCP_SNAPSHOT_META } from './snapshot.js';\nimport { type GuardedNoteWriteResult, updateNoteWithVersionGuardAndSnapshot } from './write.js';\nimport {\n isInvalidNoteVersionError,\n isMissingNoteVersionError,\n isNoteVersionConflictError,\n parseNoteVersion,\n} from './write-conflict.js';\n\ninterface MarkdownIntentNoteRecord {\n id: number;\n title: string;\n content: string;\n layout: NoteLayout;\n updatedAt: Date;\n}\n\ninterface MarkdownIntentWriteDeps {\n findNoteById: (id: number) => Promise<MarkdownIntentNoteRecord | null>;\n renderMarkdown: (contentJson: string) => Promise<string>;\n parseMarkdownToContentJson: (markdown: string) => Promise<string>;\n extractTagIds: (contentJson: string) => string[];\n countReferenceInlines?: (contentJson: string) => number;\n hasUnsupportedMarkdownBlocks?: (contentJson: string) => boolean;\n updateNote: (input: {\n id: number;\n data: {\n title?: string;\n content?: string;\n layout?: NoteLayout;\n tagIds?: number[];\n };\n expectedUpdatedAt: string;\n snapshotMeta?: string;\n force?: boolean;\n }) => Promise<GuardedNoteWriteResult | null>;\n resolvePropertyPatch?: (patch: NotePropertiesByKeyPatchInput) => Promise<NotePropertiesPatchInput>;\n validatePropertyPatch?: (patch: NotePropertiesPatchInput) => Promise<NotePropertiesPatchInput>;\n updateProperties?: (input: {\n id: number;\n patch: NotePropertiesPatchInput;\n expectedUpdatedAt: string;\n noteData?: {\n title?: string;\n layout?: NoteLayout;\n };\n snapshotMeta?: string;\n }) => Promise<{\n note: {\n id: number;\n title: string;\n layout: NoteLayout;\n updatedAt: Date;\n };\n snapshot: unknown;\n } | null>;\n}\n\nexport interface MarkdownWritePolicy {\n allowNoop?: boolean;\n maxChangedChars?: number;\n maxChangedLines?: number;\n diffPreviewMaxChars?: number;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface PatchNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n selector: MarkdownPatchSelector;\n operation: MarkdownPatchOperation;\n policy?: MarkdownWritePolicy;\n dryRun?: boolean;\n}\n\nexport interface AppendNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n insertion: string;\n placement?:\n | {\n type: 'end';\n }\n | {\n type: 'after_heading';\n heading: string;\n level?: number;\n };\n separator?: '\\n\\n' | '\\n';\n policy?: MarkdownWritePolicy;\n dryRun?: boolean;\n}\n\nexport interface ReplaceNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownWritePolicy;\n dryRun?: boolean;\n}\n\nexport interface UpdateNoteMetadataInput {\n id: number;\n expectedUpdatedAt: string;\n title?: string;\n layout?: NoteLayout;\n properties?: NotePropertiesByKeyPatchInput;\n}\n\nexport interface AppliedMarkdownWriteResult {\n status: 'applied';\n note: {\n id: string;\n updatedAt: string;\n };\n change: {\n summary: string;\n changedLineCount: number;\n changedCharCount: number;\n };\n snapshot: {\n id: string;\n label: string;\n createdAt: string;\n };\n}\n\nexport interface MetadataUpdatePreview {\n status: 'dry_run';\n note: {\n id: string;\n title: string;\n updatedAt: string;\n };\n proposed: {\n title?: string;\n layout?: NoteLayout;\n properties?: NotePropertiesPatchInput;\n };\n warnings: string[];\n}\n\nexport interface AppliedMetadataUpdateResult {\n status: 'applied';\n note: {\n id: string;\n title: string;\n layout: NoteLayout;\n updatedAt: string;\n };\n snapshot: {\n id: string;\n label: string;\n createdAt: string;\n };\n}\n\nexport type MarkdownIntentWriteResult =\n | MarkdownChangeDryRun\n | AppliedMarkdownWriteResult\n | MarkdownChangeFailure\n | Extract<MarkdownPatchPlanResult, { status: 'needs_disambiguation' }>;\n\nexport type MetadataUpdateResult = MetadataUpdatePreview | AppliedMetadataUpdateResult | MarkdownChangeFailure;\n\nconst serializeNoteSnapshot = async (\n note: MarkdownIntentNoteRecord,\n renderMarkdown: (contentJson: string) => Promise<string>,\n) => {\n return {\n id: String(note.id),\n title: note.title,\n updatedAt: note.updatedAt.toISOString(),\n markdown: await renderMarkdown(note.content),\n };\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst serializeSnapshot = (snapshot: unknown) => {\n const meta = isRecord(snapshot) && isRecord(snapshot.meta) ? snapshot.meta : {};\n const label = typeof meta.label === 'string' ? meta.label : 'MCP';\n\n return {\n id: isRecord(snapshot) && snapshot.id !== undefined ? String(snapshot.id) : '',\n label,\n createdAt: isRecord(snapshot) && typeof snapshot.createdAt === 'string' ? snapshot.createdAt : '',\n };\n};\n\nconst toDryRun = (plan: MarkdownChangePlan): MarkdownChangeDryRun => ({\n status: 'dry_run',\n note: plan.note,\n ...(plan.match ? { match: plan.match } : {}),\n ...(plan.placement ? { placement: plan.placement } : {}),\n proposed: plan.proposed,\n warnings: plan.warnings,\n});\n\nconst toTagIds = (tagIds: string[]) => {\n return tagIds.map((tagId) => Number(tagId)).filter((tagId) => Number.isSafeInteger(tagId) && tagId > 0);\n};\n\nconst baselineMismatchFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message:\n 'The markdown write baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n});\n\nconst missingBaselineFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n});\n\nconst unsupportedMarkdownStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'UNSUPPORTED_MARKDOWN_STRUCTURE',\n message: 'This note contains BlockNote content that cannot be safely represented as Markdown.',\n});\n\nconst referenceStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'REFERENCE_STRUCTURE_DECREASED',\n message: 'The markdown write would reduce structured note reference links.',\n});\n\nconst markdownImportLossyFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'MARKDOWN_IMPORT_LOSSY',\n message: 'The markdown write would lose literal text during Markdown import.',\n});\n\nconst invalidPropertyInputFailure = (error: InvalidNotePropertyInputError): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'INVALID_PROPERTY_INPUT',\n message: error.message,\n});\n\nconst mapGuardedWriteError = (error: unknown): MarkdownChangeFailure | null => {\n if (isNoteVersionConflictError(error) || isInvalidNoteVersionError(error)) {\n return baselineMismatchFailure();\n }\n\n if (isMissingNoteVersionError(error)) {\n return missingBaselineFailure();\n }\n\n return null;\n};\n\nconst resolveMetadataPropertyPatch = async (\n deps: MarkdownIntentWriteDeps,\n patch: NotePropertiesByKeyPatchInput | undefined,\n): Promise<NotePropertiesPatchInput | MarkdownChangeFailure | undefined> => {\n if (patch === undefined) {\n return undefined;\n }\n\n if (!deps.resolvePropertyPatch || !deps.validatePropertyPatch) {\n throw new Error('Metadata property patch dependencies are not configured.');\n }\n\n try {\n const resolvedPatch = await deps.resolvePropertyPatch(patch);\n return deps.validatePropertyPatch(resolvedPatch);\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n return invalidPropertyInputFailure(error);\n }\n\n throw error;\n }\n};\n\nconst noteVersionMatches = (expectedUpdatedAt: string | undefined, currentUpdatedAt: Date) => {\n try {\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n return expectedTimestamp !== null && expectedTimestamp === currentUpdatedAt.getTime();\n } catch {\n return false;\n }\n};\n\nconst getUnsupportedSourceFailure = (deps: MarkdownIntentWriteDeps, note: MarkdownIntentNoteRecord) => {\n if (deps.hasUnsupportedMarkdownBlocks?.(note.content)) {\n return unsupportedMarkdownStructureFailure();\n }\n\n return null;\n};\n\nconst applyMarkdownPlan = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: Extract<MarkdownPatchPlanResult, { status: 'dry_run' }>;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<AppliedMarkdownWriteResult | MarkdownChangeFailure> => {\n const content = await deps.parseMarkdownToContentJson(input.plan.afterMarkdown);\n const literalAngleBracketTokens = extractLiteralAngleBracketTextTokens(input.plan.afterMarkdown);\n const shouldCheckMarkdownImportLoss =\n literalAngleBracketTokens.length > 0 || hasNumericTildeRangeMarkers(input.plan.afterMarkdown);\n\n if (shouldCheckMarkdownImportLoss) {\n const importedMarkdown = await deps.renderMarkdown(content);\n\n if (\n hasLiteralAngleBracketTextTokenLoss(input.plan.afterMarkdown, importedMarkdown) ||\n hasNumericTildeRangeMarkerLoss(input.plan.afterMarkdown, importedMarkdown)\n ) {\n return markdownImportLossyFailure();\n }\n }\n\n const beforeReferenceCount = deps.countReferenceInlines?.(input.beforeContentJson) ?? 0;\n const afterReferenceCount = deps.countReferenceInlines?.(content) ?? 0;\n\n if (input.policy?.preserveReferences === true && afterReferenceCount < beforeReferenceCount) {\n return referenceStructureFailure();\n }\n\n const tagIds = toTagIds(deps.extractTagIds(content));\n let updateResult: GuardedNoteWriteResult | null;\n\n try {\n updateResult = await deps.updateNote({\n id: input.noteId,\n data: {\n content,\n tagIds,\n },\n expectedUpdatedAt: input.expectedUpdatedAt ?? input.noteUpdatedAt,\n snapshotMeta: MCP_SNAPSHOT_META,\n ...(input.force ? { force: true } : {}),\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n change: {\n summary: input.summary,\n changedLineCount: input.plan.proposed.changedLineCount,\n changedCharCount: input.plan.proposed.changedCharCount,\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n};\n\nconst mapPlanResult = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: MarkdownPatchPlanResult | MarkdownAppendPlanResult | MarkdownReplacePlanResult;\n dryRun?: boolean;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<MarkdownIntentWriteResult> => {\n if (input.plan.status !== 'dry_run') {\n return input.plan;\n }\n\n if (input.dryRun ?? true) {\n return toDryRun(input.plan);\n }\n\n return applyMarkdownPlan(deps, {\n noteId: input.noteId,\n noteUpdatedAt: input.noteUpdatedAt,\n beforeContentJson: input.beforeContentJson,\n plan: input.plan,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.summary,\n ...(input.force ? { force: true } : {}),\n });\n};\n\nexport const createMarkdownIntentWriteService = (deps: MarkdownIntentWriteDeps) => ({\n patchNoteMarkdown: async (input: PatchNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownPatchPlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n selector: input.selector,\n operation: input.operation,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n dryRun: input.dryRun,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n appendNoteMarkdown: async (input: AppendNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownAppendPlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n insertion: input.insertion,\n placement: input.placement,\n separator: input.separator,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n dryRun: input.dryRun,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n replaceNoteMarkdown: async (input: ReplaceNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownReplacePlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n replacement: input.replacement,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n dryRun: input.dryRun,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n updateNoteMetadata: async (input: UpdateNoteMetadataInput): Promise<MetadataUpdateResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n if (!noteVersionMatches(input.expectedUpdatedAt, note.updatedAt)) {\n return {\n status: 'failed',\n reason: input.expectedUpdatedAt ? 'BASELINE_MISMATCH' : 'MISSING_BASELINE',\n message:\n 'The metadata update baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n };\n }\n\n const nextTitle = input.title?.trim();\n const resolvedPropertyPatch = await resolveMetadataPropertyPatch(deps, input.properties);\n\n if (resolvedPropertyPatch && 'status' in resolvedPropertyPatch) {\n return resolvedPropertyPatch;\n }\n\n if (input.title !== undefined && !nextTitle) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Note title must not be empty.',\n };\n }\n\n if (\n nextTitle === note.title &&\n (input.layout === undefined || input.layout === note.layout) &&\n resolvedPropertyPatch === undefined\n ) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The metadata update would not change the note.',\n };\n }\n\n return {\n status: 'dry_run',\n note: {\n id: String(note.id),\n title: note.title,\n updatedAt: note.updatedAt.toISOString(),\n },\n proposed: {\n ...(nextTitle !== undefined ? { title: nextTitle } : {}),\n ...(input.layout !== undefined ? { layout: input.layout } : {}),\n ...(resolvedPropertyPatch ? { properties: resolvedPropertyPatch } : {}),\n },\n warnings: [],\n };\n },\n\n applyNoteMetadata: async (input: UpdateNoteMetadataInput): Promise<MetadataUpdateResult> => {\n const preview = await createMarkdownIntentWriteService(deps).updateNoteMetadata(input);\n\n if (preview.status !== 'dry_run') {\n return preview;\n }\n\n if (input.properties !== undefined) {\n if (!deps.updateProperties) {\n throw new Error('Metadata property write dependency is not configured.');\n }\n\n let updateResult: Awaited<ReturnType<NonNullable<MarkdownIntentWriteDeps['updateProperties']>>>;\n\n try {\n updateResult = await deps.updateProperties({\n id: input.id,\n patch: preview.proposed.properties ?? { set: [], deleteKeys: [] },\n expectedUpdatedAt: input.expectedUpdatedAt,\n noteData: {\n ...(preview.proposed.title !== undefined ? { title: preview.proposed.title } : {}),\n ...(preview.proposed.layout !== undefined ? { layout: preview.proposed.layout } : {}),\n },\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n if (error instanceof InvalidNotePropertyInputError) {\n return invalidPropertyInputFailure(error);\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n title: updateResult.note.title,\n layout: updateResult.note.layout,\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n }\n\n let updateResult: GuardedNoteWriteResult | null;\n\n try {\n updateResult = await deps.updateNote({\n id: input.id,\n data: {\n ...(preview.proposed.title !== undefined ? { title: preview.proposed.title } : {}),\n ...(preview.proposed.layout !== undefined ? { layout: preview.proposed.layout } : {}),\n },\n expectedUpdatedAt: input.expectedUpdatedAt,\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n title: updateResult.note.title,\n layout: updateResult.note.layout,\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n },\n});\n\nexport const defaultMarkdownIntentWriteService = createMarkdownIntentWriteService({\n findNoteById: (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n id: true,\n title: true,\n content: true,\n layout: true,\n updatedAt: true,\n },\n }),\n renderMarkdown: blocksToMarkdown,\n parseMarkdownToContentJson: markdownToBlocksJson,\n extractTagIds: extractTagIdsFromContentJson,\n countReferenceInlines: countReferenceInlinesFromContentJson,\n hasUnsupportedMarkdownBlocks,\n updateNote: updateNoteWithVersionGuardAndSnapshot,\n resolvePropertyPatch: resolveNotePropertiesPatchValueTypes,\n validatePropertyPatch: validateNotePropertiesPatchValues,\n updateProperties: updateNotePropertiesWithVersionGuardAndSnapshot,\n});\n\nexport const patchNoteMarkdown = async (input: PatchNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.patchNoteMarkdown(input);\n};\n\nexport const appendNoteMarkdown = async (input: AppendNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.appendNoteMarkdown(input);\n};\n\nexport const replaceNoteMarkdown = async (input: ReplaceNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.replaceNoteMarkdown(input);\n};\n\nexport const updateNoteMetadata = async (input: UpdateNoteMetadataInput & { dryRun?: boolean }) => {\n if (input.dryRun ?? true) {\n return defaultMarkdownIntentWriteService.updateNoteMetadata(input);\n }\n\n return defaultMarkdownIntentWriteService.applyNoteMetadata(input);\n};\n\nexport const calculateNoteMarkdownSha256 = (markdown: string) => {\n return calculateMarkdownSha256(markdown);\n};\n"],"mappings":"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","names":["updateResult"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/note/services/markdown-intent-write.ts"],"sourcesContent":["import models, { type NoteLayout } from '~/models.js';\nimport {\n blocksToMarkdown,\n countReferenceInlinesFromContentJson,\n extractLiteralAngleBracketTextTokens,\n extractTagIdsFromContentJson,\n hasLiteralAngleBracketTextTokenLoss,\n hasNumericTildeRangeMarkerLoss,\n hasNumericTildeRangeMarkers,\n hasUnsupportedMarkdownBlocks,\n markdownToBlocksJson,\n} from '~/modules/blocknote.js';\nimport type {\n MarkdownAppendPlanResult,\n MarkdownChangeFailure,\n MarkdownPatchOperation,\n MarkdownPatchPlanResult,\n MarkdownPatchSelector,\n MarkdownReplacePlanResult,\n} from './markdown-patch.js';\nimport {\n buildMarkdownAppendPlan,\n buildMarkdownPatchPlan,\n buildMarkdownReplacePlan,\n calculateMarkdownSha256,\n} from './markdown-patch.js';\nimport {\n InvalidNotePropertyInputError,\n type NotePropertiesByKeyPatchInput,\n type NotePropertiesPatchInput,\n resolveNotePropertiesPatchValueTypes,\n updateNotePropertiesWithVersionGuardAndSnapshot,\n validateNotePropertiesPatchValues,\n} from './properties.js';\nimport { MCP_SNAPSHOT_META } from './snapshot.js';\nimport { type GuardedNoteWriteResult, updateNoteWithVersionGuardAndSnapshot } from './write.js';\nimport {\n isInvalidNoteVersionError,\n isMissingNoteVersionError,\n isNoteVersionConflictError,\n parseNoteVersion,\n} from './write-conflict.js';\n\ninterface MarkdownIntentNoteRecord {\n id: number;\n title: string;\n content: string;\n layout: NoteLayout;\n updatedAt: Date;\n}\n\ninterface MarkdownIntentWriteDeps {\n findNoteById: (id: number) => Promise<MarkdownIntentNoteRecord | null>;\n renderMarkdown: (contentJson: string) => Promise<string>;\n parseMarkdownToContentJson: (markdown: string) => Promise<string>;\n extractTagIds: (contentJson: string) => string[];\n countReferenceInlines?: (contentJson: string) => number;\n hasUnsupportedMarkdownBlocks?: (contentJson: string) => boolean;\n updateNote: (input: {\n id: number;\n data: {\n title?: string;\n content?: string;\n layout?: NoteLayout;\n tagIds?: number[];\n };\n expectedUpdatedAt: string;\n snapshotMeta?: string;\n force?: boolean;\n }) => Promise<GuardedNoteWriteResult | null>;\n resolvePropertyPatch?: (patch: NotePropertiesByKeyPatchInput) => Promise<NotePropertiesPatchInput>;\n validatePropertyPatch?: (patch: NotePropertiesPatchInput) => Promise<NotePropertiesPatchInput>;\n updateProperties?: (input: {\n id: number;\n patch: NotePropertiesPatchInput;\n expectedUpdatedAt: string;\n noteData?: {\n title?: string;\n layout?: NoteLayout;\n };\n snapshotMeta?: string;\n }) => Promise<{\n note: {\n id: number;\n title: string;\n layout: NoteLayout;\n updatedAt: Date;\n };\n snapshot: unknown;\n } | null>;\n}\n\nexport interface MarkdownWritePolicy {\n allowNoop?: boolean;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface PatchNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n selector: MarkdownPatchSelector;\n operation: MarkdownPatchOperation;\n policy?: MarkdownWritePolicy;\n}\n\nexport interface AppendNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n insertion: string;\n placement?:\n | {\n type: 'end';\n }\n | {\n type: 'after_heading';\n heading: string;\n level?: number;\n };\n separator?: '\\n\\n' | '\\n';\n policy?: MarkdownWritePolicy;\n}\n\nexport interface ReplaceNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownWritePolicy;\n}\n\nexport interface UpdateNoteMetadataInput {\n id: number;\n expectedUpdatedAt: string;\n title?: string;\n layout?: NoteLayout;\n properties?: NotePropertiesByKeyPatchInput;\n}\n\nexport interface AppliedMarkdownWriteResult {\n status: 'applied';\n note: {\n id: string;\n updatedAt: string;\n };\n change: {\n summary: string;\n changedLineCount: number;\n changedCharCount: number;\n };\n snapshot: {\n id: string;\n label: string;\n createdAt: string;\n };\n}\n\nexport interface AppliedMetadataUpdateResult {\n status: 'applied';\n note: {\n id: string;\n title: string;\n layout: NoteLayout;\n updatedAt: string;\n };\n snapshot: {\n id: string;\n label: string;\n createdAt: string;\n };\n}\n\nexport type MarkdownIntentWriteResult =\n | AppliedMarkdownWriteResult\n | MarkdownChangeFailure\n | Extract<MarkdownPatchPlanResult, { status: 'needs_disambiguation' }>;\n\nexport type MetadataUpdateResult = AppliedMetadataUpdateResult | MarkdownChangeFailure;\n\nconst serializeNoteSnapshot = async (\n note: MarkdownIntentNoteRecord,\n renderMarkdown: (contentJson: string) => Promise<string>,\n) => {\n return {\n id: String(note.id),\n title: note.title,\n updatedAt: note.updatedAt.toISOString(),\n markdown: await renderMarkdown(note.content),\n };\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst serializeSnapshot = (snapshot: unknown) => {\n const meta = isRecord(snapshot) && isRecord(snapshot.meta) ? snapshot.meta : {};\n const label = typeof meta.label === 'string' ? meta.label : 'MCP';\n\n return {\n id: isRecord(snapshot) && snapshot.id !== undefined ? String(snapshot.id) : '',\n label,\n createdAt: isRecord(snapshot) && typeof snapshot.createdAt === 'string' ? snapshot.createdAt : '',\n };\n};\n\nconst toTagIds = (tagIds: string[]) => {\n return tagIds.map((tagId) => Number(tagId)).filter((tagId) => Number.isSafeInteger(tagId) && tagId > 0);\n};\n\nconst baselineMismatchFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message:\n 'The markdown write baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n});\n\nconst missingBaselineFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n});\n\nconst unsupportedMarkdownStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'UNSUPPORTED_MARKDOWN_STRUCTURE',\n message: 'This note contains BlockNote content that cannot be safely represented as Markdown.',\n});\n\nconst referenceStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'REFERENCE_STRUCTURE_DECREASED',\n message: 'The markdown write would reduce structured note reference links.',\n});\n\nconst markdownImportLossyFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'MARKDOWN_IMPORT_LOSSY',\n message: 'The markdown write would lose literal text during Markdown import.',\n});\n\nconst invalidPropertyInputFailure = (error: InvalidNotePropertyInputError): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'INVALID_PROPERTY_INPUT',\n message: error.message,\n});\n\nconst mapGuardedWriteError = (error: unknown): MarkdownChangeFailure | null => {\n if (isNoteVersionConflictError(error) || isInvalidNoteVersionError(error)) {\n return baselineMismatchFailure();\n }\n\n if (isMissingNoteVersionError(error)) {\n return missingBaselineFailure();\n }\n\n return null;\n};\n\nconst resolveMetadataPropertyPatch = async (\n deps: MarkdownIntentWriteDeps,\n patch: NotePropertiesByKeyPatchInput | undefined,\n): Promise<NotePropertiesPatchInput | MarkdownChangeFailure | undefined> => {\n if (patch === undefined) {\n return undefined;\n }\n\n if (!deps.resolvePropertyPatch || !deps.validatePropertyPatch) {\n throw new Error('Metadata property patch dependencies are not configured.');\n }\n\n try {\n const resolvedPatch = await deps.resolvePropertyPatch(patch);\n return deps.validatePropertyPatch(resolvedPatch);\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n return invalidPropertyInputFailure(error);\n }\n\n throw error;\n }\n};\n\nconst noteVersionMatches = (expectedUpdatedAt: string | undefined, currentUpdatedAt: Date) => {\n try {\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n return expectedTimestamp !== null && expectedTimestamp === currentUpdatedAt.getTime();\n } catch {\n return false;\n }\n};\n\nconst getUnsupportedSourceFailure = (deps: MarkdownIntentWriteDeps, note: MarkdownIntentNoteRecord) => {\n if (deps.hasUnsupportedMarkdownBlocks?.(note.content)) {\n return unsupportedMarkdownStructureFailure();\n }\n\n return null;\n};\n\nconst applyMarkdownPlan = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: Extract<MarkdownPatchPlanResult, { status: 'planned' }>;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<AppliedMarkdownWriteResult | MarkdownChangeFailure> => {\n const content = await deps.parseMarkdownToContentJson(input.plan.afterMarkdown);\n const literalAngleBracketTokens = extractLiteralAngleBracketTextTokens(input.plan.afterMarkdown);\n const shouldCheckMarkdownImportLoss =\n literalAngleBracketTokens.length > 0 || hasNumericTildeRangeMarkers(input.plan.afterMarkdown);\n\n if (shouldCheckMarkdownImportLoss) {\n const importedMarkdown = await deps.renderMarkdown(content);\n\n if (\n hasLiteralAngleBracketTextTokenLoss(input.plan.afterMarkdown, importedMarkdown) ||\n hasNumericTildeRangeMarkerLoss(input.plan.afterMarkdown, importedMarkdown)\n ) {\n return markdownImportLossyFailure();\n }\n }\n\n const beforeReferenceCount = deps.countReferenceInlines?.(input.beforeContentJson) ?? 0;\n const afterReferenceCount = deps.countReferenceInlines?.(content) ?? 0;\n\n if (input.policy?.preserveReferences === true && afterReferenceCount < beforeReferenceCount) {\n return referenceStructureFailure();\n }\n\n const tagIds = toTagIds(deps.extractTagIds(content));\n let updateResult: GuardedNoteWriteResult | null;\n\n try {\n updateResult = await deps.updateNote({\n id: input.noteId,\n data: {\n content,\n tagIds,\n },\n expectedUpdatedAt: input.expectedUpdatedAt ?? input.noteUpdatedAt,\n snapshotMeta: MCP_SNAPSHOT_META,\n ...(input.force ? { force: true } : {}),\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n change: {\n summary: input.summary,\n changedLineCount: input.plan.proposed.changedLineCount,\n changedCharCount: input.plan.proposed.changedCharCount,\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n};\n\nconst mapPlanResult = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: MarkdownPatchPlanResult | MarkdownAppendPlanResult | MarkdownReplacePlanResult;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<MarkdownIntentWriteResult> => {\n if (input.plan.status !== 'planned') {\n return input.plan;\n }\n\n return applyMarkdownPlan(deps, {\n noteId: input.noteId,\n noteUpdatedAt: input.noteUpdatedAt,\n beforeContentJson: input.beforeContentJson,\n plan: input.plan,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.summary,\n ...(input.force ? { force: true } : {}),\n });\n};\n\nexport const createMarkdownIntentWriteService = (deps: MarkdownIntentWriteDeps) => ({\n patchNoteMarkdown: async (input: PatchNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownPatchPlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n selector: input.selector,\n operation: input.operation,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n appendNoteMarkdown: async (input: AppendNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownAppendPlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n insertion: input.insertion,\n placement: input.placement,\n separator: input.separator,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n replaceNoteMarkdown: async (input: ReplaceNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownReplacePlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n replacement: input.replacement,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n updateNoteMetadata: async (input: UpdateNoteMetadataInput): Promise<MetadataUpdateResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n if (!noteVersionMatches(input.expectedUpdatedAt, note.updatedAt)) {\n return {\n status: 'failed',\n reason: input.expectedUpdatedAt ? 'BASELINE_MISMATCH' : 'MISSING_BASELINE',\n message:\n 'The metadata update baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n };\n }\n\n const nextTitle = input.title?.trim();\n const resolvedPropertyPatch = await resolveMetadataPropertyPatch(deps, input.properties);\n\n if (resolvedPropertyPatch && 'status' in resolvedPropertyPatch) {\n return resolvedPropertyPatch;\n }\n\n if (input.title !== undefined && !nextTitle) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Note title must not be empty.',\n };\n }\n\n if (\n nextTitle === note.title &&\n (input.layout === undefined || input.layout === note.layout) &&\n resolvedPropertyPatch === undefined\n ) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The metadata update would not change the note.',\n };\n }\n\n const proposed = {\n ...(nextTitle !== undefined ? { title: nextTitle } : {}),\n ...(input.layout !== undefined ? { layout: input.layout } : {}),\n ...(resolvedPropertyPatch ? { properties: resolvedPropertyPatch } : {}),\n };\n\n if (input.properties !== undefined) {\n if (!deps.updateProperties) {\n throw new Error('Metadata property write dependency is not configured.');\n }\n\n let updateResult: Awaited<ReturnType<NonNullable<MarkdownIntentWriteDeps['updateProperties']>>>;\n\n try {\n updateResult = await deps.updateProperties({\n id: input.id,\n patch: proposed.properties ?? { set: [], deleteKeys: [] },\n expectedUpdatedAt: input.expectedUpdatedAt,\n noteData: {\n ...(proposed.title !== undefined ? { title: proposed.title } : {}),\n ...(proposed.layout !== undefined ? { layout: proposed.layout } : {}),\n },\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n if (error instanceof InvalidNotePropertyInputError) {\n return invalidPropertyInputFailure(error);\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n title: updateResult.note.title,\n layout: updateResult.note.layout,\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n }\n\n let updateResult: GuardedNoteWriteResult | null;\n\n try {\n updateResult = await deps.updateNote({\n id: input.id,\n data: {\n ...(proposed.title !== undefined ? { title: proposed.title } : {}),\n ...(proposed.layout !== undefined ? { layout: proposed.layout } : {}),\n },\n expectedUpdatedAt: input.expectedUpdatedAt,\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n title: updateResult.note.title,\n layout: updateResult.note.layout,\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n },\n});\n\nexport const defaultMarkdownIntentWriteService = createMarkdownIntentWriteService({\n findNoteById: (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n id: true,\n title: true,\n content: true,\n layout: true,\n updatedAt: true,\n },\n }),\n renderMarkdown: blocksToMarkdown,\n parseMarkdownToContentJson: markdownToBlocksJson,\n extractTagIds: extractTagIdsFromContentJson,\n countReferenceInlines: countReferenceInlinesFromContentJson,\n hasUnsupportedMarkdownBlocks,\n updateNote: updateNoteWithVersionGuardAndSnapshot,\n resolvePropertyPatch: resolveNotePropertiesPatchValueTypes,\n validatePropertyPatch: validateNotePropertiesPatchValues,\n updateProperties: updateNotePropertiesWithVersionGuardAndSnapshot,\n});\n\nexport const patchNoteMarkdown = async (input: PatchNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.patchNoteMarkdown(input);\n};\n\nexport const appendNoteMarkdown = async (input: AppendNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.appendNoteMarkdown(input);\n};\n\nexport const replaceNoteMarkdown = async (input: ReplaceNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.replaceNoteMarkdown(input);\n};\n\nexport const updateNoteMetadata = async (input: UpdateNoteMetadataInput) => {\n return defaultMarkdownIntentWriteService.updateNoteMetadata(input);\n};\n\nexport const calculateNoteMarkdownSha256 = (markdown: string) => {\n return calculateMarkdownSha256(markdown);\n};\n"],"mappings":"AAAA,OAAO,YAAiC;AACxC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AASP;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP;AAAA,EACI;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,yBAAyB;AAClC,SAAsC,6CAA6C;AACnF;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AA+IP,MAAM,wBAAwB,OAC1B,MACA,mBACC;AACD,SAAO;AAAA,IACH,IAAI,OAAO,KAAK,EAAE;AAAA,IAClB,OAAO,KAAK;AAAA,IACZ,WAAW,KAAK,UAAU,YAAY;AAAA,IACtC,UAAU,MAAM,eAAe,KAAK,OAAO;AAAA,EAC/C;AACJ;AAEA,MAAM,WAAW,CAAC,UAAqD;AACnE,SAAO,OAAO,UAAU,YAAY,UAAU;AAClD;AAEA,MAAM,oBAAoB,CAAC,aAAsB;AAC7C,QAAM,OAAO,SAAS,QAAQ,KAAK,SAAS,SAAS,IAAI,IAAI,SAAS,OAAO,CAAC;AAC9E,QAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAE5D,SAAO;AAAA,IACH,IAAI,SAAS,QAAQ,KAAK,SAAS,OAAO,SAAY,OAAO,SAAS,EAAE,IAAI;AAAA,IAC5E;AAAA,IACA,WAAW,SAAS,QAAQ,KAAK,OAAO,SAAS,cAAc,WAAW,SAAS,YAAY;AAAA,EACnG;AACJ;AAEA,MAAM,WAAW,CAAC,WAAqB;AACnC,SAAO,OAAO,IAAI,CAAC,UAAU,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,OAAO,cAAc,KAAK,KAAK,QAAQ,CAAC;AAC1G;AAEA,MAAM,0BAA0B,OAA8B;AAAA,EAC1D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SACI;AACR;AAEA,MAAM,yBAAyB,OAA8B;AAAA,EACzD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,sCAAsC,OAA8B;AAAA,EACtE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,4BAA4B,OAA8B;AAAA,EAC5D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,6BAA6B,OAA8B;AAAA,EAC7D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,8BAA8B,CAAC,WAAiE;AAAA,EAClG,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS,MAAM;AACnB;AAEA,MAAM,uBAAuB,CAAC,UAAiD;AAC3E,MAAI,2BAA2B,KAAK,KAAK,0BAA0B,KAAK,GAAG;AACvE,WAAO,wBAAwB;AAAA,EACnC;AAEA,MAAI,0BAA0B,KAAK,GAAG;AAClC,WAAO,uBAAuB;AAAA,EAClC;AAEA,SAAO;AACX;AAEA,MAAM,+BAA+B,OACjC,MACA,UACwE;AACxE,MAAI,UAAU,QAAW;AACrB,WAAO;AAAA,EACX;AAEA,MAAI,CAAC,KAAK,wBAAwB,CAAC,KAAK,uBAAuB;AAC3D,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC9E;AAEA,MAAI;AACA,UAAM,gBAAgB,MAAM,KAAK,qBAAqB,KAAK;AAC3D,WAAO,KAAK,sBAAsB,aAAa;AAAA,EACnD,SAAS,OAAO;AACZ,QAAI,iBAAiB,+BAA+B;AAChD,aAAO,4BAA4B,KAAK;AAAA,IAC5C;AAEA,UAAM;AAAA,EACV;AACJ;AAEA,MAAM,qBAAqB,CAAC,mBAAuC,qBAA2B;AAC1F,MAAI;AACA,UAAM,oBAAoB,iBAAiB,iBAAiB;AAE5D,WAAO,sBAAsB,QAAQ,sBAAsB,iBAAiB,QAAQ;AAAA,EACxF,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,MAAM,8BAA8B,CAAC,MAA+B,SAAmC;AACnG,MAAI,KAAK,+BAA+B,KAAK,OAAO,GAAG;AACnD,WAAO,oCAAoC;AAAA,EAC/C;AAEA,SAAO;AACX;AAEA,MAAM,oBAAoB,OACtB,MACA,UAU8D;AAC9D,QAAM,UAAU,MAAM,KAAK,2BAA2B,MAAM,KAAK,aAAa;AAC9E,QAAM,4BAA4B,qCAAqC,MAAM,KAAK,aAAa;AAC/F,QAAM,gCACF,0BAA0B,SAAS,KAAK,4BAA4B,MAAM,KAAK,aAAa;AAEhG,MAAI,+BAA+B;AAC/B,UAAM,mBAAmB,MAAM,KAAK,eAAe,OAAO;AAE1D,QACI,oCAAoC,MAAM,KAAK,eAAe,gBAAgB,KAC9E,+BAA+B,MAAM,KAAK,eAAe,gBAAgB,GAC3E;AACE,aAAO,2BAA2B;AAAA,IACtC;AAAA,EACJ;AAEA,QAAM,uBAAuB,KAAK,wBAAwB,MAAM,iBAAiB,KAAK;AACtF,QAAM,sBAAsB,KAAK,wBAAwB,OAAO,KAAK;AAErE,MAAI,MAAM,QAAQ,uBAAuB,QAAQ,sBAAsB,sBAAsB;AACzF,WAAO,0BAA0B;AAAA,EACrC;AAEA,QAAM,SAAS,SAAS,KAAK,cAAc,OAAO,CAAC;AACnD,MAAI;AAEJ,MAAI;AACA,mBAAe,MAAM,KAAK,WAAW;AAAA,MACjC,IAAI,MAAM;AAAA,MACV,MAAM;AAAA,QACF;AAAA,QACA;AAAA,MACJ;AAAA,MACA,mBAAmB,MAAM,qBAAqB,MAAM;AAAA,MACpD,cAAc;AAAA,MACd,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,IACzC,CAAC;AAAA,EACL,SAAS,OAAO;AACZ,UAAM,cAAc,qBAAqB,KAAK;AAE9C,QAAI,aAAa;AACb,aAAO;AAAA,IACX;AAEA,UAAM;AAAA,EACV;AAEA,MAAI,CAAC,cAAc;AACf,WAAO;AAAA,MACH,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SAAS;AAAA,IACb;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,QAAQ;AAAA,IACR,MAAM;AAAA,MACF,IAAI,OAAO,aAAa,KAAK,EAAE;AAAA,MAC/B,WAAW,aAAa,KAAK,UAAU,YAAY;AAAA,IACvD;AAAA,IACA,QAAQ;AAAA,MACJ,SAAS,MAAM;AAAA,MACf,kBAAkB,MAAM,KAAK,SAAS;AAAA,MACtC,kBAAkB,MAAM,KAAK,SAAS;AAAA,IAC1C;AAAA,IACA,UAAU,kBAAkB,aAAa,QAAQ;AAAA,EACrD;AACJ;AAEA,MAAM,gBAAgB,OAClB,MACA,UAUqC;AACrC,MAAI,MAAM,KAAK,WAAW,WAAW;AACjC,WAAO,MAAM;AAAA,EACjB;AAEA,SAAO,kBAAkB,MAAM;AAAA,IAC3B,QAAQ,MAAM;AAAA,IACd,eAAe,MAAM;AAAA,IACrB,mBAAmB,MAAM;AAAA,IACzB,MAAM,MAAM;AAAA,IACZ,mBAAmB,MAAM;AAAA,IACzB,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM;AAAA,IACf,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,EACzC,CAAC;AACL;AAEO,MAAM,mCAAmC,CAAC,UAAmC;AAAA,EAChF,mBAAmB,OAAO,UAAsE;AAC5F,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,qBAAqB,4BAA4B,MAAM,IAAI;AAEjE,QAAI,oBAAoB;AACpB,aAAO;AAAA,IACX;AAEA,UAAM,eAAe,MAAM,sBAAsB,MAAM,KAAK,cAAc;AAC1E,UAAM,OAAO,uBAAuB;AAAA,MAChC,MAAM;AAAA,MACN,mBAAmB,MAAM;AAAA,MACzB,oBAAoB,MAAM;AAAA,MAC1B,QAAQ,MAAM;AAAA,MACd,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MACjB,QAAQ,MAAM;AAAA,IAClB,CAAC;AAED,WAAO,cAAc,MAAM;AAAA,MACvB,QAAQ,MAAM;AAAA,MACd,eAAe,aAAa;AAAA,MAC5B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA,mBAAmB,MAAM;AAAA,MACzB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACnB,CAAC;AAAA,EACL;AAAA,EAEA,oBAAoB,OAAO,UAAuE;AAC9F,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,qBAAqB,4BAA4B,MAAM,IAAI;AAEjE,QAAI,oBAAoB;AACpB,aAAO;AAAA,IACX;AAEA,UAAM,eAAe,MAAM,sBAAsB,MAAM,KAAK,cAAc;AAC1E,UAAM,OAAO,wBAAwB;AAAA,MACjC,MAAM;AAAA,MACN,mBAAmB,MAAM;AAAA,MACzB,oBAAoB,MAAM;AAAA,MAC1B,QAAQ,MAAM;AAAA,MACd,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,QAAQ,MAAM;AAAA,IAClB,CAAC;AAED,WAAO,cAAc,MAAM;AAAA,MACvB,QAAQ,MAAM;AAAA,MACd,eAAe,aAAa;AAAA,MAC5B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA,mBAAmB,MAAM;AAAA,MACzB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACnB,CAAC;AAAA,EACL;AAAA,EAEA,qBAAqB,OAAO,UAAwE;AAChG,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,qBAAqB,4BAA4B,MAAM,IAAI;AAEjE,QAAI,oBAAoB;AACpB,aAAO;AAAA,IACX;AAEA,UAAM,eAAe,MAAM,sBAAsB,MAAM,KAAK,cAAc;AAC1E,UAAM,OAAO,yBAAyB;AAAA,MAClC,MAAM;AAAA,MACN,mBAAmB,MAAM;AAAA,MACzB,oBAAoB,MAAM;AAAA,MAC1B,QAAQ,MAAM;AAAA,MACd,aAAa,MAAM;AAAA,MACnB,QAAQ,MAAM;AAAA,IAClB,CAAC;AAED,WAAO,cAAc,MAAM;AAAA,MACvB,QAAQ,MAAM;AAAA,MACd,eAAe,aAAa;AAAA,MAC5B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA,mBAAmB,MAAM;AAAA,MACzB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACnB,CAAC;AAAA,EACL;AAAA,EAEA,oBAAoB,OAAO,UAAkE;AACzF,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,QAAI,CAAC,mBAAmB,MAAM,mBAAmB,KAAK,SAAS,GAAG;AAC9D,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ,MAAM,oBAAoB,sBAAsB;AAAA,QACxD,SACI;AAAA,MACR;AAAA,IACJ;AAEA,UAAM,YAAY,MAAM,OAAO,KAAK;AACpC,UAAM,wBAAwB,MAAM,6BAA6B,MAAM,MAAM,UAAU;AAEvF,QAAI,yBAAyB,YAAY,uBAAuB;AAC5D,aAAO;AAAA,IACX;AAEA,QAAI,MAAM,UAAU,UAAa,CAAC,WAAW;AACzC,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,QACI,cAAc,KAAK,UAClB,MAAM,WAAW,UAAa,MAAM,WAAW,KAAK,WACrD,0BAA0B,QAC5B;AACE,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,WAAW;AAAA,MACb,GAAI,cAAc,SAAY,EAAE,OAAO,UAAU,IAAI,CAAC;AAAA,MACtD,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,MAC7D,GAAI,wBAAwB,EAAE,YAAY,sBAAsB,IAAI,CAAC;AAAA,IACzE;AAEA,QAAI,MAAM,eAAe,QAAW;AAChC,UAAI,CAAC,KAAK,kBAAkB;AACxB,cAAM,IAAI,MAAM,uDAAuD;AAAA,MAC3E;AAEA,UAAIA;AAEJ,UAAI;AACA,QAAAA,gBAAe,MAAM,KAAK,iBAAiB;AAAA,UACvC,IAAI,MAAM;AAAA,UACV,OAAO,SAAS,cAAc,EAAE,KAAK,CAAC,GAAG,YAAY,CAAC,EAAE;AAAA,UACxD,mBAAmB,MAAM;AAAA,UACzB,UAAU;AAAA,YACN,GAAI,SAAS,UAAU,SAAY,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;AAAA,YAChE,GAAI,SAAS,WAAW,SAAY,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;AAAA,UACvE;AAAA,UACA,cAAc;AAAA,QAClB,CAAC;AAAA,MACL,SAAS,OAAO;AACZ,cAAM,cAAc,qBAAqB,KAAK;AAE9C,YAAI,aAAa;AACb,iBAAO;AAAA,QACX;AAEA,YAAI,iBAAiB,+BAA+B;AAChD,iBAAO,4BAA4B,KAAK;AAAA,QAC5C;AAEA,cAAM;AAAA,MACV;AAEA,UAAI,CAACA,eAAc;AACf,eAAO;AAAA,UACH,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,MAAM;AAAA,UACF,IAAI,OAAOA,cAAa,KAAK,EAAE;AAAA,UAC/B,OAAOA,cAAa,KAAK;AAAA,UACzB,QAAQA,cAAa,KAAK;AAAA,UAC1B,WAAWA,cAAa,KAAK,UAAU,YAAY;AAAA,QACvD;AAAA,QACA,UAAU,kBAAkBA,cAAa,QAAQ;AAAA,MACrD;AAAA,IACJ;AAEA,QAAI;AAEJ,QAAI;AACA,qBAAe,MAAM,KAAK,WAAW;AAAA,QACjC,IAAI,MAAM;AAAA,QACV,MAAM;AAAA,UACF,GAAI,SAAS,UAAU,SAAY,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;AAAA,UAChE,GAAI,SAAS,WAAW,SAAY,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;AAAA,QACvE;AAAA,QACA,mBAAmB,MAAM;AAAA,QACzB,cAAc;AAAA,MAClB,CAAC;AAAA,IACL,SAAS,OAAO;AACZ,YAAM,cAAc,qBAAqB,KAAK;AAE9C,UAAI,aAAa;AACb,eAAO;AAAA,MACX;AAEA,YAAM;AAAA,IACV;AAEA,QAAI,CAAC,cAAc;AACf,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,WAAO;AAAA,MACH,QAAQ;AAAA,MACR,MAAM;AAAA,QACF,IAAI,OAAO,aAAa,KAAK,EAAE;AAAA,QAC/B,OAAO,aAAa,KAAK;AAAA,QACzB,QAAQ,aAAa,KAAK;AAAA,QAC1B,WAAW,aAAa,KAAK,UAAU,YAAY;AAAA,MACvD;AAAA,MACA,UAAU,kBAAkB,aAAa,QAAQ;AAAA,IACrD;AAAA,EACJ;AACJ;AAEO,MAAM,oCAAoC,iCAAiC;AAAA,EAC9E,cAAc,CAAC,OACX,OAAO,KAAK,WAAW;AAAA,IACnB,OAAO,EAAE,GAAG;AAAA,IACZ,QAAQ;AAAA,MACJ,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,WAAW;AAAA,IACf;AAAA,EACJ,CAAC;AAAA,EACL,gBAAgB;AAAA,EAChB,4BAA4B;AAAA,EAC5B,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB;AAAA,EACA,YAAY;AAAA,EACZ,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,kBAAkB;AACtB,CAAC;AAEM,MAAM,oBAAoB,OAAO,UAAkC;AACtE,SAAO,kCAAkC,kBAAkB,KAAK;AACpE;AAEO,MAAM,qBAAqB,OAAO,UAAmC;AACxE,SAAO,kCAAkC,mBAAmB,KAAK;AACrE;AAEO,MAAM,sBAAsB,OAAO,UAAoC;AAC1E,SAAO,kCAAkC,oBAAoB,KAAK;AACtE;AAEO,MAAM,qBAAqB,OAAO,UAAmC;AACxE,SAAO,kCAAkC,mBAAmB,KAAK;AACrE;AAEO,MAAM,8BAA8B,CAAC,aAAqB;AAC7D,SAAO,wBAAwB,QAAQ;AAC3C;","names":["updateResult"]}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import crypto from "crypto";
|
|
2
2
|
const HASH_ALGORITHM = "sha256";
|
|
3
3
|
const EXCERPT_CONTEXT_CHARS = 80;
|
|
4
|
-
const DIFF_CONTEXT_LINES = 3;
|
|
5
4
|
const calculateMarkdownSha256 = (value) => {
|
|
6
5
|
return crypto.createHash(HASH_ALGORITHM).update(value, "utf8").digest("hex");
|
|
7
6
|
};
|
|
@@ -82,41 +81,6 @@ const findSharedLineEdges = (beforeLines, afterLines) => {
|
|
|
82
81
|
suffix
|
|
83
82
|
};
|
|
84
83
|
};
|
|
85
|
-
const formatDiffRange = (lineIndex, lineCount) => {
|
|
86
|
-
const lineNumber = lineIndex + 1;
|
|
87
|
-
return lineCount === 1 ? String(lineNumber) : `${lineNumber},${lineCount}`;
|
|
88
|
-
};
|
|
89
|
-
const createUnifiedMarkdownDiff = (beforeMarkdown, afterMarkdown, contextLines = DIFF_CONTEXT_LINES) => {
|
|
90
|
-
const beforeLines = splitLines(beforeMarkdown);
|
|
91
|
-
const afterLines = splitLines(afterMarkdown);
|
|
92
|
-
const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);
|
|
93
|
-
const changedBeforeStart = prefix;
|
|
94
|
-
const changedBeforeEnd = beforeLines.length - suffix;
|
|
95
|
-
const changedAfterStart = prefix;
|
|
96
|
-
const changedAfterEnd = afterLines.length - suffix;
|
|
97
|
-
const displayBeforeStart = contextLines === null ? 0 : Math.max(0, changedBeforeStart - contextLines);
|
|
98
|
-
const displayBeforeEnd = contextLines === null ? beforeLines.length : Math.min(beforeLines.length, changedBeforeEnd + contextLines);
|
|
99
|
-
const displayAfterStart = contextLines === null ? 0 : Math.max(0, changedAfterStart - contextLines);
|
|
100
|
-
const displayAfterEnd = contextLines === null ? afterLines.length : Math.min(afterLines.length, changedAfterEnd + contextLines);
|
|
101
|
-
const diffLines = [
|
|
102
|
-
"--- before.md",
|
|
103
|
-
"+++ after.md",
|
|
104
|
-
`@@ -${formatDiffRange(displayBeforeStart, displayBeforeEnd - displayBeforeStart)} +${formatDiffRange(displayAfterStart, displayAfterEnd - displayAfterStart)} @@`
|
|
105
|
-
];
|
|
106
|
-
for (let index = displayBeforeStart; index < changedBeforeStart; index += 1) {
|
|
107
|
-
diffLines.push(` ${beforeLines[index]}`);
|
|
108
|
-
}
|
|
109
|
-
for (let index = changedBeforeStart; index < changedBeforeEnd; index += 1) {
|
|
110
|
-
diffLines.push(`-${beforeLines[index]}`);
|
|
111
|
-
}
|
|
112
|
-
for (let index = changedAfterStart; index < changedAfterEnd; index += 1) {
|
|
113
|
-
diffLines.push(`+${afterLines[index]}`);
|
|
114
|
-
}
|
|
115
|
-
for (let index = changedBeforeEnd; index < displayBeforeEnd; index += 1) {
|
|
116
|
-
diffLines.push(` ${beforeLines[index]}`);
|
|
117
|
-
}
|
|
118
|
-
return diffLines.join("\n");
|
|
119
|
-
};
|
|
120
84
|
const countChangedLines = (beforeMarkdown, afterMarkdown) => {
|
|
121
85
|
const beforeLines = splitLines(beforeMarkdown);
|
|
122
86
|
const afterLines = splitLines(afterMarkdown);
|
|
@@ -235,49 +199,11 @@ const validateBaseline = (note, expectedUpdatedAt, baseMarkdownSha256) => {
|
|
|
235
199
|
}
|
|
236
200
|
return null;
|
|
237
201
|
};
|
|
238
|
-
const validateChangeLimits = (beforeMarkdown, afterMarkdown, changedCharCount, policy) => {
|
|
239
|
-
const changedLineCount = countChangedLines(beforeMarkdown, afterMarkdown);
|
|
240
|
-
const exceededLines = policy?.maxChangedLines !== void 0 && changedLineCount > policy.maxChangedLines;
|
|
241
|
-
const exceededChars = policy?.maxChangedChars !== void 0 && changedCharCount > policy.maxChangedChars;
|
|
242
|
-
if (exceededLines || exceededChars) {
|
|
243
|
-
return {
|
|
244
|
-
status: "failed",
|
|
245
|
-
reason: "CHANGE_LIMIT_EXCEEDED",
|
|
246
|
-
message: "The markdown write exceeds the configured change limit. Increase maxChangedLines/maxChangedChars to at least the requiredMinimum values or omit them for this write.",
|
|
247
|
-
details: {
|
|
248
|
-
configured: {
|
|
249
|
-
...policy?.maxChangedLines !== void 0 ? { maxChangedLines: policy.maxChangedLines } : {},
|
|
250
|
-
...policy?.maxChangedChars !== void 0 ? { maxChangedChars: policy.maxChangedChars } : {}
|
|
251
|
-
},
|
|
252
|
-
requiredMinimum: {
|
|
253
|
-
maxChangedLines: changedLineCount,
|
|
254
|
-
maxChangedChars: changedCharCount
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
return null;
|
|
260
|
-
};
|
|
261
|
-
const buildDiffPreview = (diff, policy) => {
|
|
262
|
-
const maxChars = policy?.diffPreviewMaxChars;
|
|
263
|
-
if (maxChars === void 0 || diff.length <= maxChars) {
|
|
264
|
-
return {
|
|
265
|
-
diff,
|
|
266
|
-
...maxChars !== void 0 ? { diffCharCount: diff.length } : {}
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
return {
|
|
270
|
-
diff: diff.slice(0, maxChars),
|
|
271
|
-
diffTruncated: true,
|
|
272
|
-
diffCharCount: diff.length
|
|
273
|
-
};
|
|
274
|
-
};
|
|
275
202
|
const createChangePlan = ({
|
|
276
203
|
note,
|
|
277
204
|
afterMarkdown,
|
|
278
205
|
changedCharCount,
|
|
279
206
|
policy,
|
|
280
|
-
diffContextLines = DIFF_CONTEXT_LINES,
|
|
281
207
|
match,
|
|
282
208
|
placement
|
|
283
209
|
}) => {
|
|
@@ -292,16 +218,8 @@ const createChangePlan = ({
|
|
|
292
218
|
if (preservationFailure) {
|
|
293
219
|
return preservationFailure;
|
|
294
220
|
}
|
|
295
|
-
const limitFailure = validateChangeLimits(note.markdown, afterMarkdown, changedCharCount, policy);
|
|
296
|
-
if (limitFailure) {
|
|
297
|
-
return limitFailure;
|
|
298
|
-
}
|
|
299
|
-
const diffPreview = buildDiffPreview(
|
|
300
|
-
createUnifiedMarkdownDiff(note.markdown, afterMarkdown, diffContextLines),
|
|
301
|
-
policy
|
|
302
|
-
);
|
|
303
221
|
return {
|
|
304
|
-
status: "
|
|
222
|
+
status: "planned",
|
|
305
223
|
note: {
|
|
306
224
|
id: note.id,
|
|
307
225
|
title: note.title,
|
|
@@ -313,8 +231,7 @@ const createChangePlan = ({
|
|
|
313
231
|
changedLineCount: countChangedLines(note.markdown, afterMarkdown),
|
|
314
232
|
changedCharCount,
|
|
315
233
|
beforeMarkdownSha256: calculateMarkdownSha256(note.markdown),
|
|
316
|
-
afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown)
|
|
317
|
-
...diffPreview
|
|
234
|
+
afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown)
|
|
318
235
|
},
|
|
319
236
|
warnings: collectWarnings(note.markdown, afterMarkdown, policy),
|
|
320
237
|
afterMarkdown
|
|
@@ -581,51 +498,15 @@ const buildMarkdownReplacePlan = (input) => {
|
|
|
581
498
|
note: input.note,
|
|
582
499
|
afterMarkdown: input.replacement,
|
|
583
500
|
changedCharCount: input.note.markdown.length + input.replacement.length,
|
|
584
|
-
policy: input.policy
|
|
585
|
-
diffContextLines: null
|
|
501
|
+
policy: input.policy
|
|
586
502
|
});
|
|
587
503
|
};
|
|
588
|
-
const toPreviewResult = (plan) => {
|
|
589
|
-
return {
|
|
590
|
-
status: "dry_run",
|
|
591
|
-
note: plan.note,
|
|
592
|
-
...plan.match ? { match: plan.match } : {},
|
|
593
|
-
...plan.placement ? { placement: plan.placement } : {},
|
|
594
|
-
proposed: plan.proposed,
|
|
595
|
-
warnings: plan.warnings
|
|
596
|
-
};
|
|
597
|
-
};
|
|
598
|
-
const previewMarkdownPatch = (input) => {
|
|
599
|
-
const plan = buildMarkdownPatchPlan(input);
|
|
600
|
-
if (plan.status !== "dry_run") {
|
|
601
|
-
return plan;
|
|
602
|
-
}
|
|
603
|
-
return toPreviewResult(plan);
|
|
604
|
-
};
|
|
605
|
-
const previewMarkdownAppend = (input) => {
|
|
606
|
-
const plan = buildMarkdownAppendPlan(input);
|
|
607
|
-
if (plan.status !== "dry_run") {
|
|
608
|
-
return plan;
|
|
609
|
-
}
|
|
610
|
-
return toPreviewResult(plan);
|
|
611
|
-
};
|
|
612
|
-
const previewMarkdownReplace = (input) => {
|
|
613
|
-
const plan = buildMarkdownReplacePlan(input);
|
|
614
|
-
if (plan.status !== "dry_run") {
|
|
615
|
-
return plan;
|
|
616
|
-
}
|
|
617
|
-
return toPreviewResult(plan);
|
|
618
|
-
};
|
|
619
504
|
export {
|
|
620
505
|
buildMarkdownAppendPlan,
|
|
621
506
|
buildMarkdownPatchPlan,
|
|
622
507
|
buildMarkdownReplacePlan,
|
|
623
508
|
calculateMarkdownSha256,
|
|
624
509
|
countExplicitTagTokens,
|
|
625
|
-
countMarkdownReferenceTokens
|
|
626
|
-
createUnifiedMarkdownDiff,
|
|
627
|
-
previewMarkdownAppend,
|
|
628
|
-
previewMarkdownPatch,
|
|
629
|
-
previewMarkdownReplace
|
|
510
|
+
countMarkdownReferenceTokens
|
|
630
511
|
};
|
|
631
512
|
//# sourceMappingURL=markdown-patch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/markdown-patch.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst HASH_ALGORITHM = 'sha256';\nconst EXCERPT_CONTEXT_CHARS = 80;\nconst DIFF_CONTEXT_LINES = 3;\n\nexport interface MarkdownPatchNoteSnapshot {\n id: string;\n title: string;\n updatedAt: string;\n markdown: string;\n}\n\nexport interface ExactTextMarkdownPatchSelector {\n type: 'exact_text';\n text: string;\n before?: string;\n after?: string;\n}\n\nexport interface MatchCandidateMarkdownPatchSelector {\n type: 'match_candidate';\n text: string;\n matchIndex: number;\n lineStart: number;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport type MarkdownPatchSelector = ExactTextMarkdownPatchSelector | MatchCandidateMarkdownPatchSelector;\n\nexport interface ReplaceMarkdownPatchOperation {\n type: 'replace';\n replacement: string;\n}\n\nexport interface InsertBeforeMarkdownPatchOperation {\n type: 'insert_before';\n insertion: string;\n}\n\nexport interface InsertAfterMarkdownPatchOperation {\n type: 'insert_after';\n insertion: string;\n}\n\nexport type MarkdownPatchOperation =\n | ReplaceMarkdownPatchOperation\n | InsertBeforeMarkdownPatchOperation\n | InsertAfterMarkdownPatchOperation;\n\nexport interface MarkdownChangePolicy {\n allowNoop?: boolean;\n maxChangedChars?: number;\n maxChangedLines?: number;\n diffPreviewMaxChars?: number;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface PreviewMarkdownPatchInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n selector: MarkdownPatchSelector;\n operation: MarkdownPatchOperation;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownAppendPlacementEnd {\n type: 'end';\n}\n\nexport interface MarkdownAppendPlacementAfterHeading {\n type: 'after_heading';\n heading: string;\n level?: number;\n}\n\nexport type MarkdownAppendPlacement = MarkdownAppendPlacementEnd | MarkdownAppendPlacementAfterHeading;\n\nexport interface PreviewMarkdownAppendInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n insertion: string;\n placement?: MarkdownAppendPlacement;\n separator?: '\\n\\n' | '\\n';\n policy?: MarkdownChangePolicy;\n}\n\nexport interface PreviewMarkdownReplaceInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownPatchMatch {\n matchIndex: number;\n lineStart: number;\n lineEnd: number;\n text: string;\n beforeExcerpt?: string;\n afterExcerpt?: string;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport interface MarkdownChangeDryRun {\n status: 'dry_run';\n note: {\n id: string;\n title: string;\n updatedAt: string;\n };\n match?: {\n count: 1;\n lineStart: number;\n lineEnd: number;\n selectorType: string;\n matchedTextSha256: string;\n surroundingHash: string;\n };\n placement?: MarkdownAppendPlacement;\n proposed: {\n changedLineCount: number;\n changedCharCount: number;\n beforeMarkdownSha256: string;\n afterMarkdownSha256: string;\n diff: string;\n diffTruncated?: boolean;\n diffCharCount?: number;\n };\n warnings: string[];\n}\n\nexport interface MarkdownChangePlan extends MarkdownChangeDryRun {\n afterMarkdown: string;\n}\n\nexport interface MarkdownPatchNeedsDisambiguation {\n status: 'needs_disambiguation';\n reason: 'TARGET_AMBIGUOUS';\n matches: MarkdownPatchMatch[];\n}\n\nexport interface MarkdownChangeFailure {\n status: 'failed';\n reason:\n | 'ANCHOR_MISMATCH'\n | 'BASELINE_MISMATCH'\n | 'CANDIDATE_MISMATCH'\n | 'CHANGE_LIMIT_EXCEEDED'\n | 'EMPTY_INSERTION'\n | 'EMPTY_REPLACEMENT'\n | 'EMPTY_TARGET'\n | 'HEADING_AMBIGUOUS'\n | 'HEADING_NOT_FOUND'\n | 'INVALID_HEADING_LEVEL'\n | 'INVALID_PROPERTY_INPUT'\n | 'MARKDOWN_IMPORT_LOSSY'\n | 'MISSING_BASELINE'\n | 'NOOP'\n | 'REFERENCE_STRUCTURE_DECREASED'\n | 'REFERENCE_TOKEN_DECREASED'\n | 'TAG_TOKEN_DECREASED'\n | 'TARGET_NOT_FOUND'\n | 'UNSUPPORTED_MARKDOWN_STRUCTURE';\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type MarkdownPatchPreviewResult =\n | MarkdownChangeDryRun\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure;\n\nexport type MarkdownPatchPlanResult = MarkdownChangePlan | MarkdownPatchNeedsDisambiguation | MarkdownChangeFailure;\nexport type MarkdownAppendPlanResult = MarkdownChangePlan | MarkdownChangeFailure;\nexport type MarkdownReplacePlanResult = MarkdownChangePlan | MarkdownChangeFailure;\n\ninterface RawTextMatch {\n matchIndex: number;\n start: number;\n end: number;\n text: string;\n}\n\ninterface HeadingMatch {\n heading: string;\n level: number;\n lineIndex: number;\n start: number;\n nextSectionStart: number;\n}\n\nexport const calculateMarkdownSha256 = (value: string) => {\n return crypto.createHash(HASH_ALGORITHM).update(value, 'utf8').digest('hex');\n};\n\nconst countNewlines = (value: string) => {\n let count = 0;\n\n for (const character of value) {\n if (character === '\\n') {\n count += 1;\n }\n }\n\n return count;\n};\n\nconst getLineRange = (markdown: string, match: RawTextMatch) => {\n const lineStart = countNewlines(markdown.slice(0, match.start)) + 1;\n const lineEnd = lineStart + countNewlines(match.text);\n\n return {\n lineStart,\n lineEnd,\n };\n};\n\nconst buildExcerpt = (markdown: string, start: number, end: number) => {\n const excerpt = markdown.slice(start, end).trim();\n\n return excerpt.length > 0 ? excerpt : undefined;\n};\n\nconst toPatchMatch = (markdown: string, match: RawTextMatch, totalMatches: number): MarkdownPatchMatch => {\n const { lineStart, lineEnd } = getLineRange(markdown, match);\n const beforeStart = Math.max(0, match.start - EXCERPT_CONTEXT_CHARS);\n const afterEnd = Math.min(markdown.length, match.end + EXCERPT_CONTEXT_CHARS);\n const beforeText = markdown.slice(beforeStart, match.start);\n const afterText = markdown.slice(match.end, afterEnd);\n\n return {\n matchIndex: match.matchIndex,\n lineStart,\n lineEnd,\n text: match.text,\n beforeExcerpt: buildExcerpt(markdown, beforeStart, match.start),\n afterExcerpt: buildExcerpt(markdown, match.end, afterEnd),\n matchSha256: calculateMarkdownSha256(match.text),\n surroundingHash: calculateMarkdownSha256(JSON.stringify({ beforeText, targetText: match.text, afterText })),\n ...(match.matchIndex === 0 && totalMatches > 1 ? { positionHint: 'first' } : {}),\n ...(match.matchIndex === totalMatches - 1 && totalMatches > 1 ? { positionHint: 'last' } : {}),\n };\n};\n\nconst findExactTextMatches = (markdown: string, text: string) => {\n const matches: RawTextMatch[] = [];\n let searchStart = 0;\n\n while (searchStart <= markdown.length) {\n const start = markdown.indexOf(text, searchStart);\n\n if (start === -1) {\n break;\n }\n\n matches.push({\n matchIndex: matches.length,\n start,\n end: start + text.length,\n text,\n });\n searchStart = start + text.length;\n }\n\n return matches;\n};\n\nconst splitLines = (value: string) => {\n return value.split('\\n');\n};\n\nconst findSharedLineEdges = (beforeLines: string[], afterLines: string[]) => {\n let prefix = 0;\n const maxPrefix = Math.min(beforeLines.length, afterLines.length);\n\n while (prefix < maxPrefix && beforeLines[prefix] === afterLines[prefix]) {\n prefix += 1;\n }\n\n let suffix = 0;\n const maxSuffix = Math.min(beforeLines.length - prefix, afterLines.length - prefix);\n\n while (\n suffix < maxSuffix &&\n beforeLines[beforeLines.length - 1 - suffix] === afterLines[afterLines.length - 1 - suffix]\n ) {\n suffix += 1;\n }\n\n return {\n prefix,\n suffix,\n };\n};\n\nconst formatDiffRange = (lineIndex: number, lineCount: number) => {\n const lineNumber = lineIndex + 1;\n\n return lineCount === 1 ? String(lineNumber) : `${lineNumber},${lineCount}`;\n};\n\nexport const createUnifiedMarkdownDiff = (\n beforeMarkdown: string,\n afterMarkdown: string,\n contextLines: number | null = DIFF_CONTEXT_LINES,\n) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const changedBeforeStart = prefix;\n const changedBeforeEnd = beforeLines.length - suffix;\n const changedAfterStart = prefix;\n const changedAfterEnd = afterLines.length - suffix;\n const displayBeforeStart = contextLines === null ? 0 : Math.max(0, changedBeforeStart - contextLines);\n const displayBeforeEnd =\n contextLines === null ? beforeLines.length : Math.min(beforeLines.length, changedBeforeEnd + contextLines);\n const displayAfterStart = contextLines === null ? 0 : Math.max(0, changedAfterStart - contextLines);\n const displayAfterEnd =\n contextLines === null ? afterLines.length : Math.min(afterLines.length, changedAfterEnd + contextLines);\n const diffLines = [\n '--- before.md',\n '+++ after.md',\n `@@ -${formatDiffRange(displayBeforeStart, displayBeforeEnd - displayBeforeStart)} +${formatDiffRange(displayAfterStart, displayAfterEnd - displayAfterStart)} @@`,\n ];\n\n for (let index = displayBeforeStart; index < changedBeforeStart; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n for (let index = changedBeforeStart; index < changedBeforeEnd; index += 1) {\n diffLines.push(`-${beforeLines[index]}`);\n }\n\n for (let index = changedAfterStart; index < changedAfterEnd; index += 1) {\n diffLines.push(`+${afterLines[index]}`);\n }\n\n for (let index = changedBeforeEnd; index < displayBeforeEnd; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n return diffLines.join('\\n');\n};\n\nconst countChangedLines = (beforeMarkdown: string, afterMarkdown: string) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const removedLineCount = beforeLines.length - prefix - suffix;\n const addedLineCount = afterLines.length - prefix - suffix;\n\n return Math.max(removedLineCount, addedLineCount);\n};\n\nconst isMarkdownTokenWhitespace = (character: string) => /\\s/.test(character);\n\nexport const countExplicitTagTokens = (value: string) => {\n let count = 0;\n\n for (let index = 0; index < value.length; index += 1) {\n if (value[index] !== '[' || (value[index + 1] !== '@' && value[index + 1] !== '#')) {\n continue;\n }\n\n let cursor = index + 2;\n let isValid = cursor < value.length;\n\n while (cursor < value.length && value[cursor] !== ']') {\n if (value[cursor] === '[' || isMarkdownTokenWhitespace(value[cursor])) {\n isValid = false;\n break;\n }\n\n cursor += 1;\n }\n\n if (isValid && cursor < value.length && cursor > index + 2) {\n count += 1;\n index = cursor;\n continue;\n }\n\n if (!isValid) {\n index = Math.max(index, cursor - 1);\n }\n }\n\n return count;\n};\n\nexport const countMarkdownReferenceTokens = (value: string) => {\n let count = 0;\n let cursor = 0;\n\n while (cursor < value.length) {\n const start = value.indexOf('[[', cursor);\n\n if (start === -1) {\n break;\n }\n\n const end = value.indexOf(']]', start + 2);\n\n if (end === -1) {\n break;\n }\n\n if (end > start + 2) {\n count += 1;\n }\n\n cursor = end + 2;\n }\n\n return count;\n};\n\nconst parseVersionTimestamp = (value: string) => {\n const timestamp = /^\\d+$/.test(value) ? Number(value) : Date.parse(value);\n\n return Number.isFinite(timestamp) ? timestamp : null;\n};\n\nconst noteVersionsMatch = (expectedUpdatedAt: string, currentUpdatedAt: string) => {\n const expectedTimestamp = parseVersionTimestamp(expectedUpdatedAt);\n const currentTimestamp = parseVersionTimestamp(currentUpdatedAt);\n\n return expectedTimestamp !== null && currentTimestamp !== null && expectedTimestamp === currentTimestamp;\n};\n\nconst getPreservationFailure = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n\n if (policy?.preserveTags === true && afterTagCount < beforeTagCount) {\n return {\n status: 'failed',\n reason: 'TAG_TOKEN_DECREASED',\n message: 'The patch would reduce explicit tag tokens.',\n };\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n\n if (policy?.preserveReferences === true && afterReferenceCount < beforeReferenceCount) {\n return {\n status: 'failed',\n reason: 'REFERENCE_TOKEN_DECREASED',\n message: 'The patch would reduce note reference tokens.',\n };\n }\n\n return null;\n};\n\nconst collectWarnings = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): string[] => {\n const warnings: string[] = [];\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n const shouldWarnTags = policy?.preserveTags === undefined || policy.preserveTags === 'warn';\n\n if (shouldWarnTags && afterTagCount < beforeTagCount) {\n warnings.push('TAG_TOKEN_COUNT_DECREASED');\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n const shouldWarnReferences = policy?.preserveReferences === undefined || policy.preserveReferences === 'warn';\n\n if (shouldWarnReferences && afterReferenceCount < beforeReferenceCount) {\n warnings.push('REFERENCE_TOKEN_COUNT_DECREASED');\n }\n\n return warnings;\n};\n\nconst validateBaseline = (\n note: MarkdownPatchNoteSnapshot,\n expectedUpdatedAt: string | undefined,\n baseMarkdownSha256: string | undefined,\n): MarkdownChangeFailure | null => {\n const beforeMarkdownSha256 = calculateMarkdownSha256(note.markdown);\n\n if (!expectedUpdatedAt && !baseMarkdownSha256) {\n return {\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n };\n }\n\n if (\n (expectedUpdatedAt && !noteVersionsMatch(expectedUpdatedAt, note.updatedAt)) ||\n (baseMarkdownSha256 && baseMarkdownSha256 !== beforeMarkdownSha256)\n ) {\n return {\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message:\n 'The markdown write baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n };\n }\n\n return null;\n};\n\nconst validateChangeLimits = (\n beforeMarkdown: string,\n afterMarkdown: string,\n changedCharCount: number,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const changedLineCount = countChangedLines(beforeMarkdown, afterMarkdown);\n const exceededLines = policy?.maxChangedLines !== undefined && changedLineCount > policy.maxChangedLines;\n const exceededChars = policy?.maxChangedChars !== undefined && changedCharCount > policy.maxChangedChars;\n\n if (exceededLines || exceededChars) {\n return {\n status: 'failed',\n reason: 'CHANGE_LIMIT_EXCEEDED',\n message:\n 'The markdown write exceeds the configured change limit. Increase maxChangedLines/maxChangedChars to at least the requiredMinimum values or omit them for this write.',\n details: {\n configured: {\n ...(policy?.maxChangedLines !== undefined ? { maxChangedLines: policy.maxChangedLines } : {}),\n ...(policy?.maxChangedChars !== undefined ? { maxChangedChars: policy.maxChangedChars } : {}),\n },\n requiredMinimum: {\n maxChangedLines: changedLineCount,\n maxChangedChars: changedCharCount,\n },\n },\n };\n }\n\n return null;\n};\n\nconst buildDiffPreview = (diff: string, policy: MarkdownChangePolicy | undefined) => {\n const maxChars = policy?.diffPreviewMaxChars;\n\n if (maxChars === undefined || diff.length <= maxChars) {\n return {\n diff,\n ...(maxChars !== undefined ? { diffCharCount: diff.length } : {}),\n };\n }\n\n return {\n diff: diff.slice(0, maxChars),\n diffTruncated: true,\n diffCharCount: diff.length,\n };\n};\n\nconst createChangePlan = ({\n note,\n afterMarkdown,\n changedCharCount,\n policy,\n diffContextLines = DIFF_CONTEXT_LINES,\n match,\n placement,\n}: {\n note: MarkdownPatchNoteSnapshot;\n afterMarkdown: string;\n changedCharCount: number;\n policy: MarkdownChangePolicy | undefined;\n diffContextLines?: number | null;\n match?: MarkdownChangeDryRun['match'];\n placement?: MarkdownAppendPlacement;\n}): MarkdownChangePlan | MarkdownChangeFailure => {\n if (!policy?.allowNoop && afterMarkdown === note.markdown) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The markdown write would not change the note.',\n };\n }\n\n const preservationFailure = getPreservationFailure(note.markdown, afterMarkdown, policy);\n\n if (preservationFailure) {\n return preservationFailure;\n }\n\n const limitFailure = validateChangeLimits(note.markdown, afterMarkdown, changedCharCount, policy);\n\n if (limitFailure) {\n return limitFailure;\n }\n\n const diffPreview = buildDiffPreview(\n createUnifiedMarkdownDiff(note.markdown, afterMarkdown, diffContextLines),\n policy,\n );\n\n return {\n status: 'dry_run',\n note: {\n id: note.id,\n title: note.title,\n updatedAt: note.updatedAt,\n },\n ...(match ? { match } : {}),\n ...(placement ? { placement } : {}),\n proposed: {\n changedLineCount: countChangedLines(note.markdown, afterMarkdown),\n changedCharCount,\n beforeMarkdownSha256: calculateMarkdownSha256(note.markdown),\n afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown),\n ...diffPreview,\n },\n warnings: collectWarnings(note.markdown, afterMarkdown, policy),\n afterMarkdown,\n };\n};\n\nconst matchHasAnchors = (markdown: string, match: RawTextMatch, selector: ExactTextMarkdownPatchSelector) => {\n if (selector.before !== undefined && !markdown.slice(0, match.start).endsWith(selector.before)) {\n return false;\n }\n\n if (selector.after !== undefined && !markdown.slice(match.end).startsWith(selector.after)) {\n return false;\n }\n\n return true;\n};\n\nconst selectExactTextMatch = (\n markdown: string,\n selector: ExactTextMarkdownPatchSelector,\n):\n | { match: RawTextMatch; patchMatch: MarkdownPatchMatch }\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Exact text selector must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No exact text match was found in the current note.',\n };\n }\n\n const anchoredMatches = rawMatches.filter((match) => matchHasAnchors(markdown, match, selector));\n\n if (anchoredMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'ANCHOR_MISMATCH',\n message: 'Exact text matches were found, but before/after anchors did not match.',\n };\n }\n\n if (anchoredMatches.length > 1) {\n return {\n status: 'needs_disambiguation',\n reason: 'TARGET_AMBIGUOUS',\n matches: anchoredMatches.map((match) => toPatchMatch(markdown, match, rawMatches.length)),\n };\n }\n\n const match = anchoredMatches[0];\n\n return {\n match,\n patchMatch: toPatchMatch(markdown, match, rawMatches.length),\n };\n};\n\nconst selectMatchCandidate = (\n markdown: string,\n selector: MatchCandidateMarkdownPatchSelector,\n): { match: RawTextMatch; patchMatch: MarkdownPatchMatch } | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Match candidate selector text must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No match candidate text was found in the current note.',\n };\n }\n\n const match = rawMatches.find((candidate) => candidate.matchIndex === selector.matchIndex);\n\n if (!match) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match index does not exist in the current note.',\n };\n }\n\n const patchMatch = toPatchMatch(markdown, match, rawMatches.length);\n const positionMatches = selector.positionHint === undefined || patchMatch.positionHint === selector.positionHint;\n\n if (\n patchMatch.lineStart !== selector.lineStart ||\n patchMatch.matchSha256 !== selector.matchSha256 ||\n patchMatch.surroundingHash !== selector.surroundingHash ||\n !positionMatches\n ) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match candidate no longer matches the current note context.',\n };\n }\n\n return {\n match,\n patchMatch,\n };\n};\n\nconst applyPatchOperation = (markdown: string, match: RawTextMatch, operation: MarkdownPatchOperation) => {\n if (operation.type === 'replace') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.replacement}${markdown.slice(match.end)}`,\n changedCharCount: match.text.length + operation.replacement.length,\n };\n }\n\n if (operation.type === 'insert_before') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.insertion}${markdown.slice(match.start)}`,\n changedCharCount: operation.insertion.length,\n };\n }\n\n return {\n markdown: `${markdown.slice(0, match.end)}${operation.insertion}${markdown.slice(match.end)}`,\n changedCharCount: operation.insertion.length,\n };\n};\n\nexport const buildMarkdownPatchPlan = (input: PreviewMarkdownPatchInput): MarkdownPatchPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const selected =\n input.selector.type === 'exact_text'\n ? selectExactTextMatch(input.note.markdown, input.selector)\n : selectMatchCandidate(input.note.markdown, input.selector);\n\n if ('status' in selected) {\n return selected;\n }\n\n const patched = applyPatchOperation(input.note.markdown, selected.match, input.operation);\n const plan = createChangePlan({\n note: input.note,\n afterMarkdown: patched.markdown,\n changedCharCount: patched.changedCharCount,\n policy: input.policy,\n match: {\n count: 1,\n lineStart: selected.patchMatch.lineStart,\n lineEnd: selected.patchMatch.lineEnd,\n selectorType: input.selector.type,\n matchedTextSha256: selected.patchMatch.matchSha256,\n surroundingHash: selected.patchMatch.surroundingHash,\n },\n });\n\n return plan;\n};\n\nconst parseHeadingLine = (line: string) => {\n const match = /^(#{1,6})\\s+(.+?)\\s*#*\\s*$/.exec(line);\n\n if (!match) {\n return null;\n }\n\n return {\n level: match[1].length,\n heading: match[2].trim(),\n };\n};\n\nconst getLineStartOffsets = (lines: string[]) => {\n const offsets: number[] = [];\n let offset = 0;\n\n for (const line of lines) {\n offsets.push(offset);\n offset += line.length + 1;\n }\n\n return offsets;\n};\n\nconst findHeadingMatches = (markdown: string, placement: MarkdownAppendPlacementAfterHeading) => {\n const lines = splitLines(markdown);\n const offsets = getLineStartOffsets(lines);\n const headingLines = lines\n .map((line, index) => {\n const heading = parseHeadingLine(line);\n\n if (!heading) {\n return null;\n }\n\n return {\n ...heading,\n lineIndex: index,\n start: offsets[index],\n };\n })\n .filter((heading): heading is Omit<HeadingMatch, 'nextSectionStart'> => heading !== null);\n const matches = headingLines.filter((heading) => {\n return (\n heading.heading === placement.heading &&\n (placement.level === undefined || heading.level === placement.level)\n );\n });\n\n return matches.map((match) => {\n const nextHeading = headingLines.find(\n (heading) => heading.lineIndex > match.lineIndex && heading.level <= match.level,\n );\n\n return {\n ...match,\n nextSectionStart: nextHeading?.start ?? markdown.length,\n };\n });\n};\n\nconst appendWithSeparator = (prefix: string, insertion: string, separator: '\\n\\n' | '\\n') => {\n if (prefix.length === 0) {\n return insertion;\n }\n\n return `${prefix}${prefix.endsWith(separator) ? '' : separator}${insertion}`;\n};\n\nconst applyAppendPlacement = (\n markdown: string,\n insertion: string,\n placement: MarkdownAppendPlacement,\n separator: '\\n\\n' | '\\n',\n): { markdown: string; placement: MarkdownAppendPlacement } | MarkdownChangeFailure => {\n if (insertion.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_INSERTION',\n message: 'Append insertion must not be empty.',\n };\n }\n\n if (placement.type === 'end') {\n return {\n markdown: appendWithSeparator(markdown, insertion, separator),\n placement,\n };\n }\n\n if (\n placement.level !== undefined &&\n (!Number.isInteger(placement.level) || placement.level < 1 || placement.level > 6)\n ) {\n return {\n status: 'failed',\n reason: 'INVALID_HEADING_LEVEL',\n message: 'Heading level must be an integer from 1 to 6.',\n };\n }\n\n const headingMatches = findHeadingMatches(markdown, placement);\n\n if (headingMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'HEADING_NOT_FOUND',\n message: 'The requested heading was not found.',\n };\n }\n\n if (headingMatches.length > 1) {\n return {\n status: 'failed',\n reason: 'HEADING_AMBIGUOUS',\n message: 'The requested heading appears more than once.',\n };\n }\n\n const [headingMatch] = headingMatches;\n const prefix = markdown.slice(0, headingMatch.nextSectionStart);\n const suffix = markdown.slice(headingMatch.nextSectionStart);\n const prefixWithInsertion = appendWithSeparator(prefix, insertion, separator);\n const nextMarkdown =\n suffix.length === 0\n ? prefixWithInsertion\n : `${prefixWithInsertion}${prefixWithInsertion.endsWith(separator) ? '' : separator}${suffix}`;\n\n return {\n markdown: nextMarkdown,\n placement,\n };\n};\n\nexport const buildMarkdownAppendPlan = (input: PreviewMarkdownAppendInput): MarkdownAppendPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const placement = input.placement ?? { type: 'end' as const };\n const separator = input.separator ?? '\\n\\n';\n const appended = applyAppendPlacement(input.note.markdown, input.insertion, placement, separator);\n\n if ('status' in appended) {\n return appended;\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: appended.markdown,\n changedCharCount: input.insertion.length,\n policy: input.policy,\n placement: appended.placement,\n });\n};\n\nexport const buildMarkdownReplacePlan = (input: PreviewMarkdownReplaceInput): MarkdownReplacePlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n if (input.replacement.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Replacement markdown must not be empty.',\n };\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: input.replacement,\n changedCharCount: input.note.markdown.length + input.replacement.length,\n policy: input.policy,\n diffContextLines: null,\n });\n};\n\nconst toPreviewResult = (plan: MarkdownChangePlan): MarkdownChangeDryRun => {\n return {\n status: 'dry_run',\n note: plan.note,\n ...(plan.match ? { match: plan.match } : {}),\n ...(plan.placement ? { placement: plan.placement } : {}),\n proposed: plan.proposed,\n warnings: plan.warnings,\n };\n};\n\nexport const previewMarkdownPatch = (input: PreviewMarkdownPatchInput): MarkdownPatchPreviewResult => {\n const plan = buildMarkdownPatchPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownAppend = (\n input: PreviewMarkdownAppendInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownAppendPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownReplace = (\n input: PreviewMarkdownReplaceInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownReplacePlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n"],"mappings":"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","names":["match"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/note/services/markdown-patch.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst HASH_ALGORITHM = 'sha256';\nconst EXCERPT_CONTEXT_CHARS = 80;\n\nexport interface MarkdownPatchNoteSnapshot {\n id: string;\n title: string;\n updatedAt: string;\n markdown: string;\n}\n\nexport interface ExactTextMarkdownPatchSelector {\n type: 'exact_text';\n text: string;\n before?: string;\n after?: string;\n}\n\nexport interface MatchCandidateMarkdownPatchSelector {\n type: 'match_candidate';\n text: string;\n matchIndex: number;\n lineStart: number;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport type MarkdownPatchSelector = ExactTextMarkdownPatchSelector | MatchCandidateMarkdownPatchSelector;\n\nexport interface ReplaceMarkdownPatchOperation {\n type: 'replace';\n replacement: string;\n}\n\nexport interface InsertBeforeMarkdownPatchOperation {\n type: 'insert_before';\n insertion: string;\n}\n\nexport interface InsertAfterMarkdownPatchOperation {\n type: 'insert_after';\n insertion: string;\n}\n\nexport type MarkdownPatchOperation =\n | ReplaceMarkdownPatchOperation\n | InsertBeforeMarkdownPatchOperation\n | InsertAfterMarkdownPatchOperation;\n\nexport interface MarkdownChangePolicy {\n allowNoop?: boolean;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface BuildMarkdownPatchPlanInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n selector: MarkdownPatchSelector;\n operation: MarkdownPatchOperation;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownAppendPlacementEnd {\n type: 'end';\n}\n\nexport interface MarkdownAppendPlacementAfterHeading {\n type: 'after_heading';\n heading: string;\n level?: number;\n}\n\nexport type MarkdownAppendPlacement = MarkdownAppendPlacementEnd | MarkdownAppendPlacementAfterHeading;\n\nexport interface BuildMarkdownAppendPlanInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n insertion: string;\n placement?: MarkdownAppendPlacement;\n separator?: '\\n\\n' | '\\n';\n policy?: MarkdownChangePolicy;\n}\n\nexport interface BuildMarkdownReplacePlanInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownPatchMatch {\n matchIndex: number;\n lineStart: number;\n lineEnd: number;\n text: string;\n beforeExcerpt?: string;\n afterExcerpt?: string;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport interface MarkdownChangePlan {\n status: 'planned';\n note: {\n id: string;\n title: string;\n updatedAt: string;\n };\n match?: {\n count: 1;\n lineStart: number;\n lineEnd: number;\n selectorType: string;\n matchedTextSha256: string;\n surroundingHash: string;\n };\n placement?: MarkdownAppendPlacement;\n proposed: {\n changedLineCount: number;\n changedCharCount: number;\n beforeMarkdownSha256: string;\n afterMarkdownSha256: string;\n };\n warnings: string[];\n afterMarkdown: string;\n}\n\nexport interface MarkdownPatchNeedsDisambiguation {\n status: 'needs_disambiguation';\n reason: 'TARGET_AMBIGUOUS';\n matches: MarkdownPatchMatch[];\n}\n\nexport interface MarkdownChangeFailure {\n status: 'failed';\n reason:\n | 'ANCHOR_MISMATCH'\n | 'BASELINE_MISMATCH'\n | 'CANDIDATE_MISMATCH'\n | 'EMPTY_INSERTION'\n | 'EMPTY_REPLACEMENT'\n | 'EMPTY_TARGET'\n | 'HEADING_AMBIGUOUS'\n | 'HEADING_NOT_FOUND'\n | 'INVALID_HEADING_LEVEL'\n | 'INVALID_PROPERTY_INPUT'\n | 'MARKDOWN_IMPORT_LOSSY'\n | 'MISSING_BASELINE'\n | 'NOOP'\n | 'REFERENCE_STRUCTURE_DECREASED'\n | 'REFERENCE_TOKEN_DECREASED'\n | 'TAG_TOKEN_DECREASED'\n | 'TARGET_NOT_FOUND'\n | 'UNSUPPORTED_MARKDOWN_STRUCTURE';\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type MarkdownPatchPlanResult = MarkdownChangePlan | MarkdownPatchNeedsDisambiguation | MarkdownChangeFailure;\nexport type MarkdownAppendPlanResult = MarkdownChangePlan | MarkdownChangeFailure;\nexport type MarkdownReplacePlanResult = MarkdownChangePlan | MarkdownChangeFailure;\n\ninterface RawTextMatch {\n matchIndex: number;\n start: number;\n end: number;\n text: string;\n}\n\ninterface HeadingMatch {\n heading: string;\n level: number;\n lineIndex: number;\n start: number;\n nextSectionStart: number;\n}\n\nexport const calculateMarkdownSha256 = (value: string) => {\n return crypto.createHash(HASH_ALGORITHM).update(value, 'utf8').digest('hex');\n};\n\nconst countNewlines = (value: string) => {\n let count = 0;\n\n for (const character of value) {\n if (character === '\\n') {\n count += 1;\n }\n }\n\n return count;\n};\n\nconst getLineRange = (markdown: string, match: RawTextMatch) => {\n const lineStart = countNewlines(markdown.slice(0, match.start)) + 1;\n const lineEnd = lineStart + countNewlines(match.text);\n\n return {\n lineStart,\n lineEnd,\n };\n};\n\nconst buildExcerpt = (markdown: string, start: number, end: number) => {\n const excerpt = markdown.slice(start, end).trim();\n\n return excerpt.length > 0 ? excerpt : undefined;\n};\n\nconst toPatchMatch = (markdown: string, match: RawTextMatch, totalMatches: number): MarkdownPatchMatch => {\n const { lineStart, lineEnd } = getLineRange(markdown, match);\n const beforeStart = Math.max(0, match.start - EXCERPT_CONTEXT_CHARS);\n const afterEnd = Math.min(markdown.length, match.end + EXCERPT_CONTEXT_CHARS);\n const beforeText = markdown.slice(beforeStart, match.start);\n const afterText = markdown.slice(match.end, afterEnd);\n\n return {\n matchIndex: match.matchIndex,\n lineStart,\n lineEnd,\n text: match.text,\n beforeExcerpt: buildExcerpt(markdown, beforeStart, match.start),\n afterExcerpt: buildExcerpt(markdown, match.end, afterEnd),\n matchSha256: calculateMarkdownSha256(match.text),\n surroundingHash: calculateMarkdownSha256(JSON.stringify({ beforeText, targetText: match.text, afterText })),\n ...(match.matchIndex === 0 && totalMatches > 1 ? { positionHint: 'first' } : {}),\n ...(match.matchIndex === totalMatches - 1 && totalMatches > 1 ? { positionHint: 'last' } : {}),\n };\n};\n\nconst findExactTextMatches = (markdown: string, text: string) => {\n const matches: RawTextMatch[] = [];\n let searchStart = 0;\n\n while (searchStart <= markdown.length) {\n const start = markdown.indexOf(text, searchStart);\n\n if (start === -1) {\n break;\n }\n\n matches.push({\n matchIndex: matches.length,\n start,\n end: start + text.length,\n text,\n });\n searchStart = start + text.length;\n }\n\n return matches;\n};\n\nconst splitLines = (value: string) => {\n return value.split('\\n');\n};\n\nconst findSharedLineEdges = (beforeLines: string[], afterLines: string[]) => {\n let prefix = 0;\n const maxPrefix = Math.min(beforeLines.length, afterLines.length);\n\n while (prefix < maxPrefix && beforeLines[prefix] === afterLines[prefix]) {\n prefix += 1;\n }\n\n let suffix = 0;\n const maxSuffix = Math.min(beforeLines.length - prefix, afterLines.length - prefix);\n\n while (\n suffix < maxSuffix &&\n beforeLines[beforeLines.length - 1 - suffix] === afterLines[afterLines.length - 1 - suffix]\n ) {\n suffix += 1;\n }\n\n return {\n prefix,\n suffix,\n };\n};\n\nconst countChangedLines = (beforeMarkdown: string, afterMarkdown: string) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const removedLineCount = beforeLines.length - prefix - suffix;\n const addedLineCount = afterLines.length - prefix - suffix;\n\n return Math.max(removedLineCount, addedLineCount);\n};\n\nconst isMarkdownTokenWhitespace = (character: string) => /\\s/.test(character);\n\nexport const countExplicitTagTokens = (value: string) => {\n let count = 0;\n\n for (let index = 0; index < value.length; index += 1) {\n if (value[index] !== '[' || (value[index + 1] !== '@' && value[index + 1] !== '#')) {\n continue;\n }\n\n let cursor = index + 2;\n let isValid = cursor < value.length;\n\n while (cursor < value.length && value[cursor] !== ']') {\n if (value[cursor] === '[' || isMarkdownTokenWhitespace(value[cursor])) {\n isValid = false;\n break;\n }\n\n cursor += 1;\n }\n\n if (isValid && cursor < value.length && cursor > index + 2) {\n count += 1;\n index = cursor;\n continue;\n }\n\n if (!isValid) {\n index = Math.max(index, cursor - 1);\n }\n }\n\n return count;\n};\n\nexport const countMarkdownReferenceTokens = (value: string) => {\n let count = 0;\n let cursor = 0;\n\n while (cursor < value.length) {\n const start = value.indexOf('[[', cursor);\n\n if (start === -1) {\n break;\n }\n\n const end = value.indexOf(']]', start + 2);\n\n if (end === -1) {\n break;\n }\n\n if (end > start + 2) {\n count += 1;\n }\n\n cursor = end + 2;\n }\n\n return count;\n};\n\nconst parseVersionTimestamp = (value: string) => {\n const timestamp = /^\\d+$/.test(value) ? Number(value) : Date.parse(value);\n\n return Number.isFinite(timestamp) ? timestamp : null;\n};\n\nconst noteVersionsMatch = (expectedUpdatedAt: string, currentUpdatedAt: string) => {\n const expectedTimestamp = parseVersionTimestamp(expectedUpdatedAt);\n const currentTimestamp = parseVersionTimestamp(currentUpdatedAt);\n\n return expectedTimestamp !== null && currentTimestamp !== null && expectedTimestamp === currentTimestamp;\n};\n\nconst getPreservationFailure = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n\n if (policy?.preserveTags === true && afterTagCount < beforeTagCount) {\n return {\n status: 'failed',\n reason: 'TAG_TOKEN_DECREASED',\n message: 'The patch would reduce explicit tag tokens.',\n };\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n\n if (policy?.preserveReferences === true && afterReferenceCount < beforeReferenceCount) {\n return {\n status: 'failed',\n reason: 'REFERENCE_TOKEN_DECREASED',\n message: 'The patch would reduce note reference tokens.',\n };\n }\n\n return null;\n};\n\nconst collectWarnings = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): string[] => {\n const warnings: string[] = [];\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n const shouldWarnTags = policy?.preserveTags === undefined || policy.preserveTags === 'warn';\n\n if (shouldWarnTags && afterTagCount < beforeTagCount) {\n warnings.push('TAG_TOKEN_COUNT_DECREASED');\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n const shouldWarnReferences = policy?.preserveReferences === undefined || policy.preserveReferences === 'warn';\n\n if (shouldWarnReferences && afterReferenceCount < beforeReferenceCount) {\n warnings.push('REFERENCE_TOKEN_COUNT_DECREASED');\n }\n\n return warnings;\n};\n\nconst validateBaseline = (\n note: MarkdownPatchNoteSnapshot,\n expectedUpdatedAt: string | undefined,\n baseMarkdownSha256: string | undefined,\n): MarkdownChangeFailure | null => {\n const beforeMarkdownSha256 = calculateMarkdownSha256(note.markdown);\n\n if (!expectedUpdatedAt && !baseMarkdownSha256) {\n return {\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n };\n }\n\n if (\n (expectedUpdatedAt && !noteVersionsMatch(expectedUpdatedAt, note.updatedAt)) ||\n (baseMarkdownSha256 && baseMarkdownSha256 !== beforeMarkdownSha256)\n ) {\n return {\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message:\n 'The markdown write baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n };\n }\n\n return null;\n};\n\nconst createChangePlan = ({\n note,\n afterMarkdown,\n changedCharCount,\n policy,\n match,\n placement,\n}: {\n note: MarkdownPatchNoteSnapshot;\n afterMarkdown: string;\n changedCharCount: number;\n policy: MarkdownChangePolicy | undefined;\n match?: MarkdownChangePlan['match'];\n placement?: MarkdownAppendPlacement;\n}): MarkdownChangePlan | MarkdownChangeFailure => {\n if (!policy?.allowNoop && afterMarkdown === note.markdown) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The markdown write would not change the note.',\n };\n }\n\n const preservationFailure = getPreservationFailure(note.markdown, afterMarkdown, policy);\n\n if (preservationFailure) {\n return preservationFailure;\n }\n\n return {\n status: 'planned',\n note: {\n id: note.id,\n title: note.title,\n updatedAt: note.updatedAt,\n },\n ...(match ? { match } : {}),\n ...(placement ? { placement } : {}),\n proposed: {\n changedLineCount: countChangedLines(note.markdown, afterMarkdown),\n changedCharCount,\n beforeMarkdownSha256: calculateMarkdownSha256(note.markdown),\n afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown),\n },\n warnings: collectWarnings(note.markdown, afterMarkdown, policy),\n afterMarkdown,\n };\n};\n\nconst matchHasAnchors = (markdown: string, match: RawTextMatch, selector: ExactTextMarkdownPatchSelector) => {\n if (selector.before !== undefined && !markdown.slice(0, match.start).endsWith(selector.before)) {\n return false;\n }\n\n if (selector.after !== undefined && !markdown.slice(match.end).startsWith(selector.after)) {\n return false;\n }\n\n return true;\n};\n\nconst selectExactTextMatch = (\n markdown: string,\n selector: ExactTextMarkdownPatchSelector,\n):\n | { match: RawTextMatch; patchMatch: MarkdownPatchMatch }\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Exact text selector must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No exact text match was found in the current note.',\n };\n }\n\n const anchoredMatches = rawMatches.filter((match) => matchHasAnchors(markdown, match, selector));\n\n if (anchoredMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'ANCHOR_MISMATCH',\n message: 'Exact text matches were found, but before/after anchors did not match.',\n };\n }\n\n if (anchoredMatches.length > 1) {\n return {\n status: 'needs_disambiguation',\n reason: 'TARGET_AMBIGUOUS',\n matches: anchoredMatches.map((match) => toPatchMatch(markdown, match, rawMatches.length)),\n };\n }\n\n const match = anchoredMatches[0];\n\n return {\n match,\n patchMatch: toPatchMatch(markdown, match, rawMatches.length),\n };\n};\n\nconst selectMatchCandidate = (\n markdown: string,\n selector: MatchCandidateMarkdownPatchSelector,\n): { match: RawTextMatch; patchMatch: MarkdownPatchMatch } | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Match candidate selector text must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No match candidate text was found in the current note.',\n };\n }\n\n const match = rawMatches.find((candidate) => candidate.matchIndex === selector.matchIndex);\n\n if (!match) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match index does not exist in the current note.',\n };\n }\n\n const patchMatch = toPatchMatch(markdown, match, rawMatches.length);\n const positionMatches = selector.positionHint === undefined || patchMatch.positionHint === selector.positionHint;\n\n if (\n patchMatch.lineStart !== selector.lineStart ||\n patchMatch.matchSha256 !== selector.matchSha256 ||\n patchMatch.surroundingHash !== selector.surroundingHash ||\n !positionMatches\n ) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match candidate no longer matches the current note context.',\n };\n }\n\n return {\n match,\n patchMatch,\n };\n};\n\nconst applyPatchOperation = (markdown: string, match: RawTextMatch, operation: MarkdownPatchOperation) => {\n if (operation.type === 'replace') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.replacement}${markdown.slice(match.end)}`,\n changedCharCount: match.text.length + operation.replacement.length,\n };\n }\n\n if (operation.type === 'insert_before') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.insertion}${markdown.slice(match.start)}`,\n changedCharCount: operation.insertion.length,\n };\n }\n\n return {\n markdown: `${markdown.slice(0, match.end)}${operation.insertion}${markdown.slice(match.end)}`,\n changedCharCount: operation.insertion.length,\n };\n};\n\nexport const buildMarkdownPatchPlan = (input: BuildMarkdownPatchPlanInput): MarkdownPatchPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const selected =\n input.selector.type === 'exact_text'\n ? selectExactTextMatch(input.note.markdown, input.selector)\n : selectMatchCandidate(input.note.markdown, input.selector);\n\n if ('status' in selected) {\n return selected;\n }\n\n const patched = applyPatchOperation(input.note.markdown, selected.match, input.operation);\n const plan = createChangePlan({\n note: input.note,\n afterMarkdown: patched.markdown,\n changedCharCount: patched.changedCharCount,\n policy: input.policy,\n match: {\n count: 1,\n lineStart: selected.patchMatch.lineStart,\n lineEnd: selected.patchMatch.lineEnd,\n selectorType: input.selector.type,\n matchedTextSha256: selected.patchMatch.matchSha256,\n surroundingHash: selected.patchMatch.surroundingHash,\n },\n });\n\n return plan;\n};\n\nconst parseHeadingLine = (line: string) => {\n const match = /^(#{1,6})\\s+(.+?)\\s*#*\\s*$/.exec(line);\n\n if (!match) {\n return null;\n }\n\n return {\n level: match[1].length,\n heading: match[2].trim(),\n };\n};\n\nconst getLineStartOffsets = (lines: string[]) => {\n const offsets: number[] = [];\n let offset = 0;\n\n for (const line of lines) {\n offsets.push(offset);\n offset += line.length + 1;\n }\n\n return offsets;\n};\n\nconst findHeadingMatches = (markdown: string, placement: MarkdownAppendPlacementAfterHeading) => {\n const lines = splitLines(markdown);\n const offsets = getLineStartOffsets(lines);\n const headingLines = lines\n .map((line, index) => {\n const heading = parseHeadingLine(line);\n\n if (!heading) {\n return null;\n }\n\n return {\n ...heading,\n lineIndex: index,\n start: offsets[index],\n };\n })\n .filter((heading): heading is Omit<HeadingMatch, 'nextSectionStart'> => heading !== null);\n const matches = headingLines.filter((heading) => {\n return (\n heading.heading === placement.heading &&\n (placement.level === undefined || heading.level === placement.level)\n );\n });\n\n return matches.map((match) => {\n const nextHeading = headingLines.find(\n (heading) => heading.lineIndex > match.lineIndex && heading.level <= match.level,\n );\n\n return {\n ...match,\n nextSectionStart: nextHeading?.start ?? markdown.length,\n };\n });\n};\n\nconst appendWithSeparator = (prefix: string, insertion: string, separator: '\\n\\n' | '\\n') => {\n if (prefix.length === 0) {\n return insertion;\n }\n\n return `${prefix}${prefix.endsWith(separator) ? '' : separator}${insertion}`;\n};\n\nconst applyAppendPlacement = (\n markdown: string,\n insertion: string,\n placement: MarkdownAppendPlacement,\n separator: '\\n\\n' | '\\n',\n): { markdown: string; placement: MarkdownAppendPlacement } | MarkdownChangeFailure => {\n if (insertion.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_INSERTION',\n message: 'Append insertion must not be empty.',\n };\n }\n\n if (placement.type === 'end') {\n return {\n markdown: appendWithSeparator(markdown, insertion, separator),\n placement,\n };\n }\n\n if (\n placement.level !== undefined &&\n (!Number.isInteger(placement.level) || placement.level < 1 || placement.level > 6)\n ) {\n return {\n status: 'failed',\n reason: 'INVALID_HEADING_LEVEL',\n message: 'Heading level must be an integer from 1 to 6.',\n };\n }\n\n const headingMatches = findHeadingMatches(markdown, placement);\n\n if (headingMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'HEADING_NOT_FOUND',\n message: 'The requested heading was not found.',\n };\n }\n\n if (headingMatches.length > 1) {\n return {\n status: 'failed',\n reason: 'HEADING_AMBIGUOUS',\n message: 'The requested heading appears more than once.',\n };\n }\n\n const [headingMatch] = headingMatches;\n const prefix = markdown.slice(0, headingMatch.nextSectionStart);\n const suffix = markdown.slice(headingMatch.nextSectionStart);\n const prefixWithInsertion = appendWithSeparator(prefix, insertion, separator);\n const nextMarkdown =\n suffix.length === 0\n ? prefixWithInsertion\n : `${prefixWithInsertion}${prefixWithInsertion.endsWith(separator) ? '' : separator}${suffix}`;\n\n return {\n markdown: nextMarkdown,\n placement,\n };\n};\n\nexport const buildMarkdownAppendPlan = (input: BuildMarkdownAppendPlanInput): MarkdownAppendPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const placement = input.placement ?? { type: 'end' as const };\n const separator = input.separator ?? '\\n\\n';\n const appended = applyAppendPlacement(input.note.markdown, input.insertion, placement, separator);\n\n if ('status' in appended) {\n return appended;\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: appended.markdown,\n changedCharCount: input.insertion.length,\n policy: input.policy,\n placement: appended.placement,\n });\n};\n\nexport const buildMarkdownReplacePlan = (input: BuildMarkdownReplacePlanInput): MarkdownReplacePlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n if (input.replacement.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Replacement markdown must not be empty.',\n };\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: input.replacement,\n changedCharCount: input.note.markdown.length + input.replacement.length,\n policy: input.policy,\n });\n};\n"],"mappings":"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","names":["match"]}
|
|
@@ -26,19 +26,6 @@ const createNoteWriteService = (deps) => {
|
|
|
26
26
|
const nextContent = data.content ?? existingNote.content;
|
|
27
27
|
const where = force ? { id } : { id, updatedAt: new Date(expectedTimestamp ?? existingNote.updatedAt.getTime()) };
|
|
28
28
|
try {
|
|
29
|
-
const updatedNote = await deps.updateNote({
|
|
30
|
-
where,
|
|
31
|
-
data: {
|
|
32
|
-
...data.title !== void 0 ? { title: data.title } : {},
|
|
33
|
-
...data.content !== void 0 ? { content: data.content } : {},
|
|
34
|
-
...data.layout !== void 0 ? { layout: data.layout } : {},
|
|
35
|
-
...buildNoteSearchProjection({
|
|
36
|
-
title: nextTitle,
|
|
37
|
-
content: nextContent
|
|
38
|
-
}),
|
|
39
|
-
...data.tagIds ? { tags: { set: data.tagIds.map((tagId) => ({ id: tagId })) } } : {}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
29
|
const snapshot = await deps.captureBaseline({
|
|
43
30
|
noteId: id,
|
|
44
31
|
baseline: {
|
|
@@ -53,6 +40,19 @@ const createNoteWriteService = (deps) => {
|
|
|
53
40
|
...snapshotMeta ? { meta: snapshotMeta } : {},
|
|
54
41
|
...force ? { force: true } : {}
|
|
55
42
|
});
|
|
43
|
+
const updatedNote = await deps.updateNote({
|
|
44
|
+
where,
|
|
45
|
+
data: {
|
|
46
|
+
...data.title !== void 0 ? { title: data.title } : {},
|
|
47
|
+
...data.content !== void 0 ? { content: data.content } : {},
|
|
48
|
+
...data.layout !== void 0 ? { layout: data.layout } : {},
|
|
49
|
+
...buildNoteSearchProjection({
|
|
50
|
+
title: nextTitle,
|
|
51
|
+
content: nextContent
|
|
52
|
+
}),
|
|
53
|
+
...data.tagIds ? { tags: { set: data.tagIds.map((tagId) => ({ id: tagId })) } } : {}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
56
|
return {
|
|
57
57
|
note: updatedNote,
|
|
58
58
|
snapshot
|