ocean-brain 0.7.5 → 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.
Files changed (89) hide show
  1. package/dist/index.js +3 -3
  2. package/dist/mcp.js +180 -736
  3. package/package.json +4 -1
  4. package/server/client/dist/assets/ArrowRight.es-C4zlc54Z.js +1 -0
  5. package/server/client/dist/assets/{Calendar-Bc2zJueR.js → Calendar-COrN6Hjv.js} +2 -2
  6. package/server/client/dist/assets/Callout-BK0K9E74.js +1 -0
  7. package/server/client/dist/assets/Copy.es-zm8f_XXX.js +1 -0
  8. package/server/client/dist/assets/Graph-BH1fzhue.js +1 -0
  9. package/server/client/dist/assets/{Image-DxWhlIDG.js → Image-KXuHOG4b.js} +1 -1
  10. package/server/client/dist/assets/Image.es-BKahRbIm.js +1 -0
  11. package/server/client/dist/assets/Note-C143cKKw.js +12 -0
  12. package/server/client/dist/assets/Plus.es-BqeC8T8h.js +1 -0
  13. package/server/client/dist/assets/Reminders-BYG5rz1M.js +1 -0
  14. package/server/client/dist/assets/Search-DBWsIMDP.js +1 -0
  15. package/server/client/dist/assets/{SurfaceCard-CkAVMLPs.js → SurfaceCard-CNSQfYfM.js} +1 -1
  16. package/server/client/dist/assets/Tag-5aCfSO8Y.js +1 -0
  17. package/server/client/dist/assets/TagNotes-BPZjN-6l.js +1 -0
  18. package/server/client/dist/assets/Trash.es-CRQkTnXZ.js +1 -0
  19. package/server/client/dist/assets/ViewNotes-DGLNF96x.js +1 -0
  20. package/server/client/dist/assets/ViewSectionDialog-CzskgmMF.js +102 -0
  21. package/server/client/dist/assets/Views-DR5jT23G.js +1 -0
  22. package/server/client/dist/assets/{graph-vendor-CUxe67Lr.js → graph-vendor-UTvF2NS_.js} +2 -2
  23. package/server/client/dist/assets/image.api-8dlMwzMd.js +17 -0
  24. package/server/client/dist/assets/{index-C5BGzDgt.css → index-BEUqDuAZ.css} +1 -1
  25. package/server/client/dist/assets/{index-D3BfroIT.js → index-DGIAh3ZY.js} +1 -1
  26. package/server/client/dist/assets/index-VHp17GQX.js +342 -0
  27. package/server/client/dist/assets/manage-image-RqtWKas2.js +1 -0
  28. package/server/client/dist/assets/{manage-image-detail-C8Q7K7I3.js → manage-image-detail-BTSIbLlQ.js} +1 -1
  29. package/server/client/dist/assets/mcp-EDurbggR.js +4 -0
  30. package/server/client/dist/assets/mcp-admin-adapter-DmGUo5G8.js +1 -0
  31. package/server/client/dist/assets/{note-core-Z6kqulGB.js → note-core-BklwyZHk.js} +1 -1
  32. package/server/client/dist/assets/{note-vendor-i1M2FSI2.js → note-vendor-DkYKUmLo.js} +1 -1
  33. package/server/client/dist/assets/{placeholder-Cr_nI8UI.js → placeholder-DeKgSBnC.js} +4 -4
  34. package/server/client/dist/assets/{properties-CvYWJWUM.js → properties-BjrZvOYn.js} +1 -1
  35. package/server/client/dist/assets/tag.api-Dl8DQd0m.js +21 -0
  36. package/server/client/dist/assets/trash-C5ntddLu.js +1 -0
  37. package/server/client/dist/assets/{useReminderMutate-CDmIEwsw.js → useReminderMutate-CoXXqJY2.js} +1 -1
  38. package/server/client/dist/index.html +5 -5
  39. package/server/dist/features/auth/http/login-page.js +1 -1
  40. package/server/dist/features/image/http/upload.js +34 -23
  41. package/server/dist/features/image/http/upload.js.map +1 -1
  42. package/server/dist/features/image/services/validation.js +111 -0
  43. package/server/dist/features/image/services/validation.js.map +1 -0
  44. package/server/dist/features/mcp-admin/http/handlers.js +11 -3
  45. package/server/dist/features/mcp-admin/http/handlers.js.map +1 -1
  46. package/server/dist/features/note/http/mcp.js +8 -43
  47. package/server/dist/features/note/http/mcp.js.map +1 -1
  48. package/server/dist/features/note/services/cleanup.js +2 -2
  49. package/server/dist/features/note/services/cleanup.js.map +1 -1
  50. package/server/dist/features/note/services/markdown-intent-write.js +11 -43
  51. package/server/dist/features/note/services/markdown-intent-write.js.map +1 -1
  52. package/server/dist/features/note/services/markdown-patch.js +4 -123
  53. package/server/dist/features/note/services/markdown-patch.js.map +1 -1
  54. package/server/dist/features/note/services/write.js +13 -13
  55. package/server/dist/features/note/services/write.js.map +1 -1
  56. package/server/dist/features/tag/graphql/tag.query.resolver.js +16 -1
  57. package/server/dist/features/tag/graphql/tag.query.resolver.js.map +1 -1
  58. package/server/dist/features/tag/graphql/tag.type-defs.js +2 -0
  59. package/server/dist/features/tag/graphql/tag.type-defs.js.map +1 -1
  60. package/server/dist/modules/app-version.js +105 -0
  61. package/server/dist/modules/app-version.js.map +1 -0
  62. package/server/dist/modules/mcp-auth.js +62 -1
  63. package/server/dist/modules/mcp-auth.js.map +1 -1
  64. package/server/dist/paths.js +1 -0
  65. package/server/dist/paths.js.map +1 -1
  66. package/server/dist/routes/api.js +2 -8
  67. package/server/dist/routes/api.js.map +1 -1
  68. package/server/dist/routes/client.js +51 -1
  69. package/server/dist/routes/client.js.map +1 -1
  70. package/server/client/dist/assets/Callout-B4kA_Cvr.js +0 -1
  71. package/server/client/dist/assets/Graph-uq5jfJgt.js +0 -1
  72. package/server/client/dist/assets/Image.es-BpAOJjJT.js +0 -1
  73. package/server/client/dist/assets/Note-Bo8ZYRP-.js +0 -22
  74. package/server/client/dist/assets/Plus.es-Dc_js8AC.js +0 -1
  75. package/server/client/dist/assets/Reminders-lj7Equld.js +0 -1
  76. package/server/client/dist/assets/Search-1wcRO5--.js +0 -1
  77. package/server/client/dist/assets/Tag-CeygwFfv.js +0 -1
  78. package/server/client/dist/assets/TagNotes-B8DqItxw.js +0 -1
  79. package/server/client/dist/assets/Trash.es-OuQVb_SX.js +0 -1
  80. package/server/client/dist/assets/ViewNotes-BH7PbdOD.js +0 -1
  81. package/server/client/dist/assets/ViewSectionTableRenderer-BJwhd5be.js +0 -102
  82. package/server/client/dist/assets/Views-eYFtsdFj.js +0 -1
  83. package/server/client/dist/assets/image.api-C0eYGCeH.js +0 -17
  84. package/server/client/dist/assets/index-jIfkRdY_.js +0 -361
  85. package/server/client/dist/assets/manage-image-C0YwkLIP.js +0 -1
  86. package/server/client/dist/assets/mcp-DHHhTLC8.js +0 -1
  87. package/server/client/dist/assets/trash-DA27aW7L.js +0 -1
  88. package/server/dist/features/image/services/remote-fetch.js +0 -176
  89. package/server/dist/features/image/services/remote-fetch.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/features/note/http/mcp.ts"],"sourcesContent":["import {\n createNoteFromMarkdown,\n InvalidNoteAuthoringInputError,\n updateNoteFromMarkdown,\n} from '~/features/note/services/authoring.js';\nimport { deleteNoteById } from '~/features/note/services/cleanup.js';\nimport {\n appendNoteMarkdown,\n patchNoteMarkdown,\n replaceNoteMarkdown,\n updateNoteMetadata,\n} from '~/features/note/services/markdown-intent-write.js';\nimport type {\n MarkdownAppendPlacement,\n MarkdownChangePolicy,\n MarkdownPatchOperation,\n MarkdownPatchSelector,\n} from '~/features/note/services/markdown-patch.js';\nimport type { NotePropertiesByKeyPatchInput } from '~/features/note/services/properties.js';\nimport { MCP_SNAPSHOT_META } from '~/features/note/services/snapshot.js';\nimport models, { type NoteLayout } from '~/models.js';\nimport { createAppError } from '~/modules/error-handler.js';\nimport { emitServerEvent, type ServerEventInput } from '~/modules/server-events.js';\nimport type { Controller } from '~/types/index.js';\n\nconst NOTE_LAYOUTS = new Set<NoteLayout>(['narrow', 'wide', 'full']);\n\nconst POSITION_HINTS = new Set(['first', 'last']);\nconst PRESERVATION_POLICIES = new Set<unknown>([true, false, 'warn']);\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst resolveNoteLayout = (value: unknown): NoteLayout | null | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'string' && NOTE_LAYOUTS.has(value as NoteLayout)) {\n return value as NoteLayout;\n }\n\n return null;\n};\n\nconst resolveOptionalString = (value: unknown, code: string, message: string) => {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'string') {\n return value;\n }\n\n throw createAppError(400, code, message);\n};\n\nconst resolveOptionalBoolean = (value: unknown, code: string, message: string) => {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'boolean') {\n return value;\n }\n\n throw createAppError(400, code, message);\n};\n\nconst resolveMetadataPropertyPatch = (value: unknown): NotePropertiesByKeyPatchInput | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Properties patch must be an object.');\n }\n\n const set =\n value.set === undefined\n ? undefined\n : Array.isArray(value.set)\n ? value.set.map((item) => {\n if (!isRecord(item) || typeof item.key !== 'string' || typeof item.value !== 'string') {\n throw createAppError(\n 400,\n 'INVALID_NOTE_PROPERTIES',\n 'Property set items must include string key and value fields.',\n );\n }\n\n return {\n key: item.key,\n value: item.value,\n };\n })\n : null;\n\n if (set === null) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property set must be an array.');\n }\n\n const deleteKeys =\n value.deleteKeys === undefined\n ? undefined\n : Array.isArray(value.deleteKeys)\n ? value.deleteKeys.map((key) => {\n if (typeof key !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property deleteKeys must be strings.');\n }\n\n return key;\n })\n : null;\n\n if (deleteKeys === null) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property deleteKeys must be an array.');\n }\n\n return {\n ...(set !== undefined ? { set } : {}),\n ...(deleteKeys !== undefined ? { deleteKeys } : {}),\n };\n};\n\nconst resolvePositiveNoteId = (value: unknown) => {\n const noteId = Number(value);\n\n if (!Number.isInteger(noteId) || noteId <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n return noteId;\n};\n\nconst serializeNoteWriteBaseline = (note: { id: number | string; updatedAt: Date | string }) => {\n return {\n id: String(note.id),\n updatedAt: note.updatedAt instanceof Date ? note.updatedAt.toISOString() : note.updatedAt,\n };\n};\n\nconst isNonNegativeInteger = (value: unknown): value is number => {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n};\n\nconst isPositiveInteger = (value: unknown): value is number => {\n return typeof value === 'number' && Number.isInteger(value) && value > 0;\n};\n\nconst resolveMarkdownWritePolicy = (value: unknown): MarkdownChangePolicy | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'Markdown write policy must be an object.');\n }\n\n const policy: MarkdownChangePolicy = {};\n\n if (value.allowNoop !== undefined) {\n if (typeof value.allowNoop !== 'boolean') {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'allowNoop must be a boolean.');\n }\n policy.allowNoop = value.allowNoop;\n }\n\n if (value.maxChangedChars !== undefined) {\n if (!isNonNegativeInteger(value.maxChangedChars)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'maxChangedChars must be a non-negative integer.');\n }\n policy.maxChangedChars = value.maxChangedChars;\n }\n\n if (value.maxChangedLines !== undefined) {\n if (!isNonNegativeInteger(value.maxChangedLines)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'maxChangedLines must be a non-negative integer.');\n }\n policy.maxChangedLines = value.maxChangedLines;\n }\n\n if (value.diffPreviewMaxChars !== undefined) {\n if (!isNonNegativeInteger(value.diffPreviewMaxChars)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'diffPreviewMaxChars must be a non-negative integer.');\n }\n policy.diffPreviewMaxChars = value.diffPreviewMaxChars;\n }\n\n if (value.preserveTags !== undefined) {\n if (!PRESERVATION_POLICIES.has(value.preserveTags)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'preserveTags must be boolean or warn.');\n }\n policy.preserveTags = value.preserveTags as MarkdownChangePolicy['preserveTags'];\n }\n\n if (value.preserveReferences !== undefined) {\n if (!PRESERVATION_POLICIES.has(value.preserveReferences)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'preserveReferences must be boolean or warn.');\n }\n policy.preserveReferences = value.preserveReferences as MarkdownChangePolicy['preserveReferences'];\n }\n\n return policy;\n};\n\nconst resolvePatchSelector = (value: unknown): MarkdownPatchSelector => {\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Patch selector must be an object.');\n }\n\n if (value.type === 'exact_text') {\n if (typeof value.text !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Exact text selector requires text.');\n }\n\n return {\n type: 'exact_text',\n text: value.text,\n ...(value.before !== undefined\n ? {\n before: resolveOptionalString(\n value.before,\n 'INVALID_PATCH_SELECTOR',\n 'Selector before must be a string.',\n ),\n }\n : {}),\n ...(value.after !== undefined\n ? {\n after: resolveOptionalString(\n value.after,\n 'INVALID_PATCH_SELECTOR',\n 'Selector after must be a string.',\n ),\n }\n : {}),\n };\n }\n\n if (value.type === 'match_candidate') {\n const { text, matchIndex, lineStart, matchSha256, surroundingHash, positionHint } = value;\n\n if (\n typeof text !== 'string' ||\n !isNonNegativeInteger(matchIndex) ||\n !isPositiveInteger(lineStart) ||\n typeof matchSha256 !== 'string' ||\n typeof surroundingHash !== 'string'\n ) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Match candidate selector has invalid fields.');\n }\n\n if (positionHint !== undefined && !POSITION_HINTS.has(String(positionHint))) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'positionHint must be first or last.');\n }\n\n return {\n type: 'match_candidate',\n text,\n matchIndex,\n lineStart,\n matchSha256,\n surroundingHash,\n ...(positionHint ? { positionHint: positionHint as 'first' | 'last' } : {}),\n };\n }\n\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Unsupported patch selector type.');\n};\n\nconst resolvePatchOperation = (value: unknown): MarkdownPatchOperation => {\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Patch operation must be an object.');\n }\n\n if (value.type === 'replace') {\n if (typeof value.replacement !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Replace operation requires replacement.');\n }\n\n return {\n type: 'replace',\n replacement: value.replacement,\n };\n }\n\n if (value.type === 'insert_before' || value.type === 'insert_after') {\n if (typeof value.insertion !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Insert operation requires insertion.');\n }\n\n return {\n type: value.type,\n insertion: value.insertion,\n };\n }\n\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Unsupported patch operation type.');\n};\n\nconst resolveAppendPlacement = (value: unknown): MarkdownAppendPlacement | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Append placement must be an object.');\n }\n\n if (value.type === 'end') {\n return { type: 'end' };\n }\n\n if (value.type === 'after_heading') {\n if (typeof value.heading !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'after_heading placement requires heading.');\n }\n\n const { heading, level } = value;\n\n if (typeof heading !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'after_heading placement requires heading.');\n }\n\n if (level !== undefined && (typeof level !== 'number' || !Number.isInteger(level) || level < 1 || level > 6)) {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Heading level must be an integer from 1 to 6.');\n }\n\n return {\n type: 'after_heading',\n heading,\n ...(level !== undefined ? { level } : {}),\n };\n }\n\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Unsupported append placement type.');\n};\n\nconst resolveSeparator = (value: unknown) => {\n if (value === undefined) {\n return undefined;\n }\n\n if (value === '\\n' || value === '\\n\\n') {\n return value;\n }\n\n throw createAppError(400, 'INVALID_SEPARATOR', 'Separator must be \"\\\\n\" or \"\\\\n\\\\n\".');\n};\n\ntype EmitServerEvent = (event: ServerEventInput) => unknown;\n\nexport const createMcpCreateNoteHandler = (\n createNote = createNoteFromMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { title, markdown, layout } = req.body ?? {};\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'A note title is required.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n try {\n const note = await createNote({\n title,\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n });\n\n emitEvent({\n type: 'mcp.note.created',\n source: 'mcp',\n noteId: note.id,\n updatedAt: note.updatedAt,\n });\n\n res.status(200)\n .json({\n created: true,\n note,\n })\n .end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpUpdateNoteHandler = (\n updateNote = updateNoteFromMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, title, markdown, layout, editSessionId } = req.body ?? {};\n const noteId = Number(id);\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (!Number.isInteger(noteId) || noteId <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n if (title !== undefined && typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'Note title must be a string.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n if (editSessionId !== undefined && typeof editSessionId !== 'string') {\n throw createAppError(400, 'INVALID_EDIT_SESSION_ID', 'Edit session id must be a string.');\n }\n\n if (title === undefined && markdown === undefined && layout === undefined) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', 'At least one note field must be provided for update.');\n }\n\n try {\n const note = await updateNote({\n id: noteId,\n ...(title !== undefined ? { title } : {}),\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n ...(editSessionId !== undefined ? { editSessionId } : {}),\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n\n if (!note) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: note.id,\n updatedAt: note.updatedAt,\n });\n\n res.status(200)\n .json({\n updated: true,\n note,\n })\n .end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpNoteWriteBaselineHandler = (\n findNoteBaseline: (id: number) => Promise<{ id: number; updatedAt: Date } | null> = async (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n id: true,\n updatedAt: true,\n },\n }),\n): Controller => {\n return async (req, res) => {\n const noteId = resolvePositiveNoteId(req.body?.id);\n const note = await findNoteBaseline(noteId);\n\n if (!note) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n res.status(200)\n .json({\n note: serializeNoteWriteBaseline(note),\n })\n .end();\n };\n};\n\nexport const createMcpPatchNoteMarkdownHandler = (\n patchMarkdown = patchNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, selector, operation, policy, dryRun } =\n req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n const resolvedDryRun = resolveOptionalBoolean(dryRun, 'INVALID_DRY_RUN', 'dryRun must be a boolean.');\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_INTENT', 'Patch intent must be a string.');\n }\n\n const result = await patchMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n selector: resolvePatchSelector(selector),\n operation: resolvePatchOperation(operation),\n policy: resolveMarkdownWritePolicy(policy),\n dryRun: resolvedDryRun,\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpAppendNoteMarkdownHandler = (\n appendMarkdown = appendNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, insertion, placement, separator, policy, dryRun } =\n req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n const resolvedDryRun = resolveOptionalBoolean(dryRun, 'INVALID_DRY_RUN', 'dryRun must be a boolean.');\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_INTENT', 'Append intent must be a string.');\n }\n\n if (typeof insertion !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_INSERTION', 'Append insertion must be a string.');\n }\n\n const result = await appendMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n insertion,\n placement: resolveAppendPlacement(placement),\n separator: resolveSeparator(separator),\n policy: resolveMarkdownWritePolicy(policy),\n dryRun: resolvedDryRun,\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpReplaceNoteMarkdownHandler = (\n replaceMarkdown = replaceNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, replacement, policy, dryRun } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n const resolvedDryRun = resolveOptionalBoolean(dryRun, 'INVALID_DRY_RUN', 'dryRun must be a boolean.');\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_REPLACE_INTENT', 'Replace intent must be a string.');\n }\n\n if (typeof replacement !== 'string') {\n throw createAppError(400, 'INVALID_REPLACE_MARKDOWN', 'Replacement markdown must be a string.');\n }\n\n const result = await replaceMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n replacement,\n policy: resolveMarkdownWritePolicy(policy),\n dryRun: resolvedDryRun,\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpUpdateNoteMetadataHandler = (\n updateMetadata = updateNoteMetadata,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, title, layout, properties, dryRun } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedLayout = resolveNoteLayout(layout);\n const resolvedProperties = resolveMetadataPropertyPatch(properties);\n const resolvedDryRun = resolveOptionalBoolean(dryRun, 'INVALID_DRY_RUN', 'dryRun must be a boolean.');\n\n if (typeof expectedUpdatedAt !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_VERSION', 'expectedUpdatedAt must be a string.');\n }\n\n if (title !== undefined && typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'Note title must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n if (title === undefined && layout === undefined && resolvedProperties === undefined) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', 'At least one metadata field must be provided.');\n }\n\n const result = await updateMetadata({\n id: noteId,\n expectedUpdatedAt,\n ...(title !== undefined ? { title } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n ...(resolvedProperties !== undefined ? { properties: resolvedProperties } : {}),\n dryRun: resolvedDryRun,\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpDeleteNoteHandler = (\n deleteNote = deleteNoteById,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const id = Number(req.body?.id);\n\n if (!Number.isInteger(id) || id <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n const deletedNote = await deleteNote(id);\n\n if (!deletedNote) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n emitEvent({\n type: 'mcp.note.deleted',\n source: 'mcp',\n noteId: deletedNote.id,\n });\n\n res.status(200)\n .json({\n deleted: true,\n note: deletedNote,\n })\n .end();\n };\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/features/note/http/mcp.ts"],"sourcesContent":["import {\n createNoteFromMarkdown,\n InvalidNoteAuthoringInputError,\n updateNoteFromMarkdown,\n} from '~/features/note/services/authoring.js';\nimport { deleteNoteById } from '~/features/note/services/cleanup.js';\nimport {\n appendNoteMarkdown,\n patchNoteMarkdown,\n replaceNoteMarkdown,\n updateNoteMetadata,\n} from '~/features/note/services/markdown-intent-write.js';\nimport type {\n MarkdownAppendPlacement,\n MarkdownChangePolicy,\n MarkdownPatchOperation,\n MarkdownPatchSelector,\n} from '~/features/note/services/markdown-patch.js';\nimport type { NotePropertiesByKeyPatchInput } from '~/features/note/services/properties.js';\nimport { MCP_SNAPSHOT_META } from '~/features/note/services/snapshot.js';\nimport models, { type NoteLayout } from '~/models.js';\nimport { createAppError } from '~/modules/error-handler.js';\nimport { emitServerEvent, type ServerEventInput } from '~/modules/server-events.js';\nimport type { Controller } from '~/types/index.js';\n\nconst NOTE_LAYOUTS = new Set<NoteLayout>(['narrow', 'wide', 'full']);\n\nconst POSITION_HINTS = new Set(['first', 'last']);\nconst PRESERVATION_POLICIES = new Set<unknown>([true, false, 'warn']);\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst resolveNoteLayout = (value: unknown): NoteLayout | null | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'string' && NOTE_LAYOUTS.has(value as NoteLayout)) {\n return value as NoteLayout;\n }\n\n return null;\n};\n\nconst resolveOptionalString = (value: unknown, code: string, message: string) => {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'string') {\n return value;\n }\n\n throw createAppError(400, code, message);\n};\n\nconst resolveMetadataPropertyPatch = (value: unknown): NotePropertiesByKeyPatchInput | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Properties patch must be an object.');\n }\n\n const set =\n value.set === undefined\n ? undefined\n : Array.isArray(value.set)\n ? value.set.map((item) => {\n if (!isRecord(item) || typeof item.key !== 'string' || typeof item.value !== 'string') {\n throw createAppError(\n 400,\n 'INVALID_NOTE_PROPERTIES',\n 'Property set items must include string key and value fields.',\n );\n }\n\n return {\n key: item.key,\n value: item.value,\n };\n })\n : null;\n\n if (set === null) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property set must be an array.');\n }\n\n const deleteKeys =\n value.deleteKeys === undefined\n ? undefined\n : Array.isArray(value.deleteKeys)\n ? value.deleteKeys.map((key) => {\n if (typeof key !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property deleteKeys must be strings.');\n }\n\n return key;\n })\n : null;\n\n if (deleteKeys === null) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property deleteKeys must be an array.');\n }\n\n return {\n ...(set !== undefined ? { set } : {}),\n ...(deleteKeys !== undefined ? { deleteKeys } : {}),\n };\n};\n\nconst resolvePositiveNoteId = (value: unknown) => {\n const noteId = Number(value);\n\n if (!Number.isInteger(noteId) || noteId <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n return noteId;\n};\n\nconst serializeNoteWriteBaseline = (note: { id: number | string; updatedAt: Date | string }) => {\n return {\n id: String(note.id),\n updatedAt: note.updatedAt instanceof Date ? note.updatedAt.toISOString() : note.updatedAt,\n };\n};\n\nconst isNonNegativeInteger = (value: unknown): value is number => {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n};\n\nconst isPositiveInteger = (value: unknown): value is number => {\n return typeof value === 'number' && Number.isInteger(value) && value > 0;\n};\n\nconst resolveMarkdownWritePolicy = (value: unknown): MarkdownChangePolicy | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'Markdown write policy must be an object.');\n }\n\n const policy: MarkdownChangePolicy = {};\n\n if (value.allowNoop !== undefined) {\n if (typeof value.allowNoop !== 'boolean') {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'allowNoop must be a boolean.');\n }\n policy.allowNoop = value.allowNoop;\n }\n\n if (value.preserveTags !== undefined) {\n if (!PRESERVATION_POLICIES.has(value.preserveTags)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'preserveTags must be boolean or warn.');\n }\n policy.preserveTags = value.preserveTags as MarkdownChangePolicy['preserveTags'];\n }\n\n if (value.preserveReferences !== undefined) {\n if (!PRESERVATION_POLICIES.has(value.preserveReferences)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'preserveReferences must be boolean or warn.');\n }\n policy.preserveReferences = value.preserveReferences as MarkdownChangePolicy['preserveReferences'];\n }\n\n return policy;\n};\n\nconst resolvePatchSelector = (value: unknown): MarkdownPatchSelector => {\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Patch selector must be an object.');\n }\n\n if (value.type === 'exact_text') {\n if (typeof value.text !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Exact text selector requires text.');\n }\n\n return {\n type: 'exact_text',\n text: value.text,\n ...(value.before !== undefined\n ? {\n before: resolveOptionalString(\n value.before,\n 'INVALID_PATCH_SELECTOR',\n 'Selector before must be a string.',\n ),\n }\n : {}),\n ...(value.after !== undefined\n ? {\n after: resolveOptionalString(\n value.after,\n 'INVALID_PATCH_SELECTOR',\n 'Selector after must be a string.',\n ),\n }\n : {}),\n };\n }\n\n if (value.type === 'match_candidate') {\n const { text, matchIndex, lineStart, matchSha256, surroundingHash, positionHint } = value;\n\n if (\n typeof text !== 'string' ||\n !isNonNegativeInteger(matchIndex) ||\n !isPositiveInteger(lineStart) ||\n typeof matchSha256 !== 'string' ||\n typeof surroundingHash !== 'string'\n ) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Match candidate selector has invalid fields.');\n }\n\n if (positionHint !== undefined && !POSITION_HINTS.has(String(positionHint))) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'positionHint must be first or last.');\n }\n\n return {\n type: 'match_candidate',\n text,\n matchIndex,\n lineStart,\n matchSha256,\n surroundingHash,\n ...(positionHint ? { positionHint: positionHint as 'first' | 'last' } : {}),\n };\n }\n\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Unsupported patch selector type.');\n};\n\nconst resolvePatchOperation = (value: unknown): MarkdownPatchOperation => {\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Patch operation must be an object.');\n }\n\n if (value.type === 'replace') {\n if (typeof value.replacement !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Replace operation requires replacement.');\n }\n\n return {\n type: 'replace',\n replacement: value.replacement,\n };\n }\n\n if (value.type === 'insert_before' || value.type === 'insert_after') {\n if (typeof value.insertion !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Insert operation requires insertion.');\n }\n\n return {\n type: value.type,\n insertion: value.insertion,\n };\n }\n\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Unsupported patch operation type.');\n};\n\nconst resolveAppendPlacement = (value: unknown): MarkdownAppendPlacement | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Append placement must be an object.');\n }\n\n if (value.type === 'end') {\n return { type: 'end' };\n }\n\n if (value.type === 'after_heading') {\n if (typeof value.heading !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'after_heading placement requires heading.');\n }\n\n const { heading, level } = value;\n\n if (typeof heading !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'after_heading placement requires heading.');\n }\n\n if (level !== undefined && (typeof level !== 'number' || !Number.isInteger(level) || level < 1 || level > 6)) {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Heading level must be an integer from 1 to 6.');\n }\n\n return {\n type: 'after_heading',\n heading,\n ...(level !== undefined ? { level } : {}),\n };\n }\n\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Unsupported append placement type.');\n};\n\nconst resolveSeparator = (value: unknown) => {\n if (value === undefined) {\n return undefined;\n }\n\n if (value === '\\n' || value === '\\n\\n') {\n return value;\n }\n\n throw createAppError(400, 'INVALID_SEPARATOR', 'Separator must be \"\\\\n\" or \"\\\\n\\\\n\".');\n};\n\ntype EmitServerEvent = (event: ServerEventInput) => unknown;\n\nexport const createMcpCreateNoteHandler = (\n createNote = createNoteFromMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { title, markdown, layout } = req.body ?? {};\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'A note title is required.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n try {\n const note = await createNote({\n title,\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n });\n\n emitEvent({\n type: 'mcp.note.created',\n source: 'mcp',\n noteId: note.id,\n updatedAt: note.updatedAt,\n });\n\n res.status(200)\n .json({\n created: true,\n note,\n })\n .end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpUpdateNoteHandler = (\n updateNote = updateNoteFromMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, title, markdown, layout, editSessionId } = req.body ?? {};\n const noteId = Number(id);\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (!Number.isInteger(noteId) || noteId <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n if (title !== undefined && typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'Note title must be a string.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n if (editSessionId !== undefined && typeof editSessionId !== 'string') {\n throw createAppError(400, 'INVALID_EDIT_SESSION_ID', 'Edit session id must be a string.');\n }\n\n if (title === undefined && markdown === undefined && layout === undefined) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', 'At least one note field must be provided for update.');\n }\n\n try {\n const note = await updateNote({\n id: noteId,\n ...(title !== undefined ? { title } : {}),\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n ...(editSessionId !== undefined ? { editSessionId } : {}),\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n\n if (!note) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: note.id,\n updatedAt: note.updatedAt,\n });\n\n res.status(200)\n .json({\n updated: true,\n note,\n })\n .end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpNoteWriteBaselineHandler = (\n findNoteBaseline: (id: number) => Promise<{ id: number; updatedAt: Date } | null> = async (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n id: true,\n updatedAt: true,\n },\n }),\n): Controller => {\n return async (req, res) => {\n const noteId = resolvePositiveNoteId(req.body?.id);\n const note = await findNoteBaseline(noteId);\n\n if (!note) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n res.status(200)\n .json({\n note: serializeNoteWriteBaseline(note),\n })\n .end();\n };\n};\n\nexport const createMcpPatchNoteMarkdownHandler = (\n patchMarkdown = patchNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, selector, operation, policy } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_INTENT', 'Patch intent must be a string.');\n }\n\n const result = await patchMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n selector: resolvePatchSelector(selector),\n operation: resolvePatchOperation(operation),\n policy: resolveMarkdownWritePolicy(policy),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpAppendNoteMarkdownHandler = (\n appendMarkdown = appendNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, insertion, placement, separator, policy } =\n req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_INTENT', 'Append intent must be a string.');\n }\n\n if (typeof insertion !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_INSERTION', 'Append insertion must be a string.');\n }\n\n const result = await appendMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n insertion,\n placement: resolveAppendPlacement(placement),\n separator: resolveSeparator(separator),\n policy: resolveMarkdownWritePolicy(policy),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpReplaceNoteMarkdownHandler = (\n replaceMarkdown = replaceNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, replacement, policy } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_REPLACE_INTENT', 'Replace intent must be a string.');\n }\n\n if (typeof replacement !== 'string') {\n throw createAppError(400, 'INVALID_REPLACE_MARKDOWN', 'Replacement markdown must be a string.');\n }\n\n const result = await replaceMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n replacement,\n policy: resolveMarkdownWritePolicy(policy),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpUpdateNoteMetadataHandler = (\n updateMetadata = updateNoteMetadata,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, title, layout, properties } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedLayout = resolveNoteLayout(layout);\n const resolvedProperties = resolveMetadataPropertyPatch(properties);\n\n if (typeof expectedUpdatedAt !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_VERSION', 'expectedUpdatedAt must be a string.');\n }\n\n if (title !== undefined && typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'Note title must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n if (title === undefined && layout === undefined && resolvedProperties === undefined) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', 'At least one metadata field must be provided.');\n }\n\n const result = await updateMetadata({\n id: noteId,\n expectedUpdatedAt,\n ...(title !== undefined ? { title } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n ...(resolvedProperties !== undefined ? { properties: resolvedProperties } : {}),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpDeleteNoteHandler = (\n deleteNote = deleteNoteById,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const id = Number(req.body?.id);\n\n if (!Number.isInteger(id) || id <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n const deletedNote = await deleteNote(id);\n\n if (!deletedNote) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n emitEvent({\n type: 'mcp.note.deleted',\n source: 'mcp',\n noteId: deletedNote.id,\n });\n\n res.status(200)\n .json({\n deleted: true,\n note: deletedNote,\n })\n .end();\n };\n};\n"],"mappings":"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","names":[]}
@@ -76,8 +76,8 @@ const createNoteCleanupService = (deps) => {
76
76
  })),
77
77
  orphanedTagNames: orphanedTags.map((tag) => tag.name),
78
78
  orphanedTagIds: orphanedTags.map((tag) => tag.id),
79
- requiresForce: candidate.requiresForce || orphanedTags.length > 0,
80
- forceReasons: [...candidate.forceReasons, ...orphanedTags.length > 0 ? ["orphan_tags"] : []]
79
+ requiresForce: candidate.requiresForce,
80
+ forceReasons: candidate.forceReasons
81
81
  };
82
82
  };
83
83
  const getDeletePreview = async (id) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/features/note/services/cleanup.ts"],"sourcesContent":["import models from '~/models.js';\nimport { contentReferencesNote } from './content-blocks.js';\nimport { trashNoteById as moveNoteToTrashById } from './trash.js';\n\nexport interface NoteCleanupBackReference {\n id: string;\n title: string;\n}\n\nexport interface NoteCleanupPreview {\n id: string;\n title: string;\n updatedAt: string;\n pinned: boolean;\n tagNames: string[];\n reminderCount: number;\n backReferences: NoteCleanupBackReference[];\n orphanedTagNames: string[];\n requiresForce: boolean;\n forceReasons: string[];\n}\n\nexport interface NoteCleanupCandidate {\n id: string;\n title: string;\n updatedAt: string;\n pinned: boolean;\n tagNames: string[];\n reminderCount: number;\n backReferenceCount: number;\n matchedTerms: string[];\n reasons: string[];\n requiresForce: boolean;\n forceReasons: string[];\n}\n\nexport interface NoteCleanupCandidatesResult {\n keywords: string[];\n limit: number;\n offset: number;\n olderThanDays: number;\n notes: NoteCleanupCandidate[];\n totalCount: number;\n}\n\nexport interface NoteCleanupService {\n deleteNoteById: (id: number) => Promise<NoteCleanupPreview | null>;\n getDeletePreview: (id: number) => Promise<NoteCleanupPreview | null>;\n listCleanupCandidates: (input?: {\n keywords?: string[];\n limit?: number;\n offset?: number;\n olderThanDays?: number;\n query?: string;\n }) => Promise<NoteCleanupCandidatesResult>;\n}\n\ninterface NoteRecord {\n id: number;\n title: string;\n content: string;\n updatedAt: Date;\n pinned: boolean;\n tags: Array<{\n id: number;\n name: string;\n }>;\n}\n\ninterface NoteCleanupPreviewInternal extends NoteCleanupPreview {\n orphanedTagIds: number[];\n}\n\nconst DEFAULT_CLEANUP_KEYWORDS = ['temp', 'temporary', 'tmp', 'draft', 'test', 'wip'];\n\nexport const normalizeCleanupKeywords = (keywords?: string[], query?: string) => {\n const queryKeywords = typeof query === 'string' ? query.split(/[,\\s]+/).map((keyword) => keyword.trim()) : [];\n const source =\n keywords && keywords.length > 0\n ? keywords\n : queryKeywords.length > 0\n ? queryKeywords\n : DEFAULT_CLEANUP_KEYWORDS;\n\n return Array.from(new Set(source.map((keyword) => keyword.trim().toLowerCase()).filter(Boolean)));\n};\n\nconst buildForceReasons = (input: { pinned: boolean; reminderCount: number; backReferenceCount: number }) => {\n const forceReasons: string[] = [];\n\n if (input.pinned) {\n forceReasons.push('note_is_pinned');\n }\n\n if (input.reminderCount > 0) {\n forceReasons.push('has_reminders');\n }\n\n if (input.backReferenceCount > 0) {\n forceReasons.push('has_back_references');\n }\n\n return forceReasons;\n};\n\nexport const createNoteCleanupService = (deps: {\n countCandidateNotes: (keywords: string[], olderThanDays: number) => Promise<number>;\n countReminders: (noteId: number) => Promise<number>;\n deleteNoteAndPruneTags: (noteId: number, orphanTagIds: number[]) => Promise<void>;\n findBackReferences: (noteId: number) => Promise<Array<{ id: number; title: string }>>;\n findCandidateNotes: (\n keywords: string[],\n skip: number,\n take: number,\n olderThanDays: number,\n ) => Promise<NoteRecord[]>;\n findNote: (id: number) => Promise<NoteRecord | null>;\n getTagNoteCounts: (tagIds: number[]) => Promise<Map<number, number>>;\n}): NoteCleanupService => {\n const buildCandidate = async (note: NoteRecord, matchedTerms?: string[]): Promise<NoteCleanupCandidate> => {\n const [reminderCount, backReferences] = await Promise.all([\n deps.countReminders(note.id),\n deps.findBackReferences(note.id),\n ]);\n const normalizedMatchedTerms = matchedTerms ?? [];\n const forceReasons = buildForceReasons({\n pinned: note.pinned,\n reminderCount,\n backReferenceCount: backReferences.length,\n });\n\n return {\n id: String(note.id),\n title: note.title,\n updatedAt: note.updatedAt.toISOString(),\n pinned: note.pinned,\n tagNames: note.tags.map((tag) => tag.name),\n reminderCount,\n backReferenceCount: backReferences.length,\n matchedTerms: normalizedMatchedTerms,\n reasons: [\n ...(normalizedMatchedTerms.length > 0 ? [`matched_terms:${normalizedMatchedTerms.join(',')}`] : []),\n ...(note.pinned ? [] : ['not_pinned']),\n ...(note.tags.length === 0 ? ['tagless'] : []),\n ...(reminderCount === 0 ? ['no_reminders'] : []),\n ...(backReferences.length === 0 ? ['no_back_references'] : []),\n ],\n requiresForce: forceReasons.length > 0,\n forceReasons,\n };\n };\n\n const getDeletePreviewInternal = async (id: number): Promise<NoteCleanupPreviewInternal | null> => {\n const note = await deps.findNote(id);\n\n if (!note) {\n return null;\n }\n\n const [candidate, backReferences, tagNoteCounts] = await Promise.all([\n buildCandidate(note),\n deps.findBackReferences(note.id),\n deps.getTagNoteCounts(note.tags.map((tag) => tag.id)),\n ]);\n\n const orphanedTags = note.tags.filter((tag) => (tagNoteCounts.get(tag.id) ?? 0) <= 1);\n\n return {\n id: candidate.id,\n title: candidate.title,\n updatedAt: candidate.updatedAt,\n pinned: candidate.pinned,\n tagNames: candidate.tagNames,\n reminderCount: candidate.reminderCount,\n backReferences: backReferences.map((backReference) => ({\n id: String(backReference.id),\n title: backReference.title,\n })),\n orphanedTagNames: orphanedTags.map((tag) => tag.name),\n orphanedTagIds: orphanedTags.map((tag) => tag.id),\n requiresForce: candidate.requiresForce || orphanedTags.length > 0,\n forceReasons: [...candidate.forceReasons, ...(orphanedTags.length > 0 ? ['orphan_tags'] : [])],\n };\n };\n\n const getDeletePreview = async (id: number): Promise<NoteCleanupPreview | null> => {\n const preview = await getDeletePreviewInternal(id);\n\n if (!preview) {\n return null;\n }\n\n const { orphanedTagIds, ...publicPreview } = preview;\n void orphanedTagIds;\n return publicPreview;\n };\n\n return {\n listCleanupCandidates: async (input = {}) => {\n const limit = Math.max(1, Number(input.limit ?? 20));\n const offset = Math.max(0, Number(input.offset ?? 0));\n const olderThanDays = Math.max(0, Number(input.olderThanDays ?? 0));\n const keywords = normalizeCleanupKeywords(input.keywords, input.query);\n\n const [totalCount, notes] = await Promise.all([\n deps.countCandidateNotes(keywords, olderThanDays),\n deps.findCandidateNotes(keywords, offset, limit, olderThanDays),\n ]);\n\n const candidates = await Promise.all(\n notes.map((note) => {\n const haystack = `${note.title}\\n${note.content}`.toLowerCase();\n const matchedTerms = keywords.filter((keyword) => haystack.includes(keyword));\n return buildCandidate(note, matchedTerms);\n }),\n );\n\n return {\n keywords,\n limit,\n offset,\n olderThanDays,\n notes: candidates,\n totalCount,\n };\n },\n getDeletePreview,\n deleteNoteById: async (id: number) => {\n const preview = await getDeletePreviewInternal(id);\n\n if (!preview) {\n return null;\n }\n\n await deps.deleteNoteAndPruneTags(id, preview.orphanedTagIds);\n\n const { orphanedTagIds, ...publicPreview } = preview;\n void orphanedTagIds;\n return publicPreview;\n },\n };\n};\n\nconst buildKeywordClauses = (keywords: string[]) => {\n return keywords.flatMap((keyword) => [{ title: { contains: keyword } }, { content: { contains: keyword } }]);\n};\n\nconst buildStaleWhere = (olderThanDays: number) => {\n if (olderThanDays <= 0) {\n return {};\n }\n\n return { updatedAt: { lte: new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000) } };\n};\n\nconst noteCleanupService = createNoteCleanupService({\n countCandidateNotes: (keywords: string[], olderThanDays: number) =>\n models.note.count({\n where: {\n ...buildStaleWhere(olderThanDays),\n OR: buildKeywordClauses(keywords),\n },\n }),\n countReminders: (noteId: number) => models.reminder.count({ where: { noteId } }),\n deleteNoteAndPruneTags: async (noteId: number, orphanTagIds: number[]) => {\n void orphanTagIds;\n await moveNoteToTrashById(noteId);\n },\n findBackReferences: (noteId: number) =>\n models.note\n .findMany({\n select: {\n id: true,\n title: true,\n content: true,\n },\n where: {\n NOT: { id: noteId },\n },\n orderBy: [{ pinned: 'desc' }, { updatedAt: 'desc' }],\n })\n .then((notes) => notes.filter((note) => contentReferencesNote(note.content, noteId))),\n findCandidateNotes: (keywords: string[], skip: number, take: number, olderThanDays: number) =>\n models.note.findMany({\n include: { tags: true },\n orderBy: { updatedAt: 'asc' },\n skip,\n take,\n where: {\n ...buildStaleWhere(olderThanDays),\n OR: buildKeywordClauses(keywords),\n },\n }),\n findNote: (id: number) =>\n models.note.findUnique({\n where: { id },\n include: { tags: true },\n }),\n getTagNoteCounts: async (tagIds: number[]) => {\n if (tagIds.length === 0) {\n return new Map<number, number>();\n }\n\n const tags = await models.tag.findMany({\n where: { id: { in: tagIds } },\n include: { _count: { select: { notes: true } } },\n });\n\n return new Map(tags.map((tag) => [tag.id, tag._count.notes]));\n },\n});\n\nexport const listNoteCleanupCandidates = noteCleanupService.listCleanupCandidates;\nexport const getNoteCleanupPreview = noteCleanupService.getDeletePreview;\nexport const deleteNoteById = noteCleanupService.deleteNoteById;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/features/note/services/cleanup.ts"],"sourcesContent":["import models from '~/models.js';\nimport { contentReferencesNote } from './content-blocks.js';\nimport { trashNoteById as moveNoteToTrashById } from './trash.js';\n\nexport interface NoteCleanupBackReference {\n id: string;\n title: string;\n}\n\nexport interface NoteCleanupPreview {\n id: string;\n title: string;\n updatedAt: string;\n pinned: boolean;\n tagNames: string[];\n reminderCount: number;\n backReferences: NoteCleanupBackReference[];\n orphanedTagNames: string[];\n requiresForce: boolean;\n forceReasons: string[];\n}\n\nexport interface NoteCleanupCandidate {\n id: string;\n title: string;\n updatedAt: string;\n pinned: boolean;\n tagNames: string[];\n reminderCount: number;\n backReferenceCount: number;\n matchedTerms: string[];\n reasons: string[];\n requiresForce: boolean;\n forceReasons: string[];\n}\n\nexport interface NoteCleanupCandidatesResult {\n keywords: string[];\n limit: number;\n offset: number;\n olderThanDays: number;\n notes: NoteCleanupCandidate[];\n totalCount: number;\n}\n\nexport interface NoteCleanupService {\n deleteNoteById: (id: number) => Promise<NoteCleanupPreview | null>;\n getDeletePreview: (id: number) => Promise<NoteCleanupPreview | null>;\n listCleanupCandidates: (input?: {\n keywords?: string[];\n limit?: number;\n offset?: number;\n olderThanDays?: number;\n query?: string;\n }) => Promise<NoteCleanupCandidatesResult>;\n}\n\ninterface NoteRecord {\n id: number;\n title: string;\n content: string;\n updatedAt: Date;\n pinned: boolean;\n tags: Array<{\n id: number;\n name: string;\n }>;\n}\n\ninterface NoteCleanupPreviewInternal extends NoteCleanupPreview {\n orphanedTagIds: number[];\n}\n\nconst DEFAULT_CLEANUP_KEYWORDS = ['temp', 'temporary', 'tmp', 'draft', 'test', 'wip'];\n\nexport const normalizeCleanupKeywords = (keywords?: string[], query?: string) => {\n const queryKeywords = typeof query === 'string' ? query.split(/[,\\s]+/).map((keyword) => keyword.trim()) : [];\n const source =\n keywords && keywords.length > 0\n ? keywords\n : queryKeywords.length > 0\n ? queryKeywords\n : DEFAULT_CLEANUP_KEYWORDS;\n\n return Array.from(new Set(source.map((keyword) => keyword.trim().toLowerCase()).filter(Boolean)));\n};\n\nconst buildForceReasons = (input: { pinned: boolean; reminderCount: number; backReferenceCount: number }) => {\n const forceReasons: string[] = [];\n\n if (input.pinned) {\n forceReasons.push('note_is_pinned');\n }\n\n if (input.reminderCount > 0) {\n forceReasons.push('has_reminders');\n }\n\n if (input.backReferenceCount > 0) {\n forceReasons.push('has_back_references');\n }\n\n return forceReasons;\n};\n\nexport const createNoteCleanupService = (deps: {\n countCandidateNotes: (keywords: string[], olderThanDays: number) => Promise<number>;\n countReminders: (noteId: number) => Promise<number>;\n deleteNoteAndPruneTags: (noteId: number, orphanTagIds: number[]) => Promise<void>;\n findBackReferences: (noteId: number) => Promise<Array<{ id: number; title: string }>>;\n findCandidateNotes: (\n keywords: string[],\n skip: number,\n take: number,\n olderThanDays: number,\n ) => Promise<NoteRecord[]>;\n findNote: (id: number) => Promise<NoteRecord | null>;\n getTagNoteCounts: (tagIds: number[]) => Promise<Map<number, number>>;\n}): NoteCleanupService => {\n const buildCandidate = async (note: NoteRecord, matchedTerms?: string[]): Promise<NoteCleanupCandidate> => {\n const [reminderCount, backReferences] = await Promise.all([\n deps.countReminders(note.id),\n deps.findBackReferences(note.id),\n ]);\n const normalizedMatchedTerms = matchedTerms ?? [];\n const forceReasons = buildForceReasons({\n pinned: note.pinned,\n reminderCount,\n backReferenceCount: backReferences.length,\n });\n\n return {\n id: String(note.id),\n title: note.title,\n updatedAt: note.updatedAt.toISOString(),\n pinned: note.pinned,\n tagNames: note.tags.map((tag) => tag.name),\n reminderCount,\n backReferenceCount: backReferences.length,\n matchedTerms: normalizedMatchedTerms,\n reasons: [\n ...(normalizedMatchedTerms.length > 0 ? [`matched_terms:${normalizedMatchedTerms.join(',')}`] : []),\n ...(note.pinned ? [] : ['not_pinned']),\n ...(note.tags.length === 0 ? ['tagless'] : []),\n ...(reminderCount === 0 ? ['no_reminders'] : []),\n ...(backReferences.length === 0 ? ['no_back_references'] : []),\n ],\n requiresForce: forceReasons.length > 0,\n forceReasons,\n };\n };\n\n const getDeletePreviewInternal = async (id: number): Promise<NoteCleanupPreviewInternal | null> => {\n const note = await deps.findNote(id);\n\n if (!note) {\n return null;\n }\n\n const [candidate, backReferences, tagNoteCounts] = await Promise.all([\n buildCandidate(note),\n deps.findBackReferences(note.id),\n deps.getTagNoteCounts(note.tags.map((tag) => tag.id)),\n ]);\n\n const orphanedTags = note.tags.filter((tag) => (tagNoteCounts.get(tag.id) ?? 0) <= 1);\n\n return {\n id: candidate.id,\n title: candidate.title,\n updatedAt: candidate.updatedAt,\n pinned: candidate.pinned,\n tagNames: candidate.tagNames,\n reminderCount: candidate.reminderCount,\n backReferences: backReferences.map((backReference) => ({\n id: String(backReference.id),\n title: backReference.title,\n })),\n orphanedTagNames: orphanedTags.map((tag) => tag.name),\n orphanedTagIds: orphanedTags.map((tag) => tag.id),\n requiresForce: candidate.requiresForce,\n forceReasons: candidate.forceReasons,\n };\n };\n\n const getDeletePreview = async (id: number): Promise<NoteCleanupPreview | null> => {\n const preview = await getDeletePreviewInternal(id);\n\n if (!preview) {\n return null;\n }\n\n const { orphanedTagIds, ...publicPreview } = preview;\n void orphanedTagIds;\n return publicPreview;\n };\n\n return {\n listCleanupCandidates: async (input = {}) => {\n const limit = Math.max(1, Number(input.limit ?? 20));\n const offset = Math.max(0, Number(input.offset ?? 0));\n const olderThanDays = Math.max(0, Number(input.olderThanDays ?? 0));\n const keywords = normalizeCleanupKeywords(input.keywords, input.query);\n\n const [totalCount, notes] = await Promise.all([\n deps.countCandidateNotes(keywords, olderThanDays),\n deps.findCandidateNotes(keywords, offset, limit, olderThanDays),\n ]);\n\n const candidates = await Promise.all(\n notes.map((note) => {\n const haystack = `${note.title}\\n${note.content}`.toLowerCase();\n const matchedTerms = keywords.filter((keyword) => haystack.includes(keyword));\n return buildCandidate(note, matchedTerms);\n }),\n );\n\n return {\n keywords,\n limit,\n offset,\n olderThanDays,\n notes: candidates,\n totalCount,\n };\n },\n getDeletePreview,\n deleteNoteById: async (id: number) => {\n const preview = await getDeletePreviewInternal(id);\n\n if (!preview) {\n return null;\n }\n\n await deps.deleteNoteAndPruneTags(id, preview.orphanedTagIds);\n\n const { orphanedTagIds, ...publicPreview } = preview;\n void orphanedTagIds;\n return publicPreview;\n },\n };\n};\n\nconst buildKeywordClauses = (keywords: string[]) => {\n return keywords.flatMap((keyword) => [{ title: { contains: keyword } }, { content: { contains: keyword } }]);\n};\n\nconst buildStaleWhere = (olderThanDays: number) => {\n if (olderThanDays <= 0) {\n return {};\n }\n\n return { updatedAt: { lte: new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000) } };\n};\n\nconst noteCleanupService = createNoteCleanupService({\n countCandidateNotes: (keywords: string[], olderThanDays: number) =>\n models.note.count({\n where: {\n ...buildStaleWhere(olderThanDays),\n OR: buildKeywordClauses(keywords),\n },\n }),\n countReminders: (noteId: number) => models.reminder.count({ where: { noteId } }),\n deleteNoteAndPruneTags: async (noteId: number, orphanTagIds: number[]) => {\n void orphanTagIds;\n await moveNoteToTrashById(noteId);\n },\n findBackReferences: (noteId: number) =>\n models.note\n .findMany({\n select: {\n id: true,\n title: true,\n content: true,\n },\n where: {\n NOT: { id: noteId },\n },\n orderBy: [{ pinned: 'desc' }, { updatedAt: 'desc' }],\n })\n .then((notes) => notes.filter((note) => contentReferencesNote(note.content, noteId))),\n findCandidateNotes: (keywords: string[], skip: number, take: number, olderThanDays: number) =>\n models.note.findMany({\n include: { tags: true },\n orderBy: { updatedAt: 'asc' },\n skip,\n take,\n where: {\n ...buildStaleWhere(olderThanDays),\n OR: buildKeywordClauses(keywords),\n },\n }),\n findNote: (id: number) =>\n models.note.findUnique({\n where: { id },\n include: { tags: true },\n }),\n getTagNoteCounts: async (tagIds: number[]) => {\n if (tagIds.length === 0) {\n return new Map<number, number>();\n }\n\n const tags = await models.tag.findMany({\n where: { id: { in: tagIds } },\n include: { _count: { select: { notes: true } } },\n });\n\n return new Map(tags.map((tag) => [tag.id, tag._count.notes]));\n },\n});\n\nexport const listNoteCleanupCandidates = noteCleanupService.listCleanupCandidates;\nexport const getNoteCleanupPreview = noteCleanupService.getDeletePreview;\nexport const deleteNoteById = noteCleanupService.deleteNoteById;\n"],"mappings":"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","names":[]}
@@ -50,14 +50,6 @@ const serializeSnapshot = (snapshot) => {
50
50
  createdAt: isRecord(snapshot) && typeof snapshot.createdAt === "string" ? snapshot.createdAt : ""
51
51
  };
52
52
  };
53
- const toDryRun = (plan) => ({
54
- status: "dry_run",
55
- note: plan.note,
56
- ...plan.match ? { match: plan.match } : {},
57
- ...plan.placement ? { placement: plan.placement } : {},
58
- proposed: plan.proposed,
59
- warnings: plan.warnings
60
- });
61
53
  const toTagIds = (tagIds) => {
62
54
  return tagIds.map((tagId) => Number(tagId)).filter((tagId) => Number.isSafeInteger(tagId) && tagId > 0);
63
55
  };
@@ -188,12 +180,9 @@ const applyMarkdownPlan = async (deps, input) => {
188
180
  };
189
181
  };
190
182
  const mapPlanResult = async (deps, input) => {
191
- if (input.plan.status !== "dry_run") {
183
+ if (input.plan.status !== "planned") {
192
184
  return input.plan;
193
185
  }
194
- if (input.dryRun ?? true) {
195
- return toDryRun(input.plan);
196
- }
197
186
  return applyMarkdownPlan(deps, {
198
187
  noteId: input.noteId,
199
188
  noteUpdatedAt: input.noteUpdatedAt,
@@ -234,7 +223,6 @@ const createMarkdownIntentWriteService = (deps) => ({
234
223
  noteUpdatedAt: noteSnapshot.updatedAt,
235
224
  beforeContentJson: note.content,
236
225
  plan,
237
- dryRun: input.dryRun,
238
226
  expectedUpdatedAt: input.expectedUpdatedAt,
239
227
  policy: input.policy,
240
228
  summary: input.intent
@@ -269,7 +257,6 @@ const createMarkdownIntentWriteService = (deps) => ({
269
257
  noteUpdatedAt: noteSnapshot.updatedAt,
270
258
  beforeContentJson: note.content,
271
259
  plan,
272
- dryRun: input.dryRun,
273
260
  expectedUpdatedAt: input.expectedUpdatedAt,
274
261
  policy: input.policy,
275
262
  summary: input.intent
@@ -302,7 +289,6 @@ const createMarkdownIntentWriteService = (deps) => ({
302
289
  noteUpdatedAt: noteSnapshot.updatedAt,
303
290
  beforeContentJson: note.content,
304
291
  plan,
305
- dryRun: input.dryRun,
306
292
  expectedUpdatedAt: input.expectedUpdatedAt,
307
293
  policy: input.policy,
308
294
  summary: input.intent
@@ -343,26 +329,11 @@ const createMarkdownIntentWriteService = (deps) => ({
343
329
  message: "The metadata update would not change the note."
344
330
  };
345
331
  }
346
- return {
347
- status: "dry_run",
348
- note: {
349
- id: String(note.id),
350
- title: note.title,
351
- updatedAt: note.updatedAt.toISOString()
352
- },
353
- proposed: {
354
- ...nextTitle !== void 0 ? { title: nextTitle } : {},
355
- ...input.layout !== void 0 ? { layout: input.layout } : {},
356
- ...resolvedPropertyPatch ? { properties: resolvedPropertyPatch } : {}
357
- },
358
- warnings: []
332
+ const proposed = {
333
+ ...nextTitle !== void 0 ? { title: nextTitle } : {},
334
+ ...input.layout !== void 0 ? { layout: input.layout } : {},
335
+ ...resolvedPropertyPatch ? { properties: resolvedPropertyPatch } : {}
359
336
  };
360
- },
361
- applyNoteMetadata: async (input) => {
362
- const preview = await createMarkdownIntentWriteService(deps).updateNoteMetadata(input);
363
- if (preview.status !== "dry_run") {
364
- return preview;
365
- }
366
337
  if (input.properties !== void 0) {
367
338
  if (!deps.updateProperties) {
368
339
  throw new Error("Metadata property write dependency is not configured.");
@@ -371,11 +342,11 @@ const createMarkdownIntentWriteService = (deps) => ({
371
342
  try {
372
343
  updateResult2 = await deps.updateProperties({
373
344
  id: input.id,
374
- patch: preview.proposed.properties ?? { set: [], deleteKeys: [] },
345
+ patch: proposed.properties ?? { set: [], deleteKeys: [] },
375
346
  expectedUpdatedAt: input.expectedUpdatedAt,
376
347
  noteData: {
377
- ...preview.proposed.title !== void 0 ? { title: preview.proposed.title } : {},
378
- ...preview.proposed.layout !== void 0 ? { layout: preview.proposed.layout } : {}
348
+ ...proposed.title !== void 0 ? { title: proposed.title } : {},
349
+ ...proposed.layout !== void 0 ? { layout: proposed.layout } : {}
379
350
  },
380
351
  snapshotMeta: MCP_SNAPSHOT_META
381
352
  });
@@ -412,8 +383,8 @@ const createMarkdownIntentWriteService = (deps) => ({
412
383
  updateResult = await deps.updateNote({
413
384
  id: input.id,
414
385
  data: {
415
- ...preview.proposed.title !== void 0 ? { title: preview.proposed.title } : {},
416
- ...preview.proposed.layout !== void 0 ? { layout: preview.proposed.layout } : {}
386
+ ...proposed.title !== void 0 ? { title: proposed.title } : {},
387
+ ...proposed.layout !== void 0 ? { layout: proposed.layout } : {}
417
388
  },
418
389
  expectedUpdatedAt: input.expectedUpdatedAt,
419
390
  snapshotMeta: MCP_SNAPSHOT_META
@@ -475,10 +446,7 @@ const replaceNoteMarkdown = async (input) => {
475
446
  return defaultMarkdownIntentWriteService.replaceNoteMarkdown(input);
476
447
  };
477
448
  const updateNoteMetadata = async (input) => {
478
- if (input.dryRun ?? true) {
479
- return defaultMarkdownIntentWriteService.updateNoteMetadata(input);
480
- }
481
- return defaultMarkdownIntentWriteService.applyNoteMetadata(input);
449
+ return defaultMarkdownIntentWriteService.updateNoteMetadata(input);
482
450
  };
483
451
  const calculateNoteMarkdownSha256 = (markdown) => {
484
452
  return calculateMarkdownSha256(markdown);
@@ -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":"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","names":["updateResult"]}