ocean-brain 0.7.5 → 0.8.1

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 (263) hide show
  1. package/dist/index.js +3 -3
  2. package/dist/mcp.js +180 -736
  3. package/package.json +6 -2
  4. package/server/client/dist/assets/ArrowRight.es-CbA8ey6a.js +1 -0
  5. package/server/client/dist/assets/{Calendar-Bc2zJueR.js → Calendar-CgWgr1BV.js} +2 -2
  6. package/server/client/dist/assets/Callout-DKruIT0f.js +1 -0
  7. package/server/client/dist/assets/Copy.es-DFbgpaNJ.js +1 -0
  8. package/server/client/dist/assets/Graph-kCltGRlY.js +1 -0
  9. package/server/client/dist/assets/{Image-DxWhlIDG.js → Image-7W0DZwMo.js} +1 -1
  10. package/server/client/dist/assets/Image.es-5pXLx9TU.js +1 -0
  11. package/server/client/dist/assets/Note-BW6Np_5g.js +20 -0
  12. package/server/client/dist/assets/Plus.es-B0sg64VJ.js +1 -0
  13. package/server/client/dist/assets/Reminders-C10fHQcl.js +1 -0
  14. package/server/client/dist/assets/Search-tf2nV4v0.js +1 -0
  15. package/server/client/dist/assets/{SurfaceCard-CkAVMLPs.js → SurfaceCard-pydtvHfu.js} +1 -1
  16. package/server/client/dist/assets/Tag-uCoYbISy.js +1 -0
  17. package/server/client/dist/assets/TagNotes-CqOcQiRw.js +1 -0
  18. package/server/client/dist/assets/Trash.es-w32Goxfa.js +1 -0
  19. package/server/client/dist/assets/ViewNotes-slPXXoOY.js +1 -0
  20. package/server/client/dist/assets/ViewSectionDialog-brTrsF6G.js +102 -0
  21. package/server/client/dist/assets/Views-B_wsb-XJ.js +1 -0
  22. package/server/client/dist/assets/{graph-vendor-CUxe67Lr.js → graph-vendor-BAF95C0g.js} +2 -2
  23. package/server/client/dist/assets/image.api-CYsjXiMr.js +17 -0
  24. package/server/client/dist/assets/index-BR05aSXr.js +342 -0
  25. package/server/client/dist/assets/{index-D3BfroIT.js → index-D83QbOcw.js} +1 -1
  26. package/server/client/dist/assets/{index-C5BGzDgt.css → index-DPI6YoDt.css} +1 -1
  27. package/server/client/dist/assets/manage-image-BYIhWu56.js +1 -0
  28. package/server/client/dist/assets/{manage-image-detail-C8Q7K7I3.js → manage-image-detail-6KgmQvbN.js} +1 -1
  29. package/server/client/dist/assets/mcp-Dd98N9Hq.js +4 -0
  30. package/server/client/dist/assets/mcp-admin-adapter-DOgBsYpl.js +1 -0
  31. package/server/client/dist/assets/note-core-CMGUaW1v.js +176 -0
  32. package/server/client/dist/assets/note-vendor-Hoz9Q4-4.js +54 -0
  33. package/server/client/dist/assets/{placeholder-Cr_nI8UI.js → placeholder-siHGg9aK.js} +4 -4
  34. package/server/client/dist/assets/{properties-CvYWJWUM.js → properties-CyVRKgcT.js} +1 -1
  35. package/server/client/dist/assets/tag.api-CwmC8crB.js +21 -0
  36. package/server/client/dist/assets/trash-wojQBK9Z.js +1 -0
  37. package/server/client/dist/assets/{useReminderMutate-CDmIEwsw.js → useReminderMutate-DuVKxpBh.js} +1 -1
  38. package/server/client/dist/index.html +5 -5
  39. package/server/dist/app.js +0 -1
  40. package/server/dist/features/auth/http/api.js +0 -1
  41. package/server/dist/features/auth/http/login-page.js +272 -5
  42. package/server/dist/features/auth/http/pages.js +20 -3
  43. package/server/dist/features/auth/service.js +3 -2
  44. package/server/dist/features/cache/graphql/cache.field.resolver.js +0 -1
  45. package/server/dist/features/cache/graphql/cache.mutation.resolver.js +0 -1
  46. package/server/dist/features/cache/graphql/cache.query.resolver.js +0 -1
  47. package/server/dist/features/cache/graphql/cache.resolver.js +0 -1
  48. package/server/dist/features/cache/graphql/cache.type-defs.js +0 -1
  49. package/server/dist/features/cache/graphql/index.js +0 -1
  50. package/server/dist/features/image/graphql/image.field.resolver.js +0 -1
  51. package/server/dist/features/image/graphql/image.mutation.resolver.js +0 -1
  52. package/server/dist/features/image/graphql/image.query.resolver.js +0 -1
  53. package/server/dist/features/image/graphql/image.resolver.js +0 -1
  54. package/server/dist/features/image/graphql/image.type-defs.js +0 -1
  55. package/server/dist/features/image/graphql/index.js +0 -1
  56. package/server/dist/features/image/http/upload.js +34 -24
  57. package/server/dist/features/image/services/delete.js +0 -1
  58. package/server/dist/features/image/services/upload.js +0 -1
  59. package/server/dist/features/image/services/validation.js +110 -0
  60. package/server/dist/features/mcp-admin/http/handlers.js +11 -4
  61. package/server/dist/features/mcp-admin/service.js +0 -1
  62. package/server/dist/features/note/graphql/index.js +0 -1
  63. package/server/dist/features/note/graphql/note.field.resolver.js +0 -1
  64. package/server/dist/features/note/graphql/note.mutation.resolver.js +0 -1
  65. package/server/dist/features/note/graphql/note.query.resolver.js +0 -1
  66. package/server/dist/features/note/graphql/note.resolver.js +0 -1
  67. package/server/dist/features/note/graphql/note.type-defs.js +0 -1
  68. package/server/dist/features/note/http/mcp.js +8 -44
  69. package/server/dist/features/note/services/authoring.js +0 -1
  70. package/server/dist/features/note/services/cleanup.js +2 -3
  71. package/server/dist/features/note/services/content-blocks.js +0 -1
  72. package/server/dist/features/note/services/markdown-intent-write.js +11 -44
  73. package/server/dist/features/note/services/markdown-patch.js +4 -124
  74. package/server/dist/features/note/services/properties.js +0 -1
  75. package/server/dist/features/note/services/search-backfill.js +0 -1
  76. package/server/dist/features/note/services/search.js +0 -1
  77. package/server/dist/features/note/services/snapshot.js +0 -1
  78. package/server/dist/features/note/services/tag-filter.js +0 -1
  79. package/server/dist/features/note/services/trash.js +0 -1
  80. package/server/dist/features/note/services/write-conflict.js +0 -1
  81. package/server/dist/features/note/services/write.js +13 -14
  82. package/server/dist/features/placeholder/graphql/index.js +0 -1
  83. package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js +0 -1
  84. package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js +0 -1
  85. package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js +0 -1
  86. package/server/dist/features/placeholder/graphql/placeholder.resolver.js +0 -1
  87. package/server/dist/features/placeholder/graphql/placeholder.type-defs.js +0 -1
  88. package/server/dist/features/reminder/graphql/index.js +0 -1
  89. package/server/dist/features/reminder/graphql/reminder.field.resolver.js +0 -1
  90. package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js +0 -1
  91. package/server/dist/features/reminder/graphql/reminder.query.resolver.js +0 -1
  92. package/server/dist/features/reminder/graphql/reminder.resolver.js +0 -1
  93. package/server/dist/features/reminder/graphql/reminder.type-defs.js +0 -1
  94. package/server/dist/features/tag/graphql/index.js +0 -1
  95. package/server/dist/features/tag/graphql/tag.field.resolver.js +0 -1
  96. package/server/dist/features/tag/graphql/tag.mutation.resolver.js +0 -1
  97. package/server/dist/features/tag/graphql/tag.query.resolver.js +16 -2
  98. package/server/dist/features/tag/graphql/tag.resolver.js +0 -1
  99. package/server/dist/features/tag/graphql/tag.type-defs.js +2 -1
  100. package/server/dist/features/tag/http/mcp.js +0 -1
  101. package/server/dist/features/tag/services/organization.js +0 -1
  102. package/server/dist/features/view/graphql/index.js +0 -1
  103. package/server/dist/features/view/graphql/view.mutation.resolver.js +0 -1
  104. package/server/dist/features/view/graphql/view.query.resolver.js +0 -1
  105. package/server/dist/features/view/graphql/view.resolver.js +0 -1
  106. package/server/dist/features/view/graphql/view.type-defs.js +0 -1
  107. package/server/dist/features/view/services/workspace.js +0 -1
  108. package/server/dist/main.js +0 -1
  109. package/server/dist/models.js +0 -1
  110. package/server/dist/modules/app-version.js +104 -0
  111. package/server/dist/modules/auth-guard.js +0 -1
  112. package/server/dist/modules/auth-mode.js +0 -1
  113. package/server/dist/modules/auth-redirect.js +0 -1
  114. package/server/dist/modules/blocknote-markdown-editor.js +53 -0
  115. package/server/dist/modules/blocknote-tree.js +0 -1
  116. package/server/dist/modules/blocknote.js +10 -4
  117. package/server/dist/modules/data-maintenance.js +0 -1
  118. package/server/dist/modules/error-handler.js +0 -1
  119. package/server/dist/modules/graphql.js +0 -1
  120. package/server/dist/modules/logger.js +0 -1
  121. package/server/dist/modules/mcp-auth.js +62 -2
  122. package/server/dist/modules/mcp-token.js +0 -1
  123. package/server/dist/modules/prisma-runtime.js +0 -1
  124. package/server/dist/modules/rate-limit.js +0 -1
  125. package/server/dist/modules/recovery-retention.js +0 -1
  126. package/server/dist/modules/server-events-handler.js +0 -1
  127. package/server/dist/modules/server-events.js +0 -1
  128. package/server/dist/modules/session-store.js +0 -1
  129. package/server/dist/modules/use-async.js +0 -1
  130. package/server/dist/paths.js +1 -1
  131. package/server/dist/routes/api.js +2 -9
  132. package/server/dist/routes/auth-pages.js +0 -1
  133. package/server/dist/routes/client.js +51 -2
  134. package/server/dist/routes/graphql.js +0 -1
  135. package/server/dist/routes/index.js +0 -1
  136. package/server/dist/routes/mcp.js +0 -1
  137. package/server/dist/schema/index.js +0 -1
  138. package/server/dist/start.js +0 -1
  139. package/server/dist/types/index.js +0 -1
  140. package/server/dist/types/input.js +0 -1
  141. package/server/client/dist/assets/Callout-B4kA_Cvr.js +0 -1
  142. package/server/client/dist/assets/Graph-uq5jfJgt.js +0 -1
  143. package/server/client/dist/assets/Image.es-BpAOJjJT.js +0 -1
  144. package/server/client/dist/assets/Note-Bo8ZYRP-.js +0 -22
  145. package/server/client/dist/assets/Plus.es-Dc_js8AC.js +0 -1
  146. package/server/client/dist/assets/Reminders-lj7Equld.js +0 -1
  147. package/server/client/dist/assets/Search-1wcRO5--.js +0 -1
  148. package/server/client/dist/assets/Tag-CeygwFfv.js +0 -1
  149. package/server/client/dist/assets/TagNotes-B8DqItxw.js +0 -1
  150. package/server/client/dist/assets/Trash.es-OuQVb_SX.js +0 -1
  151. package/server/client/dist/assets/ViewNotes-BH7PbdOD.js +0 -1
  152. package/server/client/dist/assets/ViewSectionTableRenderer-BJwhd5be.js +0 -102
  153. package/server/client/dist/assets/Views-eYFtsdFj.js +0 -1
  154. package/server/client/dist/assets/image.api-C0eYGCeH.js +0 -17
  155. package/server/client/dist/assets/index-jIfkRdY_.js +0 -361
  156. package/server/client/dist/assets/manage-image-C0YwkLIP.js +0 -1
  157. package/server/client/dist/assets/mcp-DHHhTLC8.js +0 -1
  158. package/server/client/dist/assets/note-core-Z6kqulGB.js +0 -180
  159. package/server/client/dist/assets/note-vendor-i1M2FSI2.js +0 -54
  160. package/server/client/dist/assets/trash-DA27aW7L.js +0 -1
  161. package/server/client/dist/fonts/Pretendard-Bold.woff +0 -0
  162. package/server/client/dist/fonts/Pretendard-Regular.woff +0 -0
  163. package/server/dist/app.js.map +0 -1
  164. package/server/dist/features/auth/http/api.js.map +0 -1
  165. package/server/dist/features/auth/http/login-page.js.map +0 -1
  166. package/server/dist/features/auth/http/pages.js.map +0 -1
  167. package/server/dist/features/auth/service.js.map +0 -1
  168. package/server/dist/features/cache/graphql/cache.field.resolver.js.map +0 -1
  169. package/server/dist/features/cache/graphql/cache.mutation.resolver.js.map +0 -1
  170. package/server/dist/features/cache/graphql/cache.query.resolver.js.map +0 -1
  171. package/server/dist/features/cache/graphql/cache.resolver.js.map +0 -1
  172. package/server/dist/features/cache/graphql/cache.type-defs.js.map +0 -1
  173. package/server/dist/features/cache/graphql/index.js.map +0 -1
  174. package/server/dist/features/image/graphql/image.field.resolver.js.map +0 -1
  175. package/server/dist/features/image/graphql/image.mutation.resolver.js.map +0 -1
  176. package/server/dist/features/image/graphql/image.query.resolver.js.map +0 -1
  177. package/server/dist/features/image/graphql/image.resolver.js.map +0 -1
  178. package/server/dist/features/image/graphql/image.type-defs.js.map +0 -1
  179. package/server/dist/features/image/graphql/index.js.map +0 -1
  180. package/server/dist/features/image/http/upload.js.map +0 -1
  181. package/server/dist/features/image/services/delete.js.map +0 -1
  182. package/server/dist/features/image/services/remote-fetch.js +0 -176
  183. package/server/dist/features/image/services/remote-fetch.js.map +0 -1
  184. package/server/dist/features/image/services/upload.js.map +0 -1
  185. package/server/dist/features/mcp-admin/http/handlers.js.map +0 -1
  186. package/server/dist/features/mcp-admin/service.js.map +0 -1
  187. package/server/dist/features/note/graphql/index.js.map +0 -1
  188. package/server/dist/features/note/graphql/note.field.resolver.js.map +0 -1
  189. package/server/dist/features/note/graphql/note.mutation.resolver.js.map +0 -1
  190. package/server/dist/features/note/graphql/note.query.resolver.js.map +0 -1
  191. package/server/dist/features/note/graphql/note.resolver.js.map +0 -1
  192. package/server/dist/features/note/graphql/note.type-defs.js.map +0 -1
  193. package/server/dist/features/note/http/mcp.js.map +0 -1
  194. package/server/dist/features/note/services/authoring.js.map +0 -1
  195. package/server/dist/features/note/services/cleanup.js.map +0 -1
  196. package/server/dist/features/note/services/content-blocks.js.map +0 -1
  197. package/server/dist/features/note/services/markdown-intent-write.js.map +0 -1
  198. package/server/dist/features/note/services/markdown-patch.js.map +0 -1
  199. package/server/dist/features/note/services/properties.js.map +0 -1
  200. package/server/dist/features/note/services/search-backfill.js.map +0 -1
  201. package/server/dist/features/note/services/search.js.map +0 -1
  202. package/server/dist/features/note/services/snapshot.js.map +0 -1
  203. package/server/dist/features/note/services/tag-filter.js.map +0 -1
  204. package/server/dist/features/note/services/trash.js.map +0 -1
  205. package/server/dist/features/note/services/write-conflict.js.map +0 -1
  206. package/server/dist/features/note/services/write.js.map +0 -1
  207. package/server/dist/features/placeholder/graphql/index.js.map +0 -1
  208. package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js.map +0 -1
  209. package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js.map +0 -1
  210. package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js.map +0 -1
  211. package/server/dist/features/placeholder/graphql/placeholder.resolver.js.map +0 -1
  212. package/server/dist/features/placeholder/graphql/placeholder.type-defs.js.map +0 -1
  213. package/server/dist/features/reminder/graphql/index.js.map +0 -1
  214. package/server/dist/features/reminder/graphql/reminder.field.resolver.js.map +0 -1
  215. package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js.map +0 -1
  216. package/server/dist/features/reminder/graphql/reminder.query.resolver.js.map +0 -1
  217. package/server/dist/features/reminder/graphql/reminder.resolver.js.map +0 -1
  218. package/server/dist/features/reminder/graphql/reminder.type-defs.js.map +0 -1
  219. package/server/dist/features/tag/graphql/index.js.map +0 -1
  220. package/server/dist/features/tag/graphql/tag.field.resolver.js.map +0 -1
  221. package/server/dist/features/tag/graphql/tag.mutation.resolver.js.map +0 -1
  222. package/server/dist/features/tag/graphql/tag.query.resolver.js.map +0 -1
  223. package/server/dist/features/tag/graphql/tag.resolver.js.map +0 -1
  224. package/server/dist/features/tag/graphql/tag.type-defs.js.map +0 -1
  225. package/server/dist/features/tag/http/mcp.js.map +0 -1
  226. package/server/dist/features/tag/services/organization.js.map +0 -1
  227. package/server/dist/features/view/graphql/index.js.map +0 -1
  228. package/server/dist/features/view/graphql/view.mutation.resolver.js.map +0 -1
  229. package/server/dist/features/view/graphql/view.query.resolver.js.map +0 -1
  230. package/server/dist/features/view/graphql/view.resolver.js.map +0 -1
  231. package/server/dist/features/view/graphql/view.type-defs.js.map +0 -1
  232. package/server/dist/features/view/services/workspace.js.map +0 -1
  233. package/server/dist/main.js.map +0 -1
  234. package/server/dist/models.js.map +0 -1
  235. package/server/dist/modules/auth-guard.js.map +0 -1
  236. package/server/dist/modules/auth-mode.js.map +0 -1
  237. package/server/dist/modules/auth-redirect.js.map +0 -1
  238. package/server/dist/modules/blocknote-tree.js.map +0 -1
  239. package/server/dist/modules/blocknote.js.map +0 -1
  240. package/server/dist/modules/data-maintenance.js.map +0 -1
  241. package/server/dist/modules/error-handler.js.map +0 -1
  242. package/server/dist/modules/graphql.js.map +0 -1
  243. package/server/dist/modules/logger.js.map +0 -1
  244. package/server/dist/modules/mcp-auth.js.map +0 -1
  245. package/server/dist/modules/mcp-token.js.map +0 -1
  246. package/server/dist/modules/prisma-runtime.js.map +0 -1
  247. package/server/dist/modules/rate-limit.js.map +0 -1
  248. package/server/dist/modules/recovery-retention.js.map +0 -1
  249. package/server/dist/modules/server-events-handler.js.map +0 -1
  250. package/server/dist/modules/server-events.js.map +0 -1
  251. package/server/dist/modules/session-store.js.map +0 -1
  252. package/server/dist/modules/use-async.js.map +0 -1
  253. package/server/dist/paths.js.map +0 -1
  254. package/server/dist/routes/api.js.map +0 -1
  255. package/server/dist/routes/auth-pages.js.map +0 -1
  256. package/server/dist/routes/client.js.map +0 -1
  257. package/server/dist/routes/graphql.js.map +0 -1
  258. package/server/dist/routes/index.js.map +0 -1
  259. package/server/dist/routes/mcp.js.map +0 -1
  260. package/server/dist/schema/index.js.map +0 -1
  261. package/server/dist/start.js.map +0 -1
  262. package/server/dist/types/index.js.map +0 -1
  263. package/server/dist/types/input.js.map +0 -1
@@ -1 +0,0 @@
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 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/services/authoring.ts"],"sourcesContent":["import models, { type NoteLayout } from '~/models.js';\nimport { extractTagIdsFromContentJson, markdownToBlocksJson } from '~/modules/blocknote.js';\nimport { buildNoteSearchProjection } from './search.js';\nimport { captureNoteBaseline } from './snapshot.js';\n\ninterface PlaceholderRecord {\n template: string;\n replacement: string;\n}\n\ninterface NoteRecord {\n id: number;\n title: string;\n content: string;\n layout: NoteLayout;\n createdAt: Date;\n updatedAt: Date;\n}\n\ninterface NoteAuthoringDeps {\n createNote: (input: {\n title: string;\n content: string;\n layout?: NoteLayout;\n tagIds?: string[];\n }) => Promise<NoteRecord>;\n findNoteById: (id: number) => Promise<NoteRecord | null>;\n findPlaceholders: (templates: string[]) => Promise<PlaceholderRecord[]>;\n parseMarkdownToContentJson: (markdown: string) => Promise<string>;\n extractTagIds: (contentJson: string) => string[];\n captureBaseline: (input: { noteId: number; editSessionId?: string; meta?: string }) => Promise<unknown>;\n updateNote: (\n id: number,\n input: {\n title?: string;\n content?: string;\n layout?: NoteLayout;\n tagIds?: string[];\n },\n ) => Promise<NoteRecord>;\n}\n\nexport interface CreateNoteAuthoringInput {\n title: string;\n markdown?: string;\n layout?: NoteLayout;\n}\n\nexport interface UpdateNoteAuthoringInput {\n id: number;\n title?: string;\n markdown?: string;\n layout?: NoteLayout;\n editSessionId?: string;\n snapshotMeta?: string;\n}\n\nexport interface AuthoredNoteSummary {\n id: string;\n title: string;\n layout: NoteLayout;\n createdAt: string;\n updatedAt: string;\n}\n\nexport class InvalidNoteAuthoringInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidNoteAuthoringInputError';\n }\n}\n\nconst PLACEHOLDER_PREFIX = '{%';\nconst PLACEHOLDER_SUFFIX = '%}';\n\nconst serializeNote = (note: NoteRecord): AuthoredNoteSummary => ({\n id: String(note.id),\n title: note.title,\n layout: note.layout,\n createdAt: note.createdAt.toISOString(),\n updatedAt: note.updatedAt.toISOString(),\n});\n\nconst extractPlaceholderTemplates = (value: string) => {\n return Array.from(\n new Set(\n Array.from(\n value.matchAll(new RegExp(`${PLACEHOLDER_PREFIX}([^}]+)${PLACEHOLDER_SUFFIX}`, 'g')),\n (match) => match[1],\n ),\n ),\n );\n};\n\nexport const createNoteAuthoringService = (deps: NoteAuthoringDeps) => {\n const replacePlaceholders = async (value: string) => {\n const templates = extractPlaceholderTemplates(value);\n\n if (templates.length === 0) {\n return value;\n }\n\n const placeholders = await deps.findPlaceholders(templates);\n let replacedValue = value;\n\n for (const placeholder of placeholders) {\n replacedValue = replacedValue.replace(\n new RegExp(`${PLACEHOLDER_PREFIX}${placeholder.template}${PLACEHOLDER_SUFFIX}`, 'g'),\n placeholder.replacement,\n );\n }\n\n return replacedValue;\n };\n\n return {\n createNote: async (input: CreateNoteAuthoringInput): Promise<AuthoredNoteSummary> => {\n const title = input.title.trim();\n\n if (!title) {\n throw new InvalidNoteAuthoringInputError('A note title is required.');\n }\n\n const replacedTitle = await replacePlaceholders(title);\n const replacedMarkdown = await replacePlaceholders(input.markdown ?? '');\n const content = await deps.parseMarkdownToContentJson(replacedMarkdown);\n const tagIds = deps.extractTagIds(content);\n const note = await deps.createNote({\n title: replacedTitle,\n content,\n tagIds,\n ...(input.layout ? { layout: input.layout } : {}),\n });\n\n return serializeNote(note);\n },\n\n updateNote: async (input: UpdateNoteAuthoringInput): Promise<AuthoredNoteSummary | null> => {\n if (input.title === undefined && input.markdown === undefined && input.layout === undefined) {\n throw new InvalidNoteAuthoringInputError('At least one note field must be provided for update.');\n }\n\n const existingNote = await deps.findNoteById(input.id);\n\n if (!existingNote) {\n return null;\n }\n\n const nextData: {\n title?: string;\n content?: string;\n layout?: NoteLayout;\n tagIds?: string[];\n } = {};\n\n if (input.title !== undefined) {\n const title = input.title.trim();\n\n if (!title) {\n throw new InvalidNoteAuthoringInputError('A note title is required.');\n }\n\n nextData.title = await replacePlaceholders(title);\n }\n\n if (input.markdown !== undefined) {\n const replacedMarkdown = await replacePlaceholders(input.markdown);\n nextData.content = await deps.parseMarkdownToContentJson(replacedMarkdown);\n nextData.tagIds = deps.extractTagIds(nextData.content);\n }\n\n if (input.layout !== undefined) {\n nextData.layout = input.layout;\n }\n\n await deps.captureBaseline({\n noteId: input.id,\n ...(input.editSessionId ? { editSessionId: input.editSessionId } : {}),\n ...(input.snapshotMeta ? { meta: input.snapshotMeta } : {}),\n });\n\n const updatedNote = await deps.updateNote(input.id, nextData);\n return serializeNote(updatedNote);\n },\n };\n};\n\nconst defaultNoteAuthoringService = createNoteAuthoringService({\n createNote: async (input) => {\n return models.note.create({\n data: {\n title: input.title,\n content: input.content,\n ...buildNoteSearchProjection({\n title: input.title,\n content: input.content,\n }),\n ...(input.layout ? { layout: input.layout } : {}),\n ...(input.tagIds ? { tags: { connect: input.tagIds.map((id) => ({ id: Number(id) })) } } : {}),\n },\n });\n },\n findNoteById: async (id) => {\n return models.note.findUnique({ where: { id } });\n },\n findPlaceholders: async (templates) => {\n if (templates.length === 0) {\n return [];\n }\n\n return models.placeholder.findMany({\n select: {\n template: true,\n replacement: true,\n },\n where: { template: { in: templates } },\n });\n },\n parseMarkdownToContentJson: markdownToBlocksJson,\n extractTagIds: extractTagIdsFromContentJson,\n captureBaseline: captureNoteBaseline,\n updateNote: async (id, input) => {\n const existingNote = await models.note.findUnique({\n where: { id },\n select: {\n title: true,\n content: true,\n },\n });\n\n if (!existingNote) {\n throw new Error('NOTE_NOT_FOUND');\n }\n\n const nextTitle = input.title ?? existingNote.title;\n const nextContent = input.content ?? existingNote.content;\n\n return models.note.update({\n where: { id },\n data: {\n title: input.title,\n content: input.content,\n layout: input.layout,\n ...buildNoteSearchProjection({\n title: nextTitle,\n content: nextContent,\n }),\n ...(input.tagIds ? { tags: { set: input.tagIds.map((tagId) => ({ id: Number(tagId) })) } } : {}),\n },\n });\n },\n});\n\nexport const createNoteFromMarkdown = async (input: CreateNoteAuthoringInput) => {\n return defaultNoteAuthoringService.createNote(input);\n};\n\nexport const updateNoteFromMarkdown = async (input: UpdateNoteAuthoringInput) => {\n return defaultNoteAuthoringService.updateNote(input);\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
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 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/services/content-blocks.ts"],"sourcesContent":["import { parseBlockNoteContent, walkBlockNoteTree } from '~/modules/blocknote-tree.js';\n\ninterface BlockNote<T = unknown> {\n id?: string;\n type?: string;\n props?: T;\n content?: unknown;\n children?: unknown;\n}\n\ninterface ReferenceProps {\n id?: string | number;\n title?: string;\n}\n\ninterface NoteGraphInput {\n id: string | number;\n title: string;\n content: string;\n}\n\nexport interface NoteGraphResult {\n nodes: Array<{ id: string; title: string; connections: number }>;\n links: Array<{ source: string; target: string }>;\n}\n\nexport const parseNoteContent = (content: string): unknown[] | null => {\n return parseBlockNoteContent(content);\n};\n\nexport const normalizeReferenceId = (id: unknown) => {\n if (typeof id === 'number') {\n return Number.isFinite(id) ? String(id) : null;\n }\n\n if (typeof id !== 'string') {\n return null;\n }\n\n const normalizedId = id.trim();\n\n return normalizedId.length > 0 ? normalizedId : null;\n};\n\nexport const extractBlocksByType = <T>(type: string, dataArray: unknown): BlockNote<T>[] => {\n const result: BlockNote<T>[] = [];\n\n walkBlockNoteTree(dataArray, (node) => {\n if (node.type === type) {\n result.push(node as BlockNote<T>);\n }\n });\n\n return result;\n};\n\nexport const extractReferenceBlocks = (dataArray: unknown) => {\n return extractBlocksByType<ReferenceProps>('reference', dataArray).filter((block) => {\n return normalizeReferenceId(block.props?.id) !== null;\n });\n};\n\nexport const extractReferenceBlocksFromContent = (content: string) => {\n const parsed = parseNoteContent(content);\n return parsed ? extractReferenceBlocks(parsed) : [];\n};\n\nexport const contentReferencesNote = (content: string, noteId: string | number) => {\n const targetId = normalizeReferenceId(noteId);\n\n if (!targetId) {\n return false;\n }\n\n return extractReferenceBlocksFromContent(content).some(\n (block) => normalizeReferenceId(block.props?.id) === targetId,\n );\n};\n\nexport const syncReferenceTitlesInContent = (content: string, titlesById: Map<string, string>) => {\n const parsed = parseNoteContent(content);\n\n if (!parsed) {\n return null;\n }\n\n let changed = false;\n\n for (const block of extractReferenceBlocks(parsed)) {\n const props = block.props;\n const id = normalizeReferenceId(props?.id);\n\n if (!props || id === null) {\n continue;\n }\n\n const title = titlesById.get(id);\n\n if (title !== undefined && props.title !== title) {\n block.props = {\n ...props,\n title,\n };\n changed = true;\n }\n }\n\n return changed ? JSON.stringify(parsed) : content;\n};\n\nexport const buildNoteGraph = (notes: NoteGraphInput[]): NoteGraphResult => {\n const existingNoteIds = new Set(notes.map((note) => String(note.id)));\n const links: NoteGraphResult['links'] = [];\n const connectionCount: Record<string, number> = {};\n const linkSet = new Set<string>();\n\n for (const note of notes) {\n const sourceId = String(note.id);\n\n for (const block of extractReferenceBlocksFromContent(note.content)) {\n const targetId = normalizeReferenceId(block.props?.id);\n\n if (!targetId || sourceId === targetId || !existingNoteIds.has(targetId)) {\n continue;\n }\n\n const linkKey = `${sourceId}-${targetId}`;\n const reverseLinkKey = `${targetId}-${sourceId}`;\n\n if (linkSet.has(linkKey) || linkSet.has(reverseLinkKey)) {\n continue;\n }\n\n linkSet.add(linkKey);\n links.push({\n source: sourceId,\n target: targetId,\n });\n connectionCount[sourceId] = (connectionCount[sourceId] || 0) + 1;\n connectionCount[targetId] = (connectionCount[targetId] || 0) + 1;\n }\n }\n\n return {\n nodes: notes.map((note) => {\n const id = String(note.id);\n\n return {\n id,\n title: note.title || 'Untitled',\n connections: connectionCount[id] || 0,\n };\n }),\n links,\n };\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
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 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/services/markdown-patch.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst HASH_ALGORITHM = 'sha256';\nconst EXCERPT_CONTEXT_CHARS = 80;\nconst DIFF_CONTEXT_LINES = 3;\n\nexport interface MarkdownPatchNoteSnapshot {\n id: string;\n title: string;\n updatedAt: string;\n markdown: string;\n}\n\nexport interface ExactTextMarkdownPatchSelector {\n type: 'exact_text';\n text: string;\n before?: string;\n after?: string;\n}\n\nexport interface MatchCandidateMarkdownPatchSelector {\n type: 'match_candidate';\n text: string;\n matchIndex: number;\n lineStart: number;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport type MarkdownPatchSelector = ExactTextMarkdownPatchSelector | MatchCandidateMarkdownPatchSelector;\n\nexport interface ReplaceMarkdownPatchOperation {\n type: 'replace';\n replacement: string;\n}\n\nexport interface InsertBeforeMarkdownPatchOperation {\n type: 'insert_before';\n insertion: string;\n}\n\nexport interface InsertAfterMarkdownPatchOperation {\n type: 'insert_after';\n insertion: string;\n}\n\nexport type MarkdownPatchOperation =\n | ReplaceMarkdownPatchOperation\n | InsertBeforeMarkdownPatchOperation\n | InsertAfterMarkdownPatchOperation;\n\nexport interface MarkdownChangePolicy {\n allowNoop?: boolean;\n maxChangedChars?: number;\n maxChangedLines?: number;\n diffPreviewMaxChars?: number;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface PreviewMarkdownPatchInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n selector: MarkdownPatchSelector;\n operation: MarkdownPatchOperation;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownAppendPlacementEnd {\n type: 'end';\n}\n\nexport interface MarkdownAppendPlacementAfterHeading {\n type: 'after_heading';\n heading: string;\n level?: number;\n}\n\nexport type MarkdownAppendPlacement = MarkdownAppendPlacementEnd | MarkdownAppendPlacementAfterHeading;\n\nexport interface PreviewMarkdownAppendInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n insertion: string;\n placement?: MarkdownAppendPlacement;\n separator?: '\\n\\n' | '\\n';\n policy?: MarkdownChangePolicy;\n}\n\nexport interface PreviewMarkdownReplaceInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownPatchMatch {\n matchIndex: number;\n lineStart: number;\n lineEnd: number;\n text: string;\n beforeExcerpt?: string;\n afterExcerpt?: string;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport interface MarkdownChangeDryRun {\n status: 'dry_run';\n note: {\n id: string;\n title: string;\n updatedAt: string;\n };\n match?: {\n count: 1;\n lineStart: number;\n lineEnd: number;\n selectorType: string;\n matchedTextSha256: string;\n surroundingHash: string;\n };\n placement?: MarkdownAppendPlacement;\n proposed: {\n changedLineCount: number;\n changedCharCount: number;\n beforeMarkdownSha256: string;\n afterMarkdownSha256: string;\n diff: string;\n diffTruncated?: boolean;\n diffCharCount?: number;\n };\n warnings: string[];\n}\n\nexport interface MarkdownChangePlan extends MarkdownChangeDryRun {\n afterMarkdown: string;\n}\n\nexport interface MarkdownPatchNeedsDisambiguation {\n status: 'needs_disambiguation';\n reason: 'TARGET_AMBIGUOUS';\n matches: MarkdownPatchMatch[];\n}\n\nexport interface MarkdownChangeFailure {\n status: 'failed';\n reason:\n | 'ANCHOR_MISMATCH'\n | 'BASELINE_MISMATCH'\n | 'CANDIDATE_MISMATCH'\n | 'CHANGE_LIMIT_EXCEEDED'\n | 'EMPTY_INSERTION'\n | 'EMPTY_REPLACEMENT'\n | 'EMPTY_TARGET'\n | 'HEADING_AMBIGUOUS'\n | 'HEADING_NOT_FOUND'\n | 'INVALID_HEADING_LEVEL'\n | 'INVALID_PROPERTY_INPUT'\n | 'MARKDOWN_IMPORT_LOSSY'\n | 'MISSING_BASELINE'\n | 'NOOP'\n | 'REFERENCE_STRUCTURE_DECREASED'\n | 'REFERENCE_TOKEN_DECREASED'\n | 'TAG_TOKEN_DECREASED'\n | 'TARGET_NOT_FOUND'\n | 'UNSUPPORTED_MARKDOWN_STRUCTURE';\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type MarkdownPatchPreviewResult =\n | MarkdownChangeDryRun\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure;\n\nexport type MarkdownPatchPlanResult = MarkdownChangePlan | MarkdownPatchNeedsDisambiguation | MarkdownChangeFailure;\nexport type MarkdownAppendPlanResult = MarkdownChangePlan | MarkdownChangeFailure;\nexport type MarkdownReplacePlanResult = MarkdownChangePlan | MarkdownChangeFailure;\n\ninterface RawTextMatch {\n matchIndex: number;\n start: number;\n end: number;\n text: string;\n}\n\ninterface HeadingMatch {\n heading: string;\n level: number;\n lineIndex: number;\n start: number;\n nextSectionStart: number;\n}\n\nexport const calculateMarkdownSha256 = (value: string) => {\n return crypto.createHash(HASH_ALGORITHM).update(value, 'utf8').digest('hex');\n};\n\nconst countNewlines = (value: string) => {\n let count = 0;\n\n for (const character of value) {\n if (character === '\\n') {\n count += 1;\n }\n }\n\n return count;\n};\n\nconst getLineRange = (markdown: string, match: RawTextMatch) => {\n const lineStart = countNewlines(markdown.slice(0, match.start)) + 1;\n const lineEnd = lineStart + countNewlines(match.text);\n\n return {\n lineStart,\n lineEnd,\n };\n};\n\nconst buildExcerpt = (markdown: string, start: number, end: number) => {\n const excerpt = markdown.slice(start, end).trim();\n\n return excerpt.length > 0 ? excerpt : undefined;\n};\n\nconst toPatchMatch = (markdown: string, match: RawTextMatch, totalMatches: number): MarkdownPatchMatch => {\n const { lineStart, lineEnd } = getLineRange(markdown, match);\n const beforeStart = Math.max(0, match.start - EXCERPT_CONTEXT_CHARS);\n const afterEnd = Math.min(markdown.length, match.end + EXCERPT_CONTEXT_CHARS);\n const beforeText = markdown.slice(beforeStart, match.start);\n const afterText = markdown.slice(match.end, afterEnd);\n\n return {\n matchIndex: match.matchIndex,\n lineStart,\n lineEnd,\n text: match.text,\n beforeExcerpt: buildExcerpt(markdown, beforeStart, match.start),\n afterExcerpt: buildExcerpt(markdown, match.end, afterEnd),\n matchSha256: calculateMarkdownSha256(match.text),\n surroundingHash: calculateMarkdownSha256(JSON.stringify({ beforeText, targetText: match.text, afterText })),\n ...(match.matchIndex === 0 && totalMatches > 1 ? { positionHint: 'first' } : {}),\n ...(match.matchIndex === totalMatches - 1 && totalMatches > 1 ? { positionHint: 'last' } : {}),\n };\n};\n\nconst findExactTextMatches = (markdown: string, text: string) => {\n const matches: RawTextMatch[] = [];\n let searchStart = 0;\n\n while (searchStart <= markdown.length) {\n const start = markdown.indexOf(text, searchStart);\n\n if (start === -1) {\n break;\n }\n\n matches.push({\n matchIndex: matches.length,\n start,\n end: start + text.length,\n text,\n });\n searchStart = start + text.length;\n }\n\n return matches;\n};\n\nconst splitLines = (value: string) => {\n return value.split('\\n');\n};\n\nconst findSharedLineEdges = (beforeLines: string[], afterLines: string[]) => {\n let prefix = 0;\n const maxPrefix = Math.min(beforeLines.length, afterLines.length);\n\n while (prefix < maxPrefix && beforeLines[prefix] === afterLines[prefix]) {\n prefix += 1;\n }\n\n let suffix = 0;\n const maxSuffix = Math.min(beforeLines.length - prefix, afterLines.length - prefix);\n\n while (\n suffix < maxSuffix &&\n beforeLines[beforeLines.length - 1 - suffix] === afterLines[afterLines.length - 1 - suffix]\n ) {\n suffix += 1;\n }\n\n return {\n prefix,\n suffix,\n };\n};\n\nconst formatDiffRange = (lineIndex: number, lineCount: number) => {\n const lineNumber = lineIndex + 1;\n\n return lineCount === 1 ? String(lineNumber) : `${lineNumber},${lineCount}`;\n};\n\nexport const createUnifiedMarkdownDiff = (\n beforeMarkdown: string,\n afterMarkdown: string,\n contextLines: number | null = DIFF_CONTEXT_LINES,\n) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const changedBeforeStart = prefix;\n const changedBeforeEnd = beforeLines.length - suffix;\n const changedAfterStart = prefix;\n const changedAfterEnd = afterLines.length - suffix;\n const displayBeforeStart = contextLines === null ? 0 : Math.max(0, changedBeforeStart - contextLines);\n const displayBeforeEnd =\n contextLines === null ? beforeLines.length : Math.min(beforeLines.length, changedBeforeEnd + contextLines);\n const displayAfterStart = contextLines === null ? 0 : Math.max(0, changedAfterStart - contextLines);\n const displayAfterEnd =\n contextLines === null ? afterLines.length : Math.min(afterLines.length, changedAfterEnd + contextLines);\n const diffLines = [\n '--- before.md',\n '+++ after.md',\n `@@ -${formatDiffRange(displayBeforeStart, displayBeforeEnd - displayBeforeStart)} +${formatDiffRange(displayAfterStart, displayAfterEnd - displayAfterStart)} @@`,\n ];\n\n for (let index = displayBeforeStart; index < changedBeforeStart; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n for (let index = changedBeforeStart; index < changedBeforeEnd; index += 1) {\n diffLines.push(`-${beforeLines[index]}`);\n }\n\n for (let index = changedAfterStart; index < changedAfterEnd; index += 1) {\n diffLines.push(`+${afterLines[index]}`);\n }\n\n for (let index = changedBeforeEnd; index < displayBeforeEnd; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n return diffLines.join('\\n');\n};\n\nconst countChangedLines = (beforeMarkdown: string, afterMarkdown: string) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const removedLineCount = beforeLines.length - prefix - suffix;\n const addedLineCount = afterLines.length - prefix - suffix;\n\n return Math.max(removedLineCount, addedLineCount);\n};\n\nconst isMarkdownTokenWhitespace = (character: string) => /\\s/.test(character);\n\nexport const countExplicitTagTokens = (value: string) => {\n let count = 0;\n\n for (let index = 0; index < value.length; index += 1) {\n if (value[index] !== '[' || (value[index + 1] !== '@' && value[index + 1] !== '#')) {\n continue;\n }\n\n let cursor = index + 2;\n let isValid = cursor < value.length;\n\n while (cursor < value.length && value[cursor] !== ']') {\n if (value[cursor] === '[' || isMarkdownTokenWhitespace(value[cursor])) {\n isValid = false;\n break;\n }\n\n cursor += 1;\n }\n\n if (isValid && cursor < value.length && cursor > index + 2) {\n count += 1;\n index = cursor;\n continue;\n }\n\n if (!isValid) {\n index = Math.max(index, cursor - 1);\n }\n }\n\n return count;\n};\n\nexport const countMarkdownReferenceTokens = (value: string) => {\n let count = 0;\n let cursor = 0;\n\n while (cursor < value.length) {\n const start = value.indexOf('[[', cursor);\n\n if (start === -1) {\n break;\n }\n\n const end = value.indexOf(']]', start + 2);\n\n if (end === -1) {\n break;\n }\n\n if (end > start + 2) {\n count += 1;\n }\n\n cursor = end + 2;\n }\n\n return count;\n};\n\nconst parseVersionTimestamp = (value: string) => {\n const timestamp = /^\\d+$/.test(value) ? Number(value) : Date.parse(value);\n\n return Number.isFinite(timestamp) ? timestamp : null;\n};\n\nconst noteVersionsMatch = (expectedUpdatedAt: string, currentUpdatedAt: string) => {\n const expectedTimestamp = parseVersionTimestamp(expectedUpdatedAt);\n const currentTimestamp = parseVersionTimestamp(currentUpdatedAt);\n\n return expectedTimestamp !== null && currentTimestamp !== null && expectedTimestamp === currentTimestamp;\n};\n\nconst getPreservationFailure = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n\n if (policy?.preserveTags === true && afterTagCount < beforeTagCount) {\n return {\n status: 'failed',\n reason: 'TAG_TOKEN_DECREASED',\n message: 'The patch would reduce explicit tag tokens.',\n };\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n\n if (policy?.preserveReferences === true && afterReferenceCount < beforeReferenceCount) {\n return {\n status: 'failed',\n reason: 'REFERENCE_TOKEN_DECREASED',\n message: 'The patch would reduce note reference tokens.',\n };\n }\n\n return null;\n};\n\nconst collectWarnings = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): string[] => {\n const warnings: string[] = [];\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n const shouldWarnTags = policy?.preserveTags === undefined || policy.preserveTags === 'warn';\n\n if (shouldWarnTags && afterTagCount < beforeTagCount) {\n warnings.push('TAG_TOKEN_COUNT_DECREASED');\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n const shouldWarnReferences = policy?.preserveReferences === undefined || policy.preserveReferences === 'warn';\n\n if (shouldWarnReferences && afterReferenceCount < beforeReferenceCount) {\n warnings.push('REFERENCE_TOKEN_COUNT_DECREASED');\n }\n\n return warnings;\n};\n\nconst validateBaseline = (\n note: MarkdownPatchNoteSnapshot,\n expectedUpdatedAt: string | undefined,\n baseMarkdownSha256: string | undefined,\n): MarkdownChangeFailure | null => {\n const beforeMarkdownSha256 = calculateMarkdownSha256(note.markdown);\n\n if (!expectedUpdatedAt && !baseMarkdownSha256) {\n return {\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n };\n }\n\n if (\n (expectedUpdatedAt && !noteVersionsMatch(expectedUpdatedAt, note.updatedAt)) ||\n (baseMarkdownSha256 && baseMarkdownSha256 !== beforeMarkdownSha256)\n ) {\n return {\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message:\n 'The markdown write baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n };\n }\n\n return null;\n};\n\nconst validateChangeLimits = (\n beforeMarkdown: string,\n afterMarkdown: string,\n changedCharCount: number,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const changedLineCount = countChangedLines(beforeMarkdown, afterMarkdown);\n const exceededLines = policy?.maxChangedLines !== undefined && changedLineCount > policy.maxChangedLines;\n const exceededChars = policy?.maxChangedChars !== undefined && changedCharCount > policy.maxChangedChars;\n\n if (exceededLines || exceededChars) {\n return {\n status: 'failed',\n reason: 'CHANGE_LIMIT_EXCEEDED',\n message:\n 'The markdown write exceeds the configured change limit. Increase maxChangedLines/maxChangedChars to at least the requiredMinimum values or omit them for this write.',\n details: {\n configured: {\n ...(policy?.maxChangedLines !== undefined ? { maxChangedLines: policy.maxChangedLines } : {}),\n ...(policy?.maxChangedChars !== undefined ? { maxChangedChars: policy.maxChangedChars } : {}),\n },\n requiredMinimum: {\n maxChangedLines: changedLineCount,\n maxChangedChars: changedCharCount,\n },\n },\n };\n }\n\n return null;\n};\n\nconst buildDiffPreview = (diff: string, policy: MarkdownChangePolicy | undefined) => {\n const maxChars = policy?.diffPreviewMaxChars;\n\n if (maxChars === undefined || diff.length <= maxChars) {\n return {\n diff,\n ...(maxChars !== undefined ? { diffCharCount: diff.length } : {}),\n };\n }\n\n return {\n diff: diff.slice(0, maxChars),\n diffTruncated: true,\n diffCharCount: diff.length,\n };\n};\n\nconst createChangePlan = ({\n note,\n afterMarkdown,\n changedCharCount,\n policy,\n diffContextLines = DIFF_CONTEXT_LINES,\n match,\n placement,\n}: {\n note: MarkdownPatchNoteSnapshot;\n afterMarkdown: string;\n changedCharCount: number;\n policy: MarkdownChangePolicy | undefined;\n diffContextLines?: number | null;\n match?: MarkdownChangeDryRun['match'];\n placement?: MarkdownAppendPlacement;\n}): MarkdownChangePlan | MarkdownChangeFailure => {\n if (!policy?.allowNoop && afterMarkdown === note.markdown) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The markdown write would not change the note.',\n };\n }\n\n const preservationFailure = getPreservationFailure(note.markdown, afterMarkdown, policy);\n\n if (preservationFailure) {\n return preservationFailure;\n }\n\n const limitFailure = validateChangeLimits(note.markdown, afterMarkdown, changedCharCount, policy);\n\n if (limitFailure) {\n return limitFailure;\n }\n\n const diffPreview = buildDiffPreview(\n createUnifiedMarkdownDiff(note.markdown, afterMarkdown, diffContextLines),\n policy,\n );\n\n return {\n status: 'dry_run',\n note: {\n id: note.id,\n title: note.title,\n updatedAt: note.updatedAt,\n },\n ...(match ? { match } : {}),\n ...(placement ? { placement } : {}),\n proposed: {\n changedLineCount: countChangedLines(note.markdown, afterMarkdown),\n changedCharCount,\n beforeMarkdownSha256: calculateMarkdownSha256(note.markdown),\n afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown),\n ...diffPreview,\n },\n warnings: collectWarnings(note.markdown, afterMarkdown, policy),\n afterMarkdown,\n };\n};\n\nconst matchHasAnchors = (markdown: string, match: RawTextMatch, selector: ExactTextMarkdownPatchSelector) => {\n if (selector.before !== undefined && !markdown.slice(0, match.start).endsWith(selector.before)) {\n return false;\n }\n\n if (selector.after !== undefined && !markdown.slice(match.end).startsWith(selector.after)) {\n return false;\n }\n\n return true;\n};\n\nconst selectExactTextMatch = (\n markdown: string,\n selector: ExactTextMarkdownPatchSelector,\n):\n | { match: RawTextMatch; patchMatch: MarkdownPatchMatch }\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Exact text selector must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No exact text match was found in the current note.',\n };\n }\n\n const anchoredMatches = rawMatches.filter((match) => matchHasAnchors(markdown, match, selector));\n\n if (anchoredMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'ANCHOR_MISMATCH',\n message: 'Exact text matches were found, but before/after anchors did not match.',\n };\n }\n\n if (anchoredMatches.length > 1) {\n return {\n status: 'needs_disambiguation',\n reason: 'TARGET_AMBIGUOUS',\n matches: anchoredMatches.map((match) => toPatchMatch(markdown, match, rawMatches.length)),\n };\n }\n\n const match = anchoredMatches[0];\n\n return {\n match,\n patchMatch: toPatchMatch(markdown, match, rawMatches.length),\n };\n};\n\nconst selectMatchCandidate = (\n markdown: string,\n selector: MatchCandidateMarkdownPatchSelector,\n): { match: RawTextMatch; patchMatch: MarkdownPatchMatch } | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Match candidate selector text must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No match candidate text was found in the current note.',\n };\n }\n\n const match = rawMatches.find((candidate) => candidate.matchIndex === selector.matchIndex);\n\n if (!match) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match index does not exist in the current note.',\n };\n }\n\n const patchMatch = toPatchMatch(markdown, match, rawMatches.length);\n const positionMatches = selector.positionHint === undefined || patchMatch.positionHint === selector.positionHint;\n\n if (\n patchMatch.lineStart !== selector.lineStart ||\n patchMatch.matchSha256 !== selector.matchSha256 ||\n patchMatch.surroundingHash !== selector.surroundingHash ||\n !positionMatches\n ) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match candidate no longer matches the current note context.',\n };\n }\n\n return {\n match,\n patchMatch,\n };\n};\n\nconst applyPatchOperation = (markdown: string, match: RawTextMatch, operation: MarkdownPatchOperation) => {\n if (operation.type === 'replace') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.replacement}${markdown.slice(match.end)}`,\n changedCharCount: match.text.length + operation.replacement.length,\n };\n }\n\n if (operation.type === 'insert_before') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.insertion}${markdown.slice(match.start)}`,\n changedCharCount: operation.insertion.length,\n };\n }\n\n return {\n markdown: `${markdown.slice(0, match.end)}${operation.insertion}${markdown.slice(match.end)}`,\n changedCharCount: operation.insertion.length,\n };\n};\n\nexport const buildMarkdownPatchPlan = (input: PreviewMarkdownPatchInput): MarkdownPatchPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const selected =\n input.selector.type === 'exact_text'\n ? selectExactTextMatch(input.note.markdown, input.selector)\n : selectMatchCandidate(input.note.markdown, input.selector);\n\n if ('status' in selected) {\n return selected;\n }\n\n const patched = applyPatchOperation(input.note.markdown, selected.match, input.operation);\n const plan = createChangePlan({\n note: input.note,\n afterMarkdown: patched.markdown,\n changedCharCount: patched.changedCharCount,\n policy: input.policy,\n match: {\n count: 1,\n lineStart: selected.patchMatch.lineStart,\n lineEnd: selected.patchMatch.lineEnd,\n selectorType: input.selector.type,\n matchedTextSha256: selected.patchMatch.matchSha256,\n surroundingHash: selected.patchMatch.surroundingHash,\n },\n });\n\n return plan;\n};\n\nconst parseHeadingLine = (line: string) => {\n const match = /^(#{1,6})\\s+(.+?)\\s*#*\\s*$/.exec(line);\n\n if (!match) {\n return null;\n }\n\n return {\n level: match[1].length,\n heading: match[2].trim(),\n };\n};\n\nconst getLineStartOffsets = (lines: string[]) => {\n const offsets: number[] = [];\n let offset = 0;\n\n for (const line of lines) {\n offsets.push(offset);\n offset += line.length + 1;\n }\n\n return offsets;\n};\n\nconst findHeadingMatches = (markdown: string, placement: MarkdownAppendPlacementAfterHeading) => {\n const lines = splitLines(markdown);\n const offsets = getLineStartOffsets(lines);\n const headingLines = lines\n .map((line, index) => {\n const heading = parseHeadingLine(line);\n\n if (!heading) {\n return null;\n }\n\n return {\n ...heading,\n lineIndex: index,\n start: offsets[index],\n };\n })\n .filter((heading): heading is Omit<HeadingMatch, 'nextSectionStart'> => heading !== null);\n const matches = headingLines.filter((heading) => {\n return (\n heading.heading === placement.heading &&\n (placement.level === undefined || heading.level === placement.level)\n );\n });\n\n return matches.map((match) => {\n const nextHeading = headingLines.find(\n (heading) => heading.lineIndex > match.lineIndex && heading.level <= match.level,\n );\n\n return {\n ...match,\n nextSectionStart: nextHeading?.start ?? markdown.length,\n };\n });\n};\n\nconst appendWithSeparator = (prefix: string, insertion: string, separator: '\\n\\n' | '\\n') => {\n if (prefix.length === 0) {\n return insertion;\n }\n\n return `${prefix}${prefix.endsWith(separator) ? '' : separator}${insertion}`;\n};\n\nconst applyAppendPlacement = (\n markdown: string,\n insertion: string,\n placement: MarkdownAppendPlacement,\n separator: '\\n\\n' | '\\n',\n): { markdown: string; placement: MarkdownAppendPlacement } | MarkdownChangeFailure => {\n if (insertion.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_INSERTION',\n message: 'Append insertion must not be empty.',\n };\n }\n\n if (placement.type === 'end') {\n return {\n markdown: appendWithSeparator(markdown, insertion, separator),\n placement,\n };\n }\n\n if (\n placement.level !== undefined &&\n (!Number.isInteger(placement.level) || placement.level < 1 || placement.level > 6)\n ) {\n return {\n status: 'failed',\n reason: 'INVALID_HEADING_LEVEL',\n message: 'Heading level must be an integer from 1 to 6.',\n };\n }\n\n const headingMatches = findHeadingMatches(markdown, placement);\n\n if (headingMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'HEADING_NOT_FOUND',\n message: 'The requested heading was not found.',\n };\n }\n\n if (headingMatches.length > 1) {\n return {\n status: 'failed',\n reason: 'HEADING_AMBIGUOUS',\n message: 'The requested heading appears more than once.',\n };\n }\n\n const [headingMatch] = headingMatches;\n const prefix = markdown.slice(0, headingMatch.nextSectionStart);\n const suffix = markdown.slice(headingMatch.nextSectionStart);\n const prefixWithInsertion = appendWithSeparator(prefix, insertion, separator);\n const nextMarkdown =\n suffix.length === 0\n ? prefixWithInsertion\n : `${prefixWithInsertion}${prefixWithInsertion.endsWith(separator) ? '' : separator}${suffix}`;\n\n return {\n markdown: nextMarkdown,\n placement,\n };\n};\n\nexport const buildMarkdownAppendPlan = (input: PreviewMarkdownAppendInput): MarkdownAppendPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const placement = input.placement ?? { type: 'end' as const };\n const separator = input.separator ?? '\\n\\n';\n const appended = applyAppendPlacement(input.note.markdown, input.insertion, placement, separator);\n\n if ('status' in appended) {\n return appended;\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: appended.markdown,\n changedCharCount: input.insertion.length,\n policy: input.policy,\n placement: appended.placement,\n });\n};\n\nexport const buildMarkdownReplacePlan = (input: PreviewMarkdownReplaceInput): MarkdownReplacePlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n if (input.replacement.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Replacement markdown must not be empty.',\n };\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: input.replacement,\n changedCharCount: input.note.markdown.length + input.replacement.length,\n policy: input.policy,\n diffContextLines: null,\n });\n};\n\nconst toPreviewResult = (plan: MarkdownChangePlan): MarkdownChangeDryRun => {\n return {\n status: 'dry_run',\n note: plan.note,\n ...(plan.match ? { match: plan.match } : {}),\n ...(plan.placement ? { placement: plan.placement } : {}),\n proposed: plan.proposed,\n warnings: plan.warnings,\n };\n};\n\nexport const previewMarkdownPatch = (input: PreviewMarkdownPatchInput): MarkdownPatchPreviewResult => {\n const plan = buildMarkdownPatchPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownAppend = (\n input: PreviewMarkdownAppendInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownAppendPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownReplace = (\n input: PreviewMarkdownReplaceInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownReplacePlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n"],"mappings":"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","names":["match"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/services/properties.ts"],"sourcesContent":["import models, { type Note, type NoteLayout, Prisma, type PropertyValueType } from '~/models.js';\nimport { buildNoteSearchProjection } from './search.js';\nimport { captureNoteBaseline } from './snapshot.js';\nimport { createNoteVersionConflictError, MissingNoteVersionError, parseNoteVersion } from './write-conflict.js';\n\nexport interface SerializedNotePropertyOption {\n id: string;\n label: string;\n value: string;\n color?: string | null;\n order: number;\n}\n\nexport interface SerializedNoteProperty {\n key: string;\n name: string;\n value: string;\n valueType: PropertyValueType;\n option?: SerializedNotePropertyOption | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SerializedNotePropertyKey {\n key: string;\n name: string;\n valueType: PropertyValueType;\n noteCount: number;\n options: SerializedNotePropertyOption[];\n updatedAt: string;\n}\n\nexport interface SerializedNotePropertyKeysResult {\n totalCount: number;\n keys: SerializedNotePropertyKey[];\n}\n\nexport interface SerializedNotePropertyDeleteResult {\n key: string;\n name: string;\n valueType: PropertyValueType;\n affectedNoteCount: number;\n deleted: boolean;\n}\n\nexport interface NotePropertySetInput {\n key: string;\n name?: string | null;\n value: string;\n valueType: PropertyValueType;\n}\n\nexport interface NotePropertiesPatchInput {\n set?: NotePropertySetInput[] | null;\n deleteKeys?: string[] | null;\n}\n\nexport interface NotePropertySetByKeyInput {\n key: string;\n value: string;\n}\n\nexport interface NotePropertiesByKeyPatchInput {\n set?: NotePropertySetByKeyInput[] | null;\n deleteKeys?: string[] | null;\n}\n\nexport interface NotePropertyOptionInput {\n label: string;\n value?: string | null;\n color?: string | null;\n order?: number | null;\n}\n\nexport interface NotePropertyOptionUpdateInput extends NotePropertyOptionInput {\n id?: string | number | null;\n}\n\nexport interface NotePropertyDefinitionInput {\n key: string;\n name?: string | null;\n valueType: PropertyValueType;\n options?: NotePropertyOptionInput[] | null;\n}\n\nexport interface NotePropertyDefinitionUpdateInput {\n name?: string | null;\n options?: NotePropertyOptionUpdateInput[] | null;\n}\n\ntype NotePropertyWithDefinition = Prisma.NotePropertyGetPayload<{\n include: { definition: true; option: true };\n}>;\n\ntype PropertyDefinitionWithOptions = Prisma.PropertyDefinitionGetPayload<{\n include: { options: true; _count: { select: { properties: true } } };\n}>;\n\ntype PropertyPatchDefinitionLookupClient = Pick<typeof models, 'propertyDefinition'>;\ntype PropertyPatchValueValidationClient = Pick<typeof models, 'propertyDefinition' | 'propertyOption'>;\n\nconst PROPERTY_KEY_MAX_LENGTH = 80;\nconst PROPERTY_NAME_MAX_LENGTH = 120;\nconst PROPERTY_TEXT_VALUE_MAX_LENGTH = 2000;\nconst PROPERTY_OPTION_LABEL_MAX_LENGTH = 80;\nconst PROPERTY_OPTION_VALUE_MAX_LENGTH = 80;\nconst PROPERTY_OPTION_LIMIT = 50;\nconst PROPERTY_PATCH_SET_LIMIT = 50;\nconst PROPERTY_PATCH_DELETE_LIMIT = 50;\n\nexport class InvalidNotePropertyInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidNotePropertyInputError';\n }\n}\n\nexport class NotePropertyDeleteConfirmationRequiredError extends Error {\n code = 'NOTE_PROPERTY_DELETE_CONFIRMATION_REQUIRED' as const;\n affectedNoteCount: number;\n\n constructor(key: string, affectedNoteCount: number) {\n super(`Property ${key} is used by ${affectedNoteCount} note(s). Confirm deletion to remove those values.`);\n this.name = 'NotePropertyDeleteConfirmationRequiredError';\n this.affectedNoteCount = affectedNoteCount;\n }\n}\n\nconst isRecordNotFoundError = (error: unknown) => {\n return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025';\n};\n\nexport const normalizePropertyKey = (value: string) => {\n const normalized = value.trim().toLowerCase().replace(/\\s+/g, '-');\n\n if (!normalized) {\n throw new InvalidNotePropertyInputError('Property key is required.');\n }\n\n if (normalized.length > PROPERTY_KEY_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(`Property key must be ${PROPERTY_KEY_MAX_LENGTH} characters or fewer.`);\n }\n\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(normalized)) {\n throw new InvalidNotePropertyInputError('Property key must use letters, numbers, dashes, or underscores.');\n }\n\n return normalized;\n};\n\nconst normalizePropertyName = (value: string | null | undefined, key: string) => {\n const normalized = value?.trim() || key;\n\n if (normalized.length > PROPERTY_NAME_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property name must be ${PROPERTY_NAME_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n return normalized;\n};\n\nconst normalizeOptionValue = (value: string) => {\n const normalized = value.trim().toLowerCase().replace(/\\s+/g, '-');\n\n if (!normalized) {\n throw new InvalidNotePropertyInputError('Property option value is required.');\n }\n\n if (normalized.length > PROPERTY_OPTION_VALUE_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property option value must be ${PROPERTY_OPTION_VALUE_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(normalized)) {\n throw new InvalidNotePropertyInputError(\n 'Property option value must use letters, numbers, dashes, or underscores.',\n );\n }\n\n return normalized;\n};\n\nconst normalizeOptionLabel = (value: string) => {\n const normalized = value.trim();\n\n if (!normalized) {\n throw new InvalidNotePropertyInputError('Property option label is required.');\n }\n\n if (normalized.length > PROPERTY_OPTION_LABEL_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property option label must be ${PROPERTY_OPTION_LABEL_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n return normalized;\n};\n\nconst normalizePropertyOptions = (options: NotePropertyOptionInput[] | null | undefined) => {\n const normalizedOptions = options ?? [];\n\n if (normalizedOptions.length > PROPERTY_OPTION_LIMIT) {\n throw new InvalidNotePropertyInputError(`Select properties can have up to ${PROPERTY_OPTION_LIMIT} options.`);\n }\n\n const seenValues = new Set<string>();\n\n return normalizedOptions.map((option, index) => {\n const label = normalizeOptionLabel(option.label);\n const value = normalizeOptionValue(option.value ?? label);\n\n if (seenValues.has(value)) {\n throw new InvalidNotePropertyInputError('Property options contain duplicate values.');\n }\n\n seenValues.add(value);\n\n return {\n label,\n value,\n color: option.color?.trim() || null,\n order: Number.isFinite(Number(option.order)) ? Number(option.order) : index,\n };\n });\n};\n\nconst normalizePropertyOptionUpdates = (\n options: NotePropertyOptionUpdateInput[] | null | undefined,\n existingOptions: Array<{ id: number; value: string }>,\n) => {\n const normalizedOptions = options ?? [];\n\n if (normalizedOptions.length > PROPERTY_OPTION_LIMIT) {\n throw new InvalidNotePropertyInputError(`Select properties can have up to ${PROPERTY_OPTION_LIMIT} options.`);\n }\n\n const existingOptionById = new Map(existingOptions.map((option) => [String(option.id), option]));\n const seenValues = new Set<string>();\n\n return normalizedOptions.map((option, index) => {\n const label = normalizeOptionLabel(option.label);\n const existingOption =\n option.id === undefined || option.id === null ? null : existingOptionById.get(String(option.id));\n\n if (option.id !== undefined && option.id !== null && !existingOption) {\n throw new InvalidNotePropertyInputError('Property option does not belong to this property.');\n }\n\n const value = normalizeOptionValue(option.value ?? existingOption?.value ?? label);\n\n if (existingOption && value !== existingOption.value) {\n throw new InvalidNotePropertyInputError(\n `Property option ${existingOption.value} value cannot be changed. Create a new option instead.`,\n );\n }\n\n if (seenValues.has(value)) {\n throw new InvalidNotePropertyInputError('Property options contain duplicate values.');\n }\n\n seenValues.add(value);\n\n return {\n id: existingOption?.id,\n label,\n value,\n color: option.color?.trim() || null,\n order: Number.isFinite(Number(option.order)) ? Number(option.order) : index,\n };\n });\n};\n\nconst normalizeTextValue = (value: string) => {\n if (value.length > PROPERTY_TEXT_VALUE_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property text value must be ${PROPERTY_TEXT_VALUE_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n return value;\n};\n\nexport const normalizeUrlValue = (value: string) => {\n const trimmedValue = normalizeTextValue(value.trim());\n\n if (!trimmedValue) {\n throw new InvalidNotePropertyInputError('URL property value is required.');\n }\n\n let url: URL;\n\n try {\n url = new URL(trimmedValue);\n } catch {\n throw new InvalidNotePropertyInputError('URL property values must be valid http(s) URLs.');\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new InvalidNotePropertyInputError('URL property values must use http or https.');\n }\n\n return url.toString();\n};\n\nconst normalizeDateValue = (value: string) => {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n throw new InvalidNotePropertyInputError('Date property values must use YYYY-MM-DD.');\n }\n\n const date = new Date(`${value}T00:00:00.000Z`);\n\n if (Number.isNaN(date.getTime()) || date.toISOString().slice(0, 10) !== value) {\n throw new InvalidNotePropertyInputError('Date property value is invalid.');\n }\n\n return date;\n};\n\nconst normalizeBooleanValue = (value: string) => {\n if (value === 'true') {\n return true;\n }\n\n if (value === 'false') {\n return false;\n }\n\n throw new InvalidNotePropertyInputError('Boolean property values must be true or false.');\n};\n\nconst normalizeNumberValue = (value: string) => {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n throw new InvalidNotePropertyInputError('Number property value must be finite.');\n }\n\n return numberValue;\n};\n\nconst serializeOption = (option: NonNullable<NotePropertyWithDefinition['option']>): SerializedNotePropertyOption => ({\n id: String(option.id),\n label: option.label,\n value: option.value,\n color: option.color,\n order: option.order,\n});\n\nconst serializeDefinitionOption = (\n option: PropertyDefinitionWithOptions['options'][number],\n): SerializedNotePropertyOption => ({\n id: String(option.id),\n label: option.label,\n value: option.value,\n color: option.color,\n order: option.order,\n});\n\nconst serializePropertyValue = (property: NotePropertyWithDefinition) => {\n switch (property.definition.valueType) {\n case 'number':\n return property.numberValue === null ? '' : String(property.numberValue);\n case 'date':\n return property.dateValue ? property.dateValue.toISOString().slice(0, 10) : '';\n case 'boolean':\n return property.boolValue === null ? '' : String(property.boolValue);\n case 'select':\n return property.option?.value ?? '';\n case 'url':\n case 'text':\n default:\n return property.textValue ?? '';\n }\n};\n\nexport const serializeNoteProperty = (property: NotePropertyWithDefinition): SerializedNoteProperty => ({\n key: property.definition.key,\n name: property.definition.name,\n value: serializePropertyValue(property),\n valueType: property.definition.valueType,\n option: property.option ? serializeOption(property.option) : null,\n createdAt: property.createdAt.toISOString(),\n updatedAt: property.updatedAt.toISOString(),\n});\n\nexport const serializeNoteProperties = (properties: NotePropertyWithDefinition[]) => {\n return properties.map(serializeNoteProperty).sort((left, right) => left.key.localeCompare(right.key));\n};\n\nconst serializePropertyDefinition = (definition: PropertyDefinitionWithOptions): SerializedNotePropertyKey => ({\n key: definition.key,\n name: definition.name,\n valueType: definition.valueType,\n noteCount: definition._count.properties,\n options: definition.options.sort((left, right) => left.order - right.order).map(serializeDefinitionOption),\n updatedAt: definition.updatedAt.toISOString(),\n});\n\nexport const assertPropertyOptionUpdateKeepsUsedValues = ({\n existingOptions,\n nextOptions,\n}: {\n existingOptions: Array<{ value: string; _count: { properties: number } }>;\n nextOptions: Array<{ value: string }>;\n}) => {\n const nextValues = new Set(nextOptions.map((option) => option.value));\n const removedUsedOption = existingOptions.find(\n (option) => option._count.properties > 0 && !nextValues.has(option.value),\n );\n\n if (removedUsedOption) {\n throw new InvalidNotePropertyInputError(\n `Property option ${removedUsedOption.value} is used by notes and cannot be removed.`,\n );\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n};\n\nconst parseViewQueryRecord = (value: string | null) => {\n if (!value) {\n return null;\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n};\n\nconst getNormalizedViewFilterKey = (filter: Record<string, unknown>) => {\n if (typeof filter.key !== 'string') {\n return null;\n }\n\n try {\n return normalizePropertyKey(filter.key);\n } catch {\n return null;\n }\n};\n\nexport const renamePropertyFiltersInViewQuery = ({\n query,\n key,\n name,\n}: {\n query: string | null;\n key: string;\n name: string;\n}) => {\n const parsed = parseViewQueryRecord(query);\n\n if (!parsed || !Array.isArray(parsed.propertyFilters)) {\n return null;\n }\n\n let changed = false;\n const propertyFilters = parsed.propertyFilters.map((filter) => {\n if (!isRecord(filter) || getNormalizedViewFilterKey(filter) !== key) {\n return filter;\n }\n\n changed = true;\n return {\n ...filter,\n key,\n name,\n };\n });\n\n if (!changed) {\n return null;\n }\n\n return JSON.stringify({\n ...parsed,\n propertyFilters,\n });\n};\n\nconst normalizeReferencedOptionValue = (value: unknown) => {\n return typeof value === 'string' ? normalizeOptionValue(value) : null;\n};\n\nexport const findReferencedRemovedPropertyOptionValue = ({\n query,\n key,\n removedValues,\n}: {\n query: string | null;\n key: string;\n removedValues: Set<string>;\n}) => {\n const parsed = parseViewQueryRecord(query);\n\n if (!parsed || !Array.isArray(parsed.propertyFilters)) {\n return null;\n }\n\n for (const filter of parsed.propertyFilters) {\n if (!isRecord(filter) || getNormalizedViewFilterKey(filter) !== key) {\n continue;\n }\n\n if (filter.valueType !== 'select' || filter.operator === 'exists' || filter.operator === 'notExists') {\n continue;\n }\n\n try {\n const optionValue = normalizeReferencedOptionValue(filter.value);\n\n if (optionValue && removedValues.has(optionValue)) {\n return optionValue;\n }\n } catch {\n continue;\n }\n }\n\n return null;\n};\n\nconst buildTypedValueData = async (\n input: NotePropertySetInput,\n definitionId: number,\n tx: Pick<typeof models, 'propertyOption'> = models,\n) => {\n const resetValues = {\n textValue: null,\n textValueNormalized: null,\n numberValue: null,\n dateValue: null,\n boolValue: null,\n optionId: null,\n };\n\n switch (input.valueType) {\n case 'number': {\n const numberValue = normalizeNumberValue(input.value);\n return { ...resetValues, numberValue };\n }\n case 'date': {\n const dateValue = normalizeDateValue(input.value);\n return { ...resetValues, dateValue };\n }\n case 'boolean': {\n const boolValue = normalizeBooleanValue(input.value);\n return { ...resetValues, boolValue };\n }\n case 'select': {\n const optionValue = normalizeOptionValue(input.value);\n const option = await tx.propertyOption.findUnique({\n where: {\n propertyDefinitionId_value: {\n propertyDefinitionId: definitionId,\n value: optionValue,\n },\n },\n });\n\n if (!option) {\n throw new InvalidNotePropertyInputError(`Property option ${optionValue} is not defined.`);\n }\n\n return { ...resetValues, optionId: option.id };\n }\n case 'url': {\n const urlValue = normalizeUrlValue(input.value);\n return { ...resetValues, textValue: urlValue, textValueNormalized: urlValue.toLowerCase() };\n }\n case 'text': {\n const textValue = normalizeTextValue(input.value);\n return { ...resetValues, textValue, textValueNormalized: textValue.toLowerCase() };\n }\n default:\n throw new InvalidNotePropertyInputError('Unsupported property value type.');\n }\n};\n\nconst normalizePatch = (patch: NotePropertiesPatchInput) => {\n const set = patch.set ?? [];\n const deleteKeys = patch.deleteKeys ?? [];\n\n if (set.length === 0 && deleteKeys.length === 0) {\n throw new InvalidNotePropertyInputError('At least one property change is required.');\n }\n\n if (set.length > PROPERTY_PATCH_SET_LIMIT || deleteKeys.length > PROPERTY_PATCH_DELETE_LIMIT) {\n throw new InvalidNotePropertyInputError('Too many property changes in one request.');\n }\n\n const normalizedSet = set.map((item) => {\n const key = normalizePropertyKey(item.key);\n return {\n ...item,\n key,\n name: normalizePropertyName(item.name, key),\n };\n });\n const normalizedDeleteKeys = deleteKeys.map(normalizePropertyKey);\n const seenSetKeys = new Set<string>();\n const seenDeleteKeys = new Set<string>();\n\n for (const item of normalizedSet) {\n if (seenSetKeys.has(item.key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate keys.');\n }\n\n seenSetKeys.add(item.key);\n }\n\n for (const key of normalizedDeleteKeys) {\n if (seenDeleteKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate delete keys.');\n }\n\n if (seenSetKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch cannot set and delete the same key.');\n }\n\n seenDeleteKeys.add(key);\n }\n\n return { set: normalizedSet, deleteKeys: normalizedDeleteKeys };\n};\n\nconst normalizePatchByKey = (patch: NotePropertiesByKeyPatchInput) => {\n const set = patch.set ?? [];\n const deleteKeys = patch.deleteKeys ?? [];\n\n if (set.length === 0 && deleteKeys.length === 0) {\n throw new InvalidNotePropertyInputError('At least one property change is required.');\n }\n\n if (set.length > PROPERTY_PATCH_SET_LIMIT || deleteKeys.length > PROPERTY_PATCH_DELETE_LIMIT) {\n throw new InvalidNotePropertyInputError('Too many property changes in one request.');\n }\n\n const normalizedSet = set.map((item) => ({\n key: normalizePropertyKey(item.key),\n value: item.value,\n }));\n const normalizedDeleteKeys = deleteKeys.map(normalizePropertyKey);\n const seenSetKeys = new Set<string>();\n const seenDeleteKeys = new Set<string>();\n\n for (const item of normalizedSet) {\n if (seenSetKeys.has(item.key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate keys.');\n }\n\n seenSetKeys.add(item.key);\n }\n\n for (const key of normalizedDeleteKeys) {\n if (seenDeleteKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate delete keys.');\n }\n\n if (seenSetKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch cannot set and delete the same key.');\n }\n\n seenDeleteKeys.add(key);\n }\n\n return { set: normalizedSet, deleteKeys: normalizedDeleteKeys };\n};\n\nexport const resolveNotePropertiesPatchValueTypes = async (\n patch: NotePropertiesByKeyPatchInput,\n tx: PropertyPatchDefinitionLookupClient = models,\n): Promise<NotePropertiesPatchInput> => {\n const normalizedPatch = normalizePatchByKey(patch);\n\n if (normalizedPatch.set.length === 0) {\n return normalizePatch({ deleteKeys: normalizedPatch.deleteKeys });\n }\n\n const definitions = await tx.propertyDefinition.findMany({\n where: { key: { in: normalizedPatch.set.map((item) => item.key) } },\n select: {\n key: true,\n name: true,\n valueType: true,\n },\n });\n const definitionByKey = new Map(definitions.map((definition) => [definition.key, definition]));\n\n return normalizePatch({\n set: normalizedPatch.set.map((item) => {\n const definition = definitionByKey.get(item.key);\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} is not defined. Create it in property settings first.`,\n );\n }\n\n return {\n key: item.key,\n name: definition.name,\n value: item.value,\n valueType: definition.valueType,\n };\n }),\n deleteKeys: normalizedPatch.deleteKeys,\n });\n};\n\nexport const validateNotePropertiesPatchValues = async (\n patch: NotePropertiesPatchInput,\n tx: PropertyPatchValueValidationClient = models,\n) => {\n const normalizedPatch = normalizePatch(patch);\n\n for (const item of normalizedPatch.set) {\n const definition = await tx.propertyDefinition.findUnique({ where: { key: item.key } });\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} is not defined. Create it in property settings first.`,\n );\n }\n\n if (definition.valueType !== item.valueType) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} already uses ${definition.valueType} values.`,\n );\n }\n\n await buildTypedValueData(item, definition.id, tx);\n }\n\n return normalizedPatch;\n};\n\nexport const listNoteProperties = async (noteId: number) => {\n const properties = await models.noteProperty.findMany({\n where: { noteId },\n include: { definition: true, option: true },\n orderBy: [{ definition: { key: 'asc' } }],\n });\n\n return serializeNoteProperties(properties);\n};\n\nexport const listNotePropertyKeys = async (input?: { query?: string; limit?: number; offset?: number }) => {\n const normalizedQuery = input?.query?.trim();\n const where = normalizedQuery\n ? {\n OR: [{ key: { contains: normalizedQuery } }, { name: { contains: normalizedQuery } }],\n }\n : undefined;\n const take = Math.max(1, Math.min(100, Number(input?.limit ?? 50)));\n const skip = Math.max(0, Number(input?.offset ?? 0));\n const [definitions, totalCount] = await Promise.all([\n models.propertyDefinition.findMany({\n where,\n orderBy: { key: 'asc' },\n take,\n skip,\n include: { options: { orderBy: { order: 'asc' } }, _count: { select: { properties: true } } },\n }),\n models.propertyDefinition.count({ where }),\n ]);\n\n return {\n totalCount,\n keys: definitions.map(serializePropertyDefinition),\n } satisfies SerializedNotePropertyKeysResult;\n};\n\nexport const createNotePropertyDefinition = async (input: NotePropertyDefinitionInput) => {\n const key = normalizePropertyKey(input.key);\n const name = normalizePropertyName(input.name, key);\n const options = normalizePropertyOptions(input.options);\n\n if (input.valueType !== 'select' && options.length > 0) {\n throw new InvalidNotePropertyInputError('Only select properties can have options.');\n }\n\n if (input.valueType === 'select' && options.length === 0) {\n throw new InvalidNotePropertyInputError('Select properties require at least one option.');\n }\n\n try {\n const definition = await models.propertyDefinition.create({\n data: {\n key,\n name,\n valueType: input.valueType,\n ...(input.valueType === 'select'\n ? {\n options: {\n create: options,\n },\n }\n : {}),\n },\n include: { options: { orderBy: { order: 'asc' } }, _count: { select: { properties: true } } },\n });\n\n return serializePropertyDefinition(definition);\n } catch (error) {\n if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {\n throw new InvalidNotePropertyInputError(`Property ${key} already exists.`);\n }\n\n throw error;\n }\n};\n\nexport const updateNotePropertyDefinition = async ({\n key,\n input,\n}: {\n key: string;\n input: NotePropertyDefinitionUpdateInput;\n}): Promise<SerializedNotePropertyKey | null> => {\n const normalizedKey = normalizePropertyKey(key);\n\n return models.$transaction(async (tx) => {\n const definition = await tx.propertyDefinition.findUnique({\n where: { key: normalizedKey },\n include: {\n options: {\n orderBy: { order: 'asc' },\n include: { _count: { select: { properties: true } } },\n },\n _count: { select: { properties: true } },\n },\n });\n\n if (!definition) {\n return null;\n }\n\n const nextName = input.name !== undefined ? normalizePropertyName(input.name, definition.key) : definition.name;\n const shouldUpdateOptions = input.options !== undefined;\n let nextOptions: ReturnType<typeof normalizePropertyOptionUpdates> | null = null;\n\n if (shouldUpdateOptions) {\n nextOptions = normalizePropertyOptionUpdates(input.options, definition.options);\n\n if (definition.valueType !== 'select' && nextOptions.length > 0) {\n throw new InvalidNotePropertyInputError('Only select properties can have options.');\n }\n\n if (definition.valueType === 'select' && nextOptions.length === 0) {\n throw new InvalidNotePropertyInputError('Select properties require at least one option.');\n }\n\n assertPropertyOptionUpdateKeepsUsedValues({\n existingOptions: definition.options,\n nextOptions,\n });\n\n const nextValues = new Set(nextOptions.map((option) => option.value));\n const removedValues = new Set(\n definition.options.filter((option) => !nextValues.has(option.value)).map((option) => option.value),\n );\n\n if (removedValues.size > 0) {\n const referencingViewSections = await tx.viewSection.findMany({\n where: { query: { contains: definition.key } },\n select: { title: true, query: true },\n });\n\n for (const section of referencingViewSections) {\n const removedReferencedValue = findReferencedRemovedPropertyOptionValue({\n query: section.query,\n key: definition.key,\n removedValues,\n });\n\n if (removedReferencedValue) {\n throw new InvalidNotePropertyInputError(\n `Property option ${removedReferencedValue} is used by view ${section.title} and cannot be removed.`,\n );\n }\n }\n }\n }\n\n await tx.propertyDefinition.update({\n where: { id: definition.id },\n data: { name: nextName },\n });\n\n if (nextName !== definition.name) {\n const referencingViewSections = await tx.viewSection.findMany({\n where: { query: { contains: definition.key } },\n select: { id: true, query: true },\n });\n\n for (const section of referencingViewSections) {\n const nextQuery = renamePropertyFiltersInViewQuery({\n query: section.query,\n key: definition.key,\n name: nextName,\n });\n\n if (nextQuery) {\n await tx.viewSection.update({\n where: { id: section.id },\n data: { query: nextQuery },\n });\n }\n }\n }\n\n if (nextOptions) {\n const existingOptionByValue = new Map(definition.options.map((option) => [option.value, option]));\n const nextValues = new Set(nextOptions.map((option) => option.value));\n const removableOptionIds = definition.options\n .filter((option) => !nextValues.has(option.value) && option._count.properties === 0)\n .map((option) => option.id);\n\n if (removableOptionIds.length > 0) {\n await tx.propertyOption.deleteMany({\n where: {\n id: { in: removableOptionIds },\n },\n });\n }\n\n for (const option of nextOptions) {\n const existingOption = existingOptionByValue.get(option.value);\n\n if (existingOption) {\n await tx.propertyOption.update({\n where: { id: existingOption.id },\n data: {\n label: option.label,\n color: option.color,\n order: option.order,\n },\n });\n continue;\n }\n\n await tx.propertyOption.create({\n data: {\n propertyDefinitionId: definition.id,\n label: option.label,\n value: option.value,\n color: option.color,\n order: option.order,\n },\n });\n }\n }\n\n const updatedDefinition = await tx.propertyDefinition.findUniqueOrThrow({\n where: { id: definition.id },\n include: { options: { orderBy: { order: 'asc' } }, _count: { select: { properties: true } } },\n });\n\n return serializePropertyDefinition(updatedDefinition);\n });\n};\n\nexport const deleteNotePropertyDefinition = async ({\n key,\n confirmImpact = false,\n}: {\n key: string;\n confirmImpact?: boolean;\n}): Promise<SerializedNotePropertyDeleteResult | null> => {\n const normalizedKey = normalizePropertyKey(key);\n\n return models.$transaction(async (tx) => {\n const definition = await tx.propertyDefinition.findUnique({\n where: { key: normalizedKey },\n include: { _count: { select: { properties: true } } },\n });\n\n if (!definition) {\n return null;\n }\n\n const affectedNoteCount = definition._count.properties;\n\n if (affectedNoteCount > 0 && !confirmImpact) {\n throw new NotePropertyDeleteConfirmationRequiredError(normalizedKey, affectedNoteCount);\n }\n\n if (affectedNoteCount > 0) {\n await tx.note.updateMany({\n where: {\n properties: {\n some: {\n propertyDefinitionId: definition.id,\n },\n },\n },\n data: {\n updatedAt: new Date(),\n },\n });\n }\n\n await tx.propertyDefinition.delete({\n where: { id: definition.id },\n });\n\n return {\n key: definition.key,\n name: definition.name,\n valueType: definition.valueType,\n affectedNoteCount,\n deleted: true,\n };\n });\n};\n\ninterface UpdateNotePropertiesWithVersionGuardInput {\n id: number;\n patch: NotePropertiesPatchInput;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n force?: boolean;\n noteData?: {\n title?: string;\n layout?: NoteLayout;\n };\n snapshotMeta?: string;\n}\n\nexport interface GuardedNotePropertiesWriteResult {\n note: Note;\n snapshot: unknown;\n}\n\nexport const updateNotePropertiesWithVersionGuardAndSnapshot = async ({\n id,\n patch,\n editSessionId,\n expectedUpdatedAt,\n force = false,\n noteData,\n snapshotMeta,\n}: UpdateNotePropertiesWithVersionGuardInput): Promise<GuardedNotePropertiesWriteResult | null> => {\n const normalizedPatch = normalizePatch(patch);\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n if (expectedTimestamp === null && !force) {\n throw new MissingNoteVersionError();\n }\n\n try {\n const result = await models.$transaction(async (tx) => {\n const existingNote = await tx.note.findUnique({\n where: { id },\n include: { properties: { include: { definition: true, option: true } } },\n });\n\n if (!existingNote) {\n return null;\n }\n\n if (!force && existingNote.updatedAt.getTime() !== expectedTimestamp) {\n throw createNoteVersionConflictError({\n currentUpdatedAt: existingNote.updatedAt.getTime(),\n expectedUpdatedAt: expectedTimestamp ?? existingNote.updatedAt.getTime(),\n });\n }\n\n const baseline = {\n ...existingNote,\n properties: existingNote.properties.map((property) => ({\n key: property.definition.key,\n name: property.definition.name,\n valueType: property.definition.valueType,\n textValue: property.textValue,\n textValueNormalized: property.textValueNormalized,\n numberValue: property.numberValue,\n dateValue: property.dateValue,\n boolValue: property.boolValue,\n optionValue: property.option?.value ?? null,\n optionLabel: property.option?.label ?? null,\n optionColor: property.option?.color ?? null,\n })),\n };\n\n for (const item of normalizedPatch.set) {\n const definition = await tx.propertyDefinition.findUnique({ where: { key: item.key } });\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} is not defined. Create it in property settings first.`,\n );\n }\n\n if (definition.valueType !== item.valueType) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} already uses ${definition.valueType} values.`,\n );\n }\n\n const valueData = await buildTypedValueData(item, definition.id, tx);\n\n await tx.noteProperty.upsert({\n where: {\n noteId_propertyDefinitionId: {\n noteId: id,\n propertyDefinitionId: definition.id,\n },\n },\n create: {\n noteId: id,\n propertyDefinitionId: definition.id,\n ...valueData,\n },\n update: valueData,\n });\n }\n\n if (normalizedPatch.deleteKeys.length > 0) {\n const definitions = await tx.propertyDefinition.findMany({\n where: { key: { in: normalizedPatch.deleteKeys } },\n select: { id: true },\n });\n\n if (definitions.length > 0) {\n await tx.noteProperty.deleteMany({\n where: {\n noteId: id,\n propertyDefinitionId: { in: definitions.map((definition) => definition.id) },\n },\n });\n }\n }\n\n const updatedNote = await tx.note.update({\n where: { id },\n data: {\n ...(noteData?.title !== undefined ? { title: noteData.title } : {}),\n ...(noteData?.layout !== undefined ? { layout: noteData.layout } : {}),\n ...(noteData?.title !== undefined\n ? buildNoteSearchProjection({\n title: noteData.title,\n content: existingNote.content,\n })\n : {}),\n updatedAt: new Date(),\n },\n });\n\n return { updatedNote, baseline };\n });\n\n if (!result) {\n return null;\n }\n\n const snapshot = await captureNoteBaseline({\n noteId: id,\n baseline: result.baseline,\n ...(editSessionId && !force ? { editSessionId } : {}),\n ...(snapshotMeta ? { meta: snapshotMeta } : {}),\n ...(force ? { force: true } : {}),\n });\n\n return {\n note: result.updatedNote,\n snapshot,\n };\n } catch (error) {\n if (isRecordNotFoundError(error)) {\n return null;\n }\n\n throw error;\n }\n};\n\nexport const updateNotePropertiesWithVersionGuard = async (input: UpdateNotePropertiesWithVersionGuardInput) => {\n const result = await updateNotePropertiesWithVersionGuardAndSnapshot(input);\n return result?.note ?? null;\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/services/search-backfill.ts"],"sourcesContent":["import models from '~/models.js';\nimport { buildNoteSearchProjection, NOTE_SEARCH_TEXT_SCHEMA_VERSION, type NoteSearchProjection } from './search.js';\n\ninterface BackfillCandidate {\n id: number;\n title: string;\n content: string;\n}\n\ninterface NoteSearchBackfillDeps {\n listStaleNotes: (limit: number) => Promise<BackfillCandidate[]>;\n updateNotes: (updates: Array<{ id: number; projection: NoteSearchProjection }>) => Promise<void>;\n}\n\nconst DEFAULT_BACKFILL_BATCH_SIZE = 100;\nlet activeNoteSearchBackfillPromise: Promise<number> | null = null;\n\nconst yieldToEventLoop = async () => {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n};\n\nexport const createNoteSearchBackfillService = (deps: NoteSearchBackfillDeps) => {\n const backfillBatch = async (limit = DEFAULT_BACKFILL_BATCH_SIZE) => {\n const staleNotes = await deps.listStaleNotes(limit);\n\n if (staleNotes.length === 0) {\n return 0;\n }\n\n await deps.updateNotes(\n staleNotes.map((note) => ({\n id: note.id,\n projection: buildNoteSearchProjection(note),\n })),\n );\n\n return staleNotes.length;\n };\n\n const backfillAll = async (limit = DEFAULT_BACKFILL_BATCH_SIZE) => {\n let total = 0;\n\n while (true) {\n const count = await backfillBatch(limit);\n\n if (!count) {\n return total;\n }\n\n total += count;\n\n if (count < limit) {\n return total;\n }\n\n await yieldToEventLoop();\n }\n };\n\n return {\n backfillBatch,\n backfillAll,\n };\n};\n\nconst defaultNoteSearchBackfillService = createNoteSearchBackfillService({\n listStaleNotes: async (limit) => {\n return models.note.findMany({\n where: { searchableTextVersion: { not: NOTE_SEARCH_TEXT_SCHEMA_VERSION } },\n orderBy: { id: 'asc' },\n take: limit,\n select: {\n id: true,\n title: true,\n content: true,\n },\n });\n },\n updateNotes: async (updates) => {\n await models.$transaction(\n updates.map(({ id, projection }) =>\n models.note.update({\n where: { id },\n data: projection,\n }),\n ),\n );\n },\n});\n\nexport const backfillStaleNoteSearchText = async (limit?: number) => {\n return defaultNoteSearchBackfillService.backfillAll(limit);\n};\n\nexport const runNoteSearchBackfillInBackground = (limit?: number) => {\n if (activeNoteSearchBackfillPromise) {\n return activeNoteSearchBackfillPromise;\n }\n\n activeNoteSearchBackfillPromise = defaultNoteSearchBackfillService.backfillAll(limit).finally(() => {\n activeNoteSearchBackfillPromise = null;\n });\n\n return activeNoteSearchBackfillPromise;\n};\n"],"mappings":"AAAA,OAAO,YAAY;AACnB,SAAS,2BAA2B,uCAAkE;AAatG,MAAM,8BAA8B;AACpC,IAAI,kCAA0D;AAE9D,MAAM,mBAAmB,YAAY;AACjC,QAAM,IAAI,QAAc,CAAC,YAAY;AACjC,eAAW,SAAS,CAAC;AAAA,EACzB,CAAC;AACL;AAEO,MAAM,kCAAkC,CAAC,SAAiC;AAC7E,QAAM,gBAAgB,OAAO,QAAQ,gCAAgC;AACjE,UAAM,aAAa,MAAM,KAAK,eAAe,KAAK;AAElD,QAAI,WAAW,WAAW,GAAG;AACzB,aAAO;AAAA,IACX;AAEA,UAAM,KAAK;AAAA,MACP,WAAW,IAAI,CAAC,UAAU;AAAA,QACtB,IAAI,KAAK;AAAA,QACT,YAAY,0BAA0B,IAAI;AAAA,MAC9C,EAAE;AAAA,IACN;AAEA,WAAO,WAAW;AAAA,EACtB;AAEA,QAAM,cAAc,OAAO,QAAQ,gCAAgC;AAC/D,QAAI,QAAQ;AAEZ,WAAO,MAAM;AACT,YAAM,QAAQ,MAAM,cAAc,KAAK;AAEvC,UAAI,CAAC,OAAO;AACR,eAAO;AAAA,MACX;AAEA,eAAS;AAET,UAAI,QAAQ,OAAO;AACf,eAAO;AAAA,MACX;AAEA,YAAM,iBAAiB;AAAA,IAC3B;AAAA,EACJ;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,EACJ;AACJ;AAEA,MAAM,mCAAmC,gCAAgC;AAAA,EACrE,gBAAgB,OAAO,UAAU;AAC7B,WAAO,OAAO,KAAK,SAAS;AAAA,MACxB,OAAO,EAAE,uBAAuB,EAAE,KAAK,gCAAgC,EAAE;AAAA,MACzE,SAAS,EAAE,IAAI,MAAM;AAAA,MACrB,MAAM;AAAA,MACN,QAAQ;AAAA,QACJ,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,SAAS;AAAA,MACb;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EACA,aAAa,OAAO,YAAY;AAC5B,UAAM,OAAO;AAAA,MACT,QAAQ;AAAA,QAAI,CAAC,EAAE,IAAI,WAAW,MAC1B,OAAO,KAAK,OAAO;AAAA,UACf,OAAO,EAAE,GAAG;AAAA,UACZ,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AAAA,IACJ;AAAA,EACJ;AACJ,CAAC;AAEM,MAAM,8BAA8B,OAAO,UAAmB;AACjE,SAAO,iCAAiC,YAAY,KAAK;AAC7D;AAEO,MAAM,oCAAoC,CAAC,UAAmB;AACjE,MAAI,iCAAiC;AACjC,WAAO;AAAA,EACX;AAEA,oCAAkC,iCAAiC,YAAY,KAAK,EAAE,QAAQ,MAAM;AAChG,sCAAkC;AAAA,EACtC,CAAC;AAED,SAAO;AACX;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/services/search.ts"],"sourcesContent":["import { isBlockNoteTreeNode, walkBlockNoteTree } from '~/modules/blocknote-tree.js';\n\nexport interface NoteSearchQuery {\n included: string[];\n excluded: string[];\n hasFilters: boolean;\n}\n\ninterface SearchableNoteLike {\n title: string;\n content: string;\n searchableText?: string;\n searchableTextVersion?: number;\n}\n\nexport interface NoteSearchProjection {\n searchableText: string;\n searchableTextVersion: number;\n}\n\ninterface SearchProjectionContext {\n segments: string[];\n}\n\ntype SearchableJsonNode = Record<string, unknown>;\ntype SearchNodeExtractor = (node: SearchableJsonNode, context: SearchProjectionContext) => void;\ntype SearchExtractorNodeType = (typeof NOTE_SEARCH_EXTRACTOR_NODE_TYPES)[number];\n\n// If we later materialize searchable text into a DB column, bump this when the\n// extraction meaning changes so stored projections can be rebuilt safely.\nexport const NOTE_SEARCH_TEXT_SCHEMA_VERSION = 1;\n\nconst FILE_BLOCK_VISIBLE_PROP_KEYS = ['name', 'caption'] as const;\n\nexport const NOTE_SEARCH_EXTRACTOR_NODE_TYPES = [\n 'text',\n 'tag',\n 'reference',\n 'image',\n 'video',\n 'audio',\n 'file',\n] as const;\n\nexport const NOTE_SEARCH_PASS_THROUGH_NODE_TYPES = [\n 'link',\n 'paragraph',\n 'heading',\n 'bulletListItem',\n 'numberedListItem',\n 'checkListItem',\n 'toggleListItem',\n 'quote',\n 'codeBlock',\n 'divider',\n 'table',\n 'tableContent',\n 'tableCell',\n] as const;\n\nexport const NOTE_SEARCH_IGNORED_NODE_TYPES = ['tableOfContents'] as const;\n\nconst KNOWN_NOTE_SEARCH_NODE_TYPES = new Set<string>([\n ...NOTE_SEARCH_EXTRACTOR_NODE_TYPES,\n ...NOTE_SEARCH_PASS_THROUGH_NODE_TYPES,\n ...NOTE_SEARCH_IGNORED_NODE_TYPES,\n]);\nconst SEARCH_NODE_EXTRACTOR_TYPE_SET = new Set<string>(NOTE_SEARCH_EXTRACTOR_NODE_TYPES);\n\nconst unknownNoteSearchNodeTypeCounts = new Map<string, number>();\n\nconst SEARCH_NODE_EXTRACTORS: Record<SearchExtractorNodeType, SearchNodeExtractor> = {\n text: (node, context) => {\n pushVisibleSegment(context, node.text);\n },\n tag: (node, context) => {\n pushVisibleProps(context, node.props, ['tag']);\n },\n reference: (node, context) => {\n pushVisibleProps(context, node.props, ['title']);\n },\n image: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n video: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n audio: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n file: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n};\n\nconst recordUnknownNoteSearchNodeType = (type: string) => {\n if (KNOWN_NOTE_SEARCH_NODE_TYPES.has(type)) {\n return;\n }\n\n const nextCount = (unknownNoteSearchNodeTypeCounts.get(type) ?? 0) + 1;\n unknownNoteSearchNodeTypeCounts.set(type, nextCount);\n\n if (nextCount === 1) {\n process.emitWarning(\n `note-search encountered unsupported BlockNote node type \"${type}\". Only nested text will be indexed until explicit support is added.`,\n { code: 'OCEAN_BRAIN_NOTE_SEARCH_UNKNOWN_NODE' },\n );\n }\n};\n\nconst isSearchExtractorNodeType = (type: string): type is SearchExtractorNodeType => {\n return SEARCH_NODE_EXTRACTOR_TYPE_SET.has(type);\n};\n\nconst normalizeSearchText = (value: string) => value.replace(/\\s+/g, ' ').trim();\n\nconst normalizeSearchToken = (value: string) => normalizeSearchText(value).toLowerCase();\n\nconst pushVisibleSegment = (context: SearchProjectionContext, value: unknown) => {\n if (typeof value !== 'string') {\n return;\n }\n\n const normalizedValue = normalizeSearchText(value);\n\n if (!normalizedValue) {\n return;\n }\n\n context.segments.push(normalizedValue);\n};\n\nconst pushVisibleProps = (context: SearchProjectionContext, props: unknown, keys: readonly string[]) => {\n if (!isBlockNoteTreeNode(props)) {\n return;\n }\n\n for (const key of keys) {\n pushVisibleSegment(context, props[key]);\n }\n};\n\nconst collectVisibleSearchSegments = (node: unknown, context: SearchProjectionContext) => {\n walkBlockNoteTree(node, (item) => {\n const type = typeof item.type === 'string' ? item.type : undefined;\n\n if (type) {\n if (isSearchExtractorNodeType(type)) {\n SEARCH_NODE_EXTRACTORS[type](item, context);\n }\n\n recordUnknownNoteSearchNodeType(type);\n }\n });\n};\n\nexport const buildNoteSearchText = (note: Pick<SearchableNoteLike, 'title' | 'content'>) => {\n const normalizedTitle = normalizeSearchText(note.title);\n const normalizedContent = extractVisibleSearchTextFromContent(note.content);\n\n return normalizeSearchToken(`${normalizedTitle} ${normalizedContent}`);\n};\n\nexport const buildNoteSearchProjection = (\n note: Pick<SearchableNoteLike, 'title' | 'content'>,\n): NoteSearchProjection => {\n return {\n searchableText: buildNoteSearchText(note),\n searchableTextVersion: NOTE_SEARCH_TEXT_SCHEMA_VERSION,\n };\n};\n\nexport const parseNoteSearchQuery = (query: string): NoteSearchQuery => {\n const included: string[] = [];\n const excluded: string[] = [];\n\n for (const item of query\n .split(/\\s+/)\n .map((value) => value.trim())\n .filter(Boolean)) {\n if (item.startsWith('-') && item.length > 1) {\n excluded.push(normalizeSearchToken(item.slice(1)));\n continue;\n }\n\n included.push(normalizeSearchToken(item));\n }\n\n return {\n included,\n excluded,\n hasFilters: included.length > 0 || excluded.length > 0,\n };\n};\n\nexport const extractVisibleSearchTextFromContent = (content: string) => {\n try {\n const parsed = JSON.parse(content) as unknown;\n const context: SearchProjectionContext = { segments: [] };\n\n collectVisibleSearchSegments(parsed, context);\n\n return normalizeSearchText(context.segments.join(' '));\n } catch {\n return '';\n }\n};\n\nexport const matchesNoteSearchQuery = (note: SearchableNoteLike, query: string | NoteSearchQuery) => {\n const parsedQuery = typeof query === 'string' ? parseNoteSearchQuery(query) : query;\n\n if (!parsedQuery.hasFilters) {\n return true;\n }\n\n const haystack =\n note.searchableTextVersion === NOTE_SEARCH_TEXT_SCHEMA_VERSION && typeof note.searchableText === 'string'\n ? normalizeSearchToken(note.searchableText)\n : buildNoteSearchText(note);\n\n return (\n parsedQuery.included.every((term) => haystack.includes(term)) &&\n parsedQuery.excluded.every((term) => !haystack.includes(term))\n );\n};\n\nexport const filterNotesBySearchQuery = <T extends SearchableNoteLike>(notes: T[], query: string | NoteSearchQuery) => {\n const parsedQuery = typeof query === 'string' ? parseNoteSearchQuery(query) : query;\n\n if (!parsedQuery.hasFilters) {\n return notes;\n }\n\n return notes.filter((note) => matchesNoteSearchQuery(note, parsedQuery));\n};\n\nexport const getUnknownNoteSearchNodeTypeCounts = () => {\n return new Map(unknownNoteSearchNodeTypeCounts);\n};\n\nexport const resetUnknownNoteSearchNodeTypeCountsForTest = () => {\n unknownNoteSearchNodeTypeCounts.clear();\n};\n"],"mappings":"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","names":[]}