ocean-brain 0.8.0 → 0.8.2

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 (279) hide show
  1. package/README.md +113 -0
  2. package/dist/mcp.js +230 -291
  3. package/package.json +6 -5
  4. package/server/client/dist/assets/ArrowRight.es-g2YZTklZ.js +1 -0
  5. package/server/client/dist/assets/Calendar-BP6HTuSn.js +25 -0
  6. package/server/client/dist/assets/Callout-B_IPSF_5.js +1 -0
  7. package/server/client/dist/assets/Copy.es-CNxe4ujr.js +1 -0
  8. package/server/client/dist/assets/Graph-D9GeO71k.js +1 -0
  9. package/server/client/dist/assets/Graph-Xha3fwhp.js +46 -0
  10. package/server/client/dist/assets/Image-BLEjLUqd.js +1 -0
  11. package/server/client/dist/assets/Image.es-DnON8cv2.js +1 -0
  12. package/server/client/dist/assets/Note-DjrCYtat.js +24 -0
  13. package/server/client/dist/assets/NotePropertiesPanel-2DgXEM4B.js +2 -0
  14. package/server/client/dist/assets/Plus.es-CQ8wn2is.js +1 -0
  15. package/server/client/dist/assets/Reminders-B4l3KdlK.js +1 -0
  16. package/server/client/dist/assets/Search-BN5sf7bE.js +1 -0
  17. package/server/client/dist/assets/SurfaceCard-BZm73nyS.js +1 -0
  18. package/server/client/dist/assets/Tag-oSnmViVp.js +1 -0
  19. package/server/client/dist/assets/TagNotes-BDTCcJcL.js +1 -0
  20. package/server/client/dist/assets/Trash.es-0aPpA-3S.js +1 -0
  21. package/server/client/dist/assets/ViewNotes-CiZtGJNE.js +1 -0
  22. package/server/client/dist/assets/{ViewSectionDialog-CzskgmMF.js → ViewSectionDialog-Ctl_694Y.js} +17 -17
  23. package/server/client/dist/assets/Views-D-zlatYD.js +1 -0
  24. package/server/client/dist/assets/app-DmqIOMWF.css +1 -0
  25. package/server/client/dist/assets/app-cTRcKMGc.js +26 -0
  26. package/server/client/dist/assets/floating-ui.react-dom-wWLZ1CdM.js +1 -0
  27. package/server/client/dist/assets/{image.api-8dlMwzMd.js → image.api-CVuuvTBk.js} +1 -1
  28. package/server/client/dist/assets/index-DKJChPr4.js +1 -0
  29. package/server/client/dist/assets/manage-image-BZjaxp-f.js +1 -0
  30. package/server/client/dist/assets/manage-image-detail-DPv0FQ2m.js +1 -0
  31. package/server/client/dist/assets/{mcp-EDurbggR.js → mcp-CT_9IV2c.js} +2 -2
  32. package/server/client/dist/assets/mcp-admin-adapter-xAuyZc_M.js +1 -0
  33. package/server/client/dist/assets/note-core-Buwzyxi7.js +13 -0
  34. package/server/client/dist/assets/note-runtime-CPDVOFEo.js +168 -0
  35. package/server/client/dist/assets/note-ui-CHI-tzj-.js +2 -0
  36. package/server/client/dist/assets/{index-BEUqDuAZ.css → note-ui-DL8owV-U.css} +1 -1
  37. package/server/client/dist/assets/note.api-D_00ZzJC.js +356 -0
  38. package/server/client/dist/assets/{placeholder-DeKgSBnC.js → placeholder-DbdGiYGL.js} +4 -4
  39. package/server/client/dist/assets/preload-helper-BXl3LOEh.js +1 -0
  40. package/server/client/dist/assets/properties-D447wsWk.js +1 -0
  41. package/server/client/dist/assets/route-preload.js +2 -0
  42. package/server/client/dist/assets/{tag.api-Dl8DQd0m.js → tag.api-CX0SE06J.js} +1 -1
  43. package/server/client/dist/assets/time-B3pt8vt4.js +9 -0
  44. package/server/client/dist/assets/trash-8sH7EZgc.js +1 -0
  45. package/server/client/dist/assets/url-BSnWpmJx.js +1 -0
  46. package/server/client/dist/assets/{useReminderMutate-CoXXqJY2.js → useReminderMutate-CLK1tHvT.js} +4 -4
  47. package/server/client/dist/index.html +10 -5
  48. package/server/dist/app.js +1 -2
  49. package/server/dist/features/auth/http/api.js +0 -1
  50. package/server/dist/features/auth/http/login-page.js +272 -5
  51. package/server/dist/features/auth/http/pages.js +20 -3
  52. package/server/dist/features/auth/service.js +3 -2
  53. package/server/dist/features/cache/graphql/cache.field.resolver.js +0 -1
  54. package/server/dist/features/cache/graphql/cache.mutation.resolver.js +0 -1
  55. package/server/dist/features/cache/graphql/cache.query.resolver.js +0 -1
  56. package/server/dist/features/cache/graphql/cache.resolver.js +0 -1
  57. package/server/dist/features/cache/graphql/cache.type-defs.js +0 -1
  58. package/server/dist/features/cache/graphql/index.js +0 -1
  59. package/server/dist/features/image/graphql/image.field.resolver.js +0 -1
  60. package/server/dist/features/image/graphql/image.mutation.resolver.js +0 -1
  61. package/server/dist/features/image/graphql/image.query.resolver.js +0 -1
  62. package/server/dist/features/image/graphql/image.resolver.js +0 -1
  63. package/server/dist/features/image/graphql/image.type-defs.js +0 -1
  64. package/server/dist/features/image/graphql/index.js +0 -1
  65. package/server/dist/features/image/http/upload.js +0 -1
  66. package/server/dist/features/image/services/delete.js +0 -1
  67. package/server/dist/features/image/services/upload.js +0 -1
  68. package/server/dist/features/image/services/validation.js +0 -1
  69. package/server/dist/features/mcp-admin/http/handlers.js +0 -1
  70. package/server/dist/features/mcp-admin/service.js +0 -1
  71. package/server/dist/features/note/graphql/index.js +0 -1
  72. package/server/dist/features/note/graphql/note.field.resolver.js +0 -1
  73. package/server/dist/features/note/graphql/note.mutation.resolver.js +0 -1
  74. package/server/dist/features/note/graphql/note.query.resolver.js +0 -1
  75. package/server/dist/features/note/graphql/note.resolver.js +0 -1
  76. package/server/dist/features/note/graphql/note.type-defs.js +0 -1
  77. package/server/dist/features/note/http/mcp.js +0 -1
  78. package/server/dist/features/note/services/authoring.js +0 -1
  79. package/server/dist/features/note/services/cleanup.js +0 -1
  80. package/server/dist/features/note/services/content-blocks.js +0 -1
  81. package/server/dist/features/note/services/markdown-intent-write.js +0 -1
  82. package/server/dist/features/note/services/markdown-patch.js +0 -1
  83. package/server/dist/features/note/services/properties.js +0 -1
  84. package/server/dist/features/note/services/search-backfill.js +0 -1
  85. package/server/dist/features/note/services/search.js +0 -1
  86. package/server/dist/features/note/services/snapshot.js +0 -1
  87. package/server/dist/features/note/services/tag-filter.js +0 -1
  88. package/server/dist/features/note/services/trash.js +0 -1
  89. package/server/dist/features/note/services/write-conflict.js +0 -1
  90. package/server/dist/features/note/services/write.js +0 -1
  91. package/server/dist/features/placeholder/graphql/index.js +0 -1
  92. package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js +0 -1
  93. package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js +0 -1
  94. package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js +0 -1
  95. package/server/dist/features/placeholder/graphql/placeholder.resolver.js +0 -1
  96. package/server/dist/features/placeholder/graphql/placeholder.type-defs.js +0 -1
  97. package/server/dist/features/reminder/graphql/index.js +0 -1
  98. package/server/dist/features/reminder/graphql/reminder.field.resolver.js +0 -1
  99. package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js +0 -1
  100. package/server/dist/features/reminder/graphql/reminder.query.resolver.js +0 -1
  101. package/server/dist/features/reminder/graphql/reminder.resolver.js +0 -1
  102. package/server/dist/features/reminder/graphql/reminder.type-defs.js +0 -1
  103. package/server/dist/features/tag/graphql/index.js +0 -1
  104. package/server/dist/features/tag/graphql/tag.field.resolver.js +0 -1
  105. package/server/dist/features/tag/graphql/tag.mutation.resolver.js +0 -1
  106. package/server/dist/features/tag/graphql/tag.query.resolver.js +0 -1
  107. package/server/dist/features/tag/graphql/tag.resolver.js +0 -1
  108. package/server/dist/features/tag/graphql/tag.type-defs.js +0 -1
  109. package/server/dist/features/tag/http/mcp.js +0 -1
  110. package/server/dist/features/tag/services/organization.js +0 -1
  111. package/server/dist/features/view/graphql/index.js +0 -1
  112. package/server/dist/features/view/graphql/view.mutation.resolver.js +0 -1
  113. package/server/dist/features/view/graphql/view.query.resolver.js +0 -1
  114. package/server/dist/features/view/graphql/view.resolver.js +0 -1
  115. package/server/dist/features/view/graphql/view.type-defs.js +0 -1
  116. package/server/dist/features/view/services/workspace.js +0 -1
  117. package/server/dist/main.js +0 -1
  118. package/server/dist/models.js +0 -1
  119. package/server/dist/modules/app-version.js +0 -1
  120. package/server/dist/modules/auth-guard.js +0 -1
  121. package/server/dist/modules/auth-mode.js +0 -1
  122. package/server/dist/modules/auth-redirect.js +0 -1
  123. package/server/dist/modules/blocknote-markdown-editor.js +53 -0
  124. package/server/dist/modules/blocknote-tree.js +0 -1
  125. package/server/dist/modules/blocknote.js +272 -79
  126. package/server/dist/modules/data-maintenance.js +0 -1
  127. package/server/dist/modules/error-handler.js +8 -1
  128. package/server/dist/modules/graphql.js +0 -1
  129. package/server/dist/modules/logger.js +0 -1
  130. package/server/dist/modules/mcp-auth.js +0 -1
  131. package/server/dist/modules/mcp-token.js +0 -1
  132. package/server/dist/modules/prisma-runtime.js +0 -1
  133. package/server/dist/modules/rate-limit.js +0 -1
  134. package/server/dist/modules/recovery-retention.js +0 -1
  135. package/server/dist/modules/server-events-handler.js +0 -1
  136. package/server/dist/modules/server-events.js +0 -1
  137. package/server/dist/modules/session-store.js +0 -1
  138. package/server/dist/modules/use-async.js +0 -1
  139. package/server/dist/paths.js +0 -1
  140. package/server/dist/routes/api.js +0 -1
  141. package/server/dist/routes/auth-pages.js +0 -1
  142. package/server/dist/routes/client.js +0 -1
  143. package/server/dist/routes/graphql.js +0 -1
  144. package/server/dist/routes/index.js +0 -1
  145. package/server/dist/routes/mcp.js +0 -1
  146. package/server/dist/schema/index.js +0 -1
  147. package/server/dist/start.js +0 -1
  148. package/server/dist/types/index.js +0 -1
  149. package/server/dist/types/input.js +0 -1
  150. package/server/client/dist/assets/ArrowRight.es-C4zlc54Z.js +0 -1
  151. package/server/client/dist/assets/Calendar-COrN6Hjv.js +0 -25
  152. package/server/client/dist/assets/Callout-BK0K9E74.js +0 -1
  153. package/server/client/dist/assets/Copy.es-zm8f_XXX.js +0 -1
  154. package/server/client/dist/assets/Graph-BH1fzhue.js +0 -1
  155. package/server/client/dist/assets/Image-KXuHOG4b.js +0 -1
  156. package/server/client/dist/assets/Image.es-BKahRbIm.js +0 -1
  157. package/server/client/dist/assets/Note-C143cKKw.js +0 -12
  158. package/server/client/dist/assets/Plus.es-BqeC8T8h.js +0 -1
  159. package/server/client/dist/assets/Reminders-BYG5rz1M.js +0 -1
  160. package/server/client/dist/assets/Search-DBWsIMDP.js +0 -1
  161. package/server/client/dist/assets/SurfaceCard-CNSQfYfM.js +0 -1
  162. package/server/client/dist/assets/Tag-5aCfSO8Y.js +0 -1
  163. package/server/client/dist/assets/TagNotes-BPZjN-6l.js +0 -1
  164. package/server/client/dist/assets/Trash.es-CRQkTnXZ.js +0 -1
  165. package/server/client/dist/assets/ViewNotes-DGLNF96x.js +0 -1
  166. package/server/client/dist/assets/Views-DR5jT23G.js +0 -1
  167. package/server/client/dist/assets/graph-vendor-UTvF2NS_.js +0 -46
  168. package/server/client/dist/assets/index-DGIAh3ZY.js +0 -1
  169. package/server/client/dist/assets/index-VHp17GQX.js +0 -342
  170. package/server/client/dist/assets/manage-image-RqtWKas2.js +0 -1
  171. package/server/client/dist/assets/manage-image-detail-BTSIbLlQ.js +0 -1
  172. package/server/client/dist/assets/mcp-admin-adapter-DmGUo5G8.js +0 -1
  173. package/server/client/dist/assets/note-core-BklwyZHk.js +0 -180
  174. package/server/client/dist/assets/note-vendor-DkYKUmLo.js +0 -54
  175. package/server/client/dist/assets/properties-BjrZvOYn.js +0 -1
  176. package/server/client/dist/assets/trash-C5ntddLu.js +0 -1
  177. package/server/client/dist/fonts/Pretendard-Bold.woff +0 -0
  178. package/server/client/dist/fonts/Pretendard-Regular.woff +0 -0
  179. package/server/dist/app.js.map +0 -1
  180. package/server/dist/features/auth/http/api.js.map +0 -1
  181. package/server/dist/features/auth/http/login-page.js.map +0 -1
  182. package/server/dist/features/auth/http/pages.js.map +0 -1
  183. package/server/dist/features/auth/service.js.map +0 -1
  184. package/server/dist/features/cache/graphql/cache.field.resolver.js.map +0 -1
  185. package/server/dist/features/cache/graphql/cache.mutation.resolver.js.map +0 -1
  186. package/server/dist/features/cache/graphql/cache.query.resolver.js.map +0 -1
  187. package/server/dist/features/cache/graphql/cache.resolver.js.map +0 -1
  188. package/server/dist/features/cache/graphql/cache.type-defs.js.map +0 -1
  189. package/server/dist/features/cache/graphql/index.js.map +0 -1
  190. package/server/dist/features/image/graphql/image.field.resolver.js.map +0 -1
  191. package/server/dist/features/image/graphql/image.mutation.resolver.js.map +0 -1
  192. package/server/dist/features/image/graphql/image.query.resolver.js.map +0 -1
  193. package/server/dist/features/image/graphql/image.resolver.js.map +0 -1
  194. package/server/dist/features/image/graphql/image.type-defs.js.map +0 -1
  195. package/server/dist/features/image/graphql/index.js.map +0 -1
  196. package/server/dist/features/image/http/upload.js.map +0 -1
  197. package/server/dist/features/image/services/delete.js.map +0 -1
  198. package/server/dist/features/image/services/upload.js.map +0 -1
  199. package/server/dist/features/image/services/validation.js.map +0 -1
  200. package/server/dist/features/mcp-admin/http/handlers.js.map +0 -1
  201. package/server/dist/features/mcp-admin/service.js.map +0 -1
  202. package/server/dist/features/note/graphql/index.js.map +0 -1
  203. package/server/dist/features/note/graphql/note.field.resolver.js.map +0 -1
  204. package/server/dist/features/note/graphql/note.mutation.resolver.js.map +0 -1
  205. package/server/dist/features/note/graphql/note.query.resolver.js.map +0 -1
  206. package/server/dist/features/note/graphql/note.resolver.js.map +0 -1
  207. package/server/dist/features/note/graphql/note.type-defs.js.map +0 -1
  208. package/server/dist/features/note/http/mcp.js.map +0 -1
  209. package/server/dist/features/note/services/authoring.js.map +0 -1
  210. package/server/dist/features/note/services/cleanup.js.map +0 -1
  211. package/server/dist/features/note/services/content-blocks.js.map +0 -1
  212. package/server/dist/features/note/services/markdown-intent-write.js.map +0 -1
  213. package/server/dist/features/note/services/markdown-patch.js.map +0 -1
  214. package/server/dist/features/note/services/properties.js.map +0 -1
  215. package/server/dist/features/note/services/search-backfill.js.map +0 -1
  216. package/server/dist/features/note/services/search.js.map +0 -1
  217. package/server/dist/features/note/services/snapshot.js.map +0 -1
  218. package/server/dist/features/note/services/tag-filter.js.map +0 -1
  219. package/server/dist/features/note/services/trash.js.map +0 -1
  220. package/server/dist/features/note/services/write-conflict.js.map +0 -1
  221. package/server/dist/features/note/services/write.js.map +0 -1
  222. package/server/dist/features/placeholder/graphql/index.js.map +0 -1
  223. package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js.map +0 -1
  224. package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js.map +0 -1
  225. package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js.map +0 -1
  226. package/server/dist/features/placeholder/graphql/placeholder.resolver.js.map +0 -1
  227. package/server/dist/features/placeholder/graphql/placeholder.type-defs.js.map +0 -1
  228. package/server/dist/features/reminder/graphql/index.js.map +0 -1
  229. package/server/dist/features/reminder/graphql/reminder.field.resolver.js.map +0 -1
  230. package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js.map +0 -1
  231. package/server/dist/features/reminder/graphql/reminder.query.resolver.js.map +0 -1
  232. package/server/dist/features/reminder/graphql/reminder.resolver.js.map +0 -1
  233. package/server/dist/features/reminder/graphql/reminder.type-defs.js.map +0 -1
  234. package/server/dist/features/tag/graphql/index.js.map +0 -1
  235. package/server/dist/features/tag/graphql/tag.field.resolver.js.map +0 -1
  236. package/server/dist/features/tag/graphql/tag.mutation.resolver.js.map +0 -1
  237. package/server/dist/features/tag/graphql/tag.query.resolver.js.map +0 -1
  238. package/server/dist/features/tag/graphql/tag.resolver.js.map +0 -1
  239. package/server/dist/features/tag/graphql/tag.type-defs.js.map +0 -1
  240. package/server/dist/features/tag/http/mcp.js.map +0 -1
  241. package/server/dist/features/tag/services/organization.js.map +0 -1
  242. package/server/dist/features/view/graphql/index.js.map +0 -1
  243. package/server/dist/features/view/graphql/view.mutation.resolver.js.map +0 -1
  244. package/server/dist/features/view/graphql/view.query.resolver.js.map +0 -1
  245. package/server/dist/features/view/graphql/view.resolver.js.map +0 -1
  246. package/server/dist/features/view/graphql/view.type-defs.js.map +0 -1
  247. package/server/dist/features/view/services/workspace.js.map +0 -1
  248. package/server/dist/main.js.map +0 -1
  249. package/server/dist/models.js.map +0 -1
  250. package/server/dist/modules/app-version.js.map +0 -1
  251. package/server/dist/modules/auth-guard.js.map +0 -1
  252. package/server/dist/modules/auth-mode.js.map +0 -1
  253. package/server/dist/modules/auth-redirect.js.map +0 -1
  254. package/server/dist/modules/blocknote-tree.js.map +0 -1
  255. package/server/dist/modules/blocknote.js.map +0 -1
  256. package/server/dist/modules/data-maintenance.js.map +0 -1
  257. package/server/dist/modules/error-handler.js.map +0 -1
  258. package/server/dist/modules/graphql.js.map +0 -1
  259. package/server/dist/modules/logger.js.map +0 -1
  260. package/server/dist/modules/mcp-auth.js.map +0 -1
  261. package/server/dist/modules/mcp-token.js.map +0 -1
  262. package/server/dist/modules/prisma-runtime.js.map +0 -1
  263. package/server/dist/modules/rate-limit.js.map +0 -1
  264. package/server/dist/modules/recovery-retention.js.map +0 -1
  265. package/server/dist/modules/server-events-handler.js.map +0 -1
  266. package/server/dist/modules/server-events.js.map +0 -1
  267. package/server/dist/modules/session-store.js.map +0 -1
  268. package/server/dist/modules/use-async.js.map +0 -1
  269. package/server/dist/paths.js.map +0 -1
  270. package/server/dist/routes/api.js.map +0 -1
  271. package/server/dist/routes/auth-pages.js.map +0 -1
  272. package/server/dist/routes/client.js.map +0 -1
  273. package/server/dist/routes/graphql.js.map +0 -1
  274. package/server/dist/routes/index.js.map +0 -1
  275. package/server/dist/routes/mcp.js.map +0 -1
  276. package/server/dist/schema/index.js.map +0 -1
  277. package/server/dist/start.js.map +0 -1
  278. package/server/dist/types/index.js.map +0 -1
  279. package/server/dist/types/input.js.map +0 -1
@@ -1 +0,0 @@
1
- import{j as e}from"./note-vendor-DkYKUmLo.js";import{k as oe,u as de,O as ce,q as x,ao as M,ba as ue,bb as pe,bc as me,P as he,T as s,B as o,M as d,I as v,S as xe,e as be,F as q,m as ve,j as ye,aR as ge}from"./index-VHp17GQX.js";import{a as m}from"./graph-vendor-UTvF2NS_.js";import"./note-core-BklwyZHk.js";const fe=ye(ge),W=[{value:"text",label:"Text",description:"Short status or label values."},{value:"url",label:"URL",description:"External sources, issues, docs, or references."},{value:"number",label:"Number",description:"Priority, score, or count values."},{value:"date",label:"Date",description:"Due dates and milestones."},{value:"boolean",label:"Boolean",description:"True or false flags."},{value:"select",label:"Select",description:"Choose one option from a controlled list."}],je=l=>!l.id&&!l.label.trim()&&!l.value.trim(),Ne=l=>(l.value.trim()||l.label.trim()).toLowerCase().replace(/\s+/g,"-"),Ce=l=>/^[a-z0-9][a-z0-9_-]*$/.test(l),X=l=>l.filter(r=>r.id||r.label.trim()).map((r,F)=>({...r.id?{id:r.id}:{},label:r.label.trim(),value:r.value.trim()||r.label.trim(),color:r.color??null,order:r.order??F}));function Ee(){const l=ve(),r=oe(),F=fe.useSearch(),Z=Number(F.page??1),R=50,V=Math.max(0,Z-1)*R,[O,Q]=m.useState(""),[I,A]=m.useState(""),[y,$]=m.useState("text"),[D,j]=m.useState([{label:"",value:""}]),[u,N]=m.useState(null),[i,H]=m.useState(null),[k,L]=m.useState(""),[E,g]=m.useState([{label:"",value:""}]),[ee,C]=m.useState(!1),S=()=>{Q(""),A(""),$("text"),j([{label:"",value:""}])},ae=a=>{H(a),L(a.name),g(a.options.length>0?a.options.map(t=>({id:t.id,label:t.label,value:t.value,color:t.color,order:t.order})):[{label:"",value:""}])},T=()=>{H(null),L(""),g([{label:"",value:""}])},_=de({queryKey:x.notes.propertyKeys({limit:R,offset:V}),queryFn:async()=>{const a=await ce({limit:R,offset:V});if(a.type==="error")throw a;return a.notePropertyKeys}}),w=M({mutationFn:async()=>ue({key:O,name:I||O,valueType:y,...y==="select"?{options:D.map((a,t)=>({label:a.label.trim(),value:a.value.trim()||a.label.trim(),order:t})).filter(a=>a.label)}:{}}),onSuccess:a=>{if(a.type==="error"){l(a.errors[0]?.message??"Failed to create property.");return}S(),C(!1),r.invalidateQueries({queryKey:x.notes.propertyKeysAll(),exact:!1}),l("Property created.")},onError:()=>{l("Failed to create property.")}}),h=M({mutationFn:async()=>{if(!i)throw new Error("Property to edit is missing.");return pe({key:i.key,name:k||i.key,...i.valueType==="select"?{options:X(E)}:{}})},onSuccess:a=>{if(a.type==="error"){l(a.errors[0]?.message??"Failed to update property.");return}T(),r.invalidateQueries({queryKey:x.notes.propertyKeysAll(),exact:!1}),r.invalidateQueries({queryKey:x.notes.all(),exact:!1}),r.invalidateQueries({queryKey:x.views.all(),exact:!1}),l("Property updated.")},onError:()=>{l("Failed to update property.")}}),f=M({mutationFn:async({propertyKey:a,confirmImpact:t})=>me({key:a.key,confirmImpact:t}),onSuccess:a=>{if(a.type==="error"){l(a.errors[0]?.message??"Failed to delete property.");return}r.invalidateQueries({queryKey:x.notes.propertyKeysAll(),exact:!1}),r.invalidateQueries({queryKey:x.notes.all(),exact:!1}),r.invalidateQueries({queryKey:x.views.all(),exact:!1}),N(null),l(a.deleteNotePropertyKey.affectedNoteCount>0?`Property deleted from ${a.deleteNotePropertyKey.affectedNoteCount} note(s).`:"Property deleted.")},onError:()=>{l("Failed to delete property.")}}),P=_.data?.keys??[],se=y!=="select"||D.some(a=>a.label.trim()),z=i?.valueType==="select"?E.filter(a=>!je(a)):[],B=z.map(Ne),K=B.find((a,t)=>B.indexOf(a)!==t),U=B.find(a=>!Ce(a)),J=z.some(a=>!!a.id&&!a.label.trim()),Y=z.some(a=>!a.id&&!a.label.trim()),te=!i||i.valueType!=="select"||z.length>0&&!J&&!Y&&!K&&!U,le=!!i&&k.trim()!==i?.name,re=i?.valueType==="select"&&JSON.stringify(X(E))!==JSON.stringify(i.options.map(a=>({id:a.id,label:a.label,value:a.value,color:a.color??null,order:a.order}))),ie=le||!!re,G=J?"Existing option labels cannot be empty.":Y?"New options need a label.":K?`Option value “${K}” is duplicated.`:U?"Option values must use letters, numbers, dashes, or underscores.":"",ne=()=>{u&&f.mutate({propertyKey:u,confirmImpact:u.noteCount>0})};return e.jsxs(he,{title:"Properties",description:"Define shared note fields before using them in notes and future views.",headerRight:e.jsx(o,{type:"button",size:"md",variant:"primary",className:"w-full sm:w-auto",onClick:()=>C(!0),children:"New property"}),children:[e.jsxs("section",{className:"flex flex-col gap-3","aria-labelledby":"shared-properties-heading",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx(s,{id:"shared-properties-heading",as:"h2",variant:"label",weight:"medium",className:"text-fg-tertiary",children:"Shared fields"}),!_.isLoading&&P.length>0&&e.jsxs(s,{as:"span",variant:"meta",tone:"tertiary",children:[P.length," field",P.length===1?"":"s"]})]}),_.isLoading?e.jsx(s,{as:"p",variant:"body",tone:"tertiary",className:"surface-base px-4 py-3.5",children:"Loading properties..."}):P.length===0?e.jsxs("div",{className:"surface-base border-dashed p-6 text-center",children:[e.jsx(s,{as:"p",variant:"body",weight:"medium",children:"No shared properties yet."}),e.jsx(s,{as:"p",variant:"label",tone:"tertiary",children:"Create one here, then attach it from the note screen."})]}):e.jsx("div",{className:"flex flex-col gap-3",children:P.map(a=>e.jsxs("article",{className:"surface-base grid gap-3 px-4 py-3.5 md:grid-cols-[minmax(0,1fr)_120px_120px_auto] md:items-center",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx(s,{as:"h3",variant:"body",weight:"semibold",className:"truncate",children:a.name}),e.jsx(s,{as:"p",variant:"micro",tone:"tertiary",className:"font-mono",children:a.key}),a.options.length>0&&e.jsxs("div",{className:"mt-2 flex flex-wrap gap-1.5",children:[a.options.slice(0,6).map(t=>e.jsx("span",{className:"rounded-full border border-border-subtle bg-subtle px-2 py-0.5 text-xs font-medium text-fg-secondary",children:t.label},t.id)),a.options.length>6&&e.jsxs("span",{className:"rounded-full border border-border-subtle bg-subtle px-2 py-0.5 text-xs font-medium text-fg-tertiary",children:["+",a.options.length-6]})]})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx(s,{as:"span",variant:"micro",tone:"tertiary",className:"block md:hidden",children:"Type"}),e.jsx(s,{as:"span",variant:"label",tone:"secondary",className:"capitalize",children:a.valueType})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx(s,{as:"span",variant:"micro",tone:"tertiary",className:"block md:hidden",children:"Used in"}),e.jsxs(s,{as:"span",variant:"label",tone:"tertiary",children:[a.noteCount," note",a.noteCount===1?"":"s"]})]}),e.jsxs("div",{className:"flex flex-wrap gap-2 justify-self-start md:justify-self-end",children:[e.jsx(o,{type:"button",size:"sm",variant:"subtle",disabled:h.isPending||f.isPending,onClick:()=>ae(a),children:"Edit"}),e.jsx(o,{type:"button",size:"sm",variant:"soft-danger",disabled:f.isPending||h.isPending,onClick:()=>N(a),children:"Delete"})]})]},a.key))})]}),e.jsxs(d,{isOpen:ee,onClose:()=>{w.isPending||(C(!1),S())},variant:"form",children:[e.jsx(d.Header,{title:"New property",onClose:()=>{C(!1),S()}}),e.jsx(d.Body,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(s,{as:"p",variant:"label",tone:"tertiary",children:"Create a shared field that notes can use for values and future views."}),e.jsxs("label",{className:"flex flex-col gap-1.5",children:[e.jsx(s,{as:"span",variant:"label",weight:"medium",children:"Key"}),e.jsx(v,{size:"md",placeholder:"status",value:O,onChange:a=>Q(a.target.value)})]}),e.jsxs("label",{className:"flex flex-col gap-1.5",children:[e.jsx(s,{as:"span",variant:"label",weight:"medium",children:"Name"}),e.jsx(v,{size:"md",placeholder:"Status",value:I,onChange:a=>A(a.target.value)})]}),e.jsxs("label",{className:"flex flex-col gap-1.5",children:[e.jsx(s,{id:"property-type-label",as:"span",variant:"label",weight:"medium",children:"Type"}),e.jsx(xe,{value:y,"aria-labelledby":"property-type-label",onValueChange:a=>$(a),children:W.map(a=>e.jsx(be,{value:a.value,children:a.label},a.value))})]}),y==="select"&&e.jsxs("div",{className:"rounded-[14px] border border-border-subtle bg-subtle/50 p-3",children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-center justify-between gap-2",children:[e.jsx(s,{as:"span",variant:"label",weight:"semibold",children:"Options"}),e.jsx(o,{type:"button",size:"sm",variant:"subtle",onClick:()=>j(a=>[...a,{label:"",value:""}]),children:"Add option"})]}),e.jsx("div",{className:"flex flex-col gap-2",children:D.map((a,t)=>e.jsxs("div",{className:"grid gap-2 sm:grid-cols-[1fr_1fr_auto]",children:[e.jsx(v,{size:"sm",placeholder:"Label",value:a.label,onChange:c=>j(p=>p.map((n,b)=>b===t?{...n,label:c.target.value}:n))}),e.jsx(v,{size:"sm",placeholder:"value",value:a.value,onChange:c=>j(p=>p.map((n,b)=>b===t?{...n,value:c.target.value}:n))}),e.jsx(o,{type:"button",size:"sm",variant:"soft-danger",onClick:()=>j(c=>c.filter((p,n)=>n!==t)),children:"Remove"})]},t))})]}),e.jsx(s,{as:"p",variant:"micro",tone:"tertiary",children:W.find(a=>a.value===y)?.description})]})}),e.jsx(d.Footer,{children:e.jsxs(q,{children:[e.jsx(o,{type:"button",variant:"ghost",size:"sm",disabled:w.isPending,onClick:()=>{C(!1),S()},children:"Cancel"}),e.jsx(o,{type:"button",variant:"primary",size:"sm",disabled:!O.trim()||!se||w.isPending,isLoading:w.isPending,onClick:()=>w.mutate(),children:"Create property"})]})})]}),e.jsxs(d,{isOpen:!!i,onClose:()=>{h.isPending||T()},variant:"form",children:[e.jsx(d.Header,{title:"Edit property",onClose:()=>{h.isPending||T()}}),e.jsx(d.Body,{children:i&&e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(s,{as:"p",variant:"label",tone:"tertiary",children:"Rename the display label or add select options. Key, type, and existing option values stay fixed so notes and views remain stable."}),e.jsxs("div",{className:"grid gap-2 sm:grid-cols-2",children:[e.jsxs("div",{className:"rounded-[14px] border border-border-subtle bg-subtle/50 px-3 py-2",children:[e.jsx(s,{as:"span",variant:"micro",tone:"tertiary",className:"block",children:"Key"}),e.jsx(s,{as:"span",variant:"label",weight:"medium",className:"font-mono",children:i.key})]}),e.jsxs("div",{className:"rounded-[14px] border border-border-subtle bg-subtle/50 px-3 py-2",children:[e.jsx(s,{as:"span",variant:"micro",tone:"tertiary",className:"block",children:"Type"}),e.jsx(s,{as:"span",variant:"label",weight:"medium",className:"capitalize",children:i.valueType})]})]}),e.jsxs("label",{className:"flex flex-col gap-1.5",children:[e.jsx(s,{as:"span",variant:"label",weight:"medium",children:"Display name"}),e.jsx(v,{size:"md",placeholder:"Workflow state",value:k,onChange:a=>L(a.target.value)})]}),i.valueType==="select"&&e.jsxs("div",{className:"rounded-[14px] border border-border-subtle bg-subtle/50 p-3",children:[e.jsxs("div",{className:"mb-2 flex flex-wrap items-center justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx(s,{as:"span",variant:"label",weight:"semibold",children:"Options"}),e.jsx(s,{as:"p",variant:"micro",tone:"tertiary",className:"mt-0.5",children:"Existing option values are locked. Removing existing options needs an impact-aware delete flow."})]}),e.jsx(o,{type:"button",size:"sm",variant:"subtle",onClick:()=>g(a=>[...a,{label:"",value:""}]),children:"Add option"})]}),e.jsx("div",{className:"flex flex-col gap-2",children:E.map((a,t)=>e.jsxs("div",{className:"grid gap-2 sm:grid-cols-[1fr_1fr_auto]",children:[e.jsx(v,{size:"sm",placeholder:"Label",value:a.label,onChange:c=>g(p=>p.map((n,b)=>b===t?{...n,label:c.target.value}:n))}),e.jsx(v,{size:"sm",placeholder:a.id?"Locked value":"value",value:a.value,disabled:!!a.id,onChange:c=>g(p=>p.map((n,b)=>b===t?{...n,value:c.target.value}:n))}),a.id?e.jsx(s,{as:"span",variant:"meta",tone:"tertiary",className:"flex h-8 items-center px-3",children:"Locked"}):e.jsx(o,{type:"button",size:"sm",variant:"soft-danger",onClick:()=>g(c=>c.filter((p,n)=>n!==t)),children:"Remove"})]},a.id??`new-${t}`))}),G&&e.jsx(s,{as:"p",variant:"micro",tone:"error",className:"mt-2",children:G})]})]})}),e.jsx(d.Footer,{children:e.jsxs(q,{children:[e.jsx(o,{type:"button",variant:"ghost",size:"sm",disabled:h.isPending,onClick:T,children:"Cancel"}),e.jsx(o,{type:"button",variant:"primary",size:"sm",disabled:!k.trim()||!ie||!te||h.isPending,isLoading:h.isPending,onClick:()=>h.mutate(),children:"Save changes"})]})})]}),e.jsxs(d,{isOpen:!!u,onClose:()=>{f.isPending||N(null)},variant:"confirm",children:[e.jsx(d.Header,{title:"Delete property",onClose:()=>N(null)}),e.jsx(d.Body,{children:u&&e.jsxs("div",{className:"space-y-3",children:[e.jsxs(s,{as:"p",variant:"body",weight:"medium",children:["Delete “",u.name,"”?"]}),u.noteCount>0?e.jsxs("div",{className:"rounded-[14px] border border-border-subtle bg-subtle p-3",children:[e.jsxs(s,{as:"p",variant:"label",tone:"secondary",children:["This property is used by ",u.noteCount," note",u.noteCount===1?"":"s","."]}),e.jsx(s,{as:"p",variant:"micro",tone:"tertiary",className:"mt-1 leading-5",children:"Deleting it removes this value from those notes. Future views using this property will need configuration."})]}):e.jsx(s,{as:"p",variant:"label",tone:"tertiary",children:"This property is not used by any note yet."})]})}),e.jsx(d.Footer,{children:e.jsxs(q,{children:[e.jsx(o,{type:"button",variant:"ghost",size:"sm",disabled:f.isPending,onClick:()=>N(null),children:"Cancel"}),e.jsx(o,{type:"button",variant:"danger",size:"sm",isLoading:f.isPending,onClick:ne,children:"Delete"})]})})]})]})}export{Ee as default};
@@ -1 +0,0 @@
1
- import{j as e}from"./note-vendor-DkYKUmLo.js";import{Y as Q,j as F,aF as M,k as L,G as _,a5 as I,b1 as K,q as i,ao as E,b2 as B,N as O,b3 as G,P as w,Q as Y,T as d,aG as H,B as b,Z as W,b4 as $,i as l,m as z,d as U,ak as V,u as Z,b5 as J,M as y}from"./index-VHp17GQX.js";import{a as k}from"./graph-vendor-UTvF2NS_.js";import{S as C}from"./SurfaceCard-CNSQfYfM.js";import"./note-core-BklwyZHk.js";const m=25,v=F(M),S=30,R=`Deleted notes stay here for ${S} days before permanent removal`,g={title:"Permanently delete note?",description:"This note is referenced by the notes below. If you permanently delete it, those links will stay broken. Other notes will not be edited automatically.",confirmLabel:"Delete now",confirmVariant:"danger"},P=s=>U(s).format("YYYY-MM-DD HH:mm"),X=({note:s,onClose:o})=>{const a=s?.id??"",c=s?.title||"Untitled note",r=Z({queryKey:i.notes.trashDetail(a),enabled:!!s,queryFn:async()=>{const h=await J(a);if(h.type==="error")throw h;if(!h.trashedNote)throw new Error("Trashed note not found");return h.trashedNote}}),f=r.data?.contentAsMarkdown.trim()??"";return e.jsxs(y,{isOpen:!!s,onClose:o,variant:"inspect",children:[e.jsx(y.Header,{title:c,onClose:o}),e.jsx(y.Body,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[s&&e.jsxs(d,{as:"p",variant:"meta",tone:"secondary",children:["Deleted ",P(s.deletedAt)]}),r.isLoading&&e.jsx(d,{as:"p",variant:"meta",tone:"secondary",children:"Loading deleted note content..."}),r.isError&&e.jsx(d,{as:"p",variant:"meta",tone:"error",children:"Failed to load deleted note content."}),!r.isLoading&&!r.isError&&f&&e.jsx("pre",{className:"max-h-[60vh] overflow-auto whitespace-pre-wrap break-words rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3 text-sm leading-6 text-fg-secondary",children:f}),!r.isLoading&&!r.isError&&!f&&e.jsx(d,{as:"p",variant:"meta",tone:"secondary",className:"rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3",children:"No readable content was found for this deleted note."})]})}),e.jsx(y.Footer,{children:e.jsx(b,{variant:"ghost",size:"sm",onClick:o,children:"Close"})})]})},ee=()=>e.jsx(w,{title:"Trash",variant:"default",heading:e.jsx(l,{width:118,height:24,className:"rounded-full"}),description:e.jsx(l,{width:352,height:16,className:"rounded-full"}),children:e.jsx("div",{className:"grid gap-3",children:Array.from({length:3},(s,o)=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1 space-y-2",children:[e.jsx(l,{width:"40%",height:18,className:"rounded-full"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(l,{width:168,height:14,className:"rounded-full"}),e.jsx(l,{width:176,height:14,className:"rounded-full"})]}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(l,{width:56,height:24,className:"rounded-full"}),e.jsx(l,{width:72,height:24,className:"rounded-full"})]})]}),e.jsx(l,{width:92,height:32,className:"rounded-[12px]"})]})},o))})}),te=()=>{const{page:s}=v.useSearch(),o=v.useNavigate(),a=L(),c=z(),r=_(),[f,h]=k.useState(null),[x,j]=k.useState(null),{data:u}=I({queryKey:i.notes.trash({limit:m,offset:(s-1)*m}),queryFn:async()=>{const t=await K({limit:m,offset:(s-1)*m});if(t.type==="error")throw t;return t.trashedNotes}}),N=E({mutationFn:B,onSuccess:async t=>{if(t.type==="error")throw t;await Promise.all([a.invalidateQueries({queryKey:i.notes.all(),exact:!1}),a.invalidateQueries({queryKey:i.tags.all(),exact:!1}),a.invalidateQueries({queryKey:i.reminders.all(),exact:!1}),a.invalidateQueries({queryKey:i.images.all(),exact:!1}),a.invalidateQueries({queryKey:i.calendar.all(),exact:!1})]),c("The note has been restored."),o({to:O,params:{id:t.restoreTrashedNote.id}})},onError:()=>{c("Failed to restore the note.")}}),p=E({mutationFn:G,onSuccess:async t=>{if(t.type==="error")throw t;await a.invalidateQueries({queryKey:i.notes.trashAll(),exact:!1}),c("The note has been permanently deleted.")},onError:()=>{c("Failed to permanently delete the note.")}}),D=()=>{if(!x)return;const{id:t}=x;j(null),p.mutate(t)},q=async t=>{const n=await V(t);if(n.type==="error"){c("Failed to check linked notes before permanently deleting this note.");return}if(a.setQueryData(i.notes.backReferences(t),n.backReferences),n.backReferences.length>0){j({id:t,backReferences:n.backReferences});return}await r("Permanently delete this note? This cannot be undone.")&&p.mutate(t)},T=u.totalCount>0?`Trash (${u.totalCount})`:void 0,A=u.notes.find(t=>t.id===f)??null;return u.notes.length===0?e.jsx(w,{title:"Trash",variant:"default",heading:T,description:R,children:e.jsx(Y,{title:"Trash is empty",description:`Delete a note and you can restore it here for ${S} days before permanent removal`})}):e.jsxs(w,{title:"Trash",variant:"default",heading:T,description:R,children:[e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("div",{className:"grid gap-3",children:u.notes.map(t=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex min-w-0 items-start gap-1.5",children:[e.jsx(d,{as:"h2",variant:"body",weight:"medium",className:"min-w-0 flex-1 break-words leading-[1.25]",children:t.title||"Untitled note"}),t.pinned&&e.jsx("span",{title:"Pinned",className:"inline-flex h-5 w-5 shrink-0 items-center justify-center text-fg-tertiary",children:e.jsx(H,{className:"h-3.5 w-3.5"})})]}),e.jsx("div",{className:"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1",children:e.jsxs(d,{as:"div",variant:"meta",weight:"medium",tone:"secondary",children:["Deleted ",P(t.deletedAt)]})}),t.tagNames.length>0&&e.jsx("div",{className:"mt-2 flex flex-wrap gap-1.5",children:t.tagNames.map(n=>e.jsx(d,{as:"span",variant:"label",weight:"medium",tone:"tertiary",className:"rounded-full border border-border-subtle bg-hover-subtle px-2 py-0.5",children:n},`${t.id}-${n}`))}),t.contentPreview&&e.jsx("div",{className:"mt-3 rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-3 py-2",children:e.jsx(d,{as:"p",variant:"meta",tone:"secondary",className:"line-clamp-3 whitespace-pre-wrap break-words",children:t.contentPreview})})]}),e.jsxs("div",{className:"flex shrink-0 flex-wrap gap-2 sm:justify-end",children:[e.jsx(b,{variant:"subtle",size:"sm",onClick:()=>h(t.id),children:"View content"}),e.jsx(b,{variant:"subtle",size:"sm",isLoading:N.isPending&&N.variables===t.id,onClick:()=>N.mutate(t.id),children:"Restore"}),e.jsx(b,{variant:"soft-danger",size:"sm",isLoading:p.isPending&&p.variables===t.id,onClick:()=>q(t.id),children:"Delete now"})]})]})},t.id))}),u.totalCount>m&&e.jsx(W,{page:s,last:Math.ceil(u.totalCount/m),onChange:t=>{o({search:n=>({...n,page:t})})}})]}),e.jsx(X,{note:A,onClose:()=>h(null)}),e.jsx($,{isOpen:!!x,title:g.title,description:g.description,references:x?.backReferences??[],confirmLabel:g.confirmLabel,confirmVariant:g.confirmVariant,onClose:()=>j(null),onConfirm:D})]})},oe=()=>{const{page:s}=v.useSearch();return e.jsx(Q,{fallback:e.jsx(ee,{}),errorTitle:"Failed to load trash",errorDescription:"Retry loading deleted notes",resetKeys:[s],children:e.jsx(te,{})})};export{oe as default};
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/app.ts"],"sourcesContent":["import express from 'express';\nimport { createMcpAdminService, type McpAdminService } from './features/mcp-admin/service.js';\nimport { purgeExpiredNoteSnapshots } from './features/note/services/snapshot.js';\nimport { purgeExpiredTrashedNotes } from './features/note/services/trash.js';\nimport { createSessionMiddleware } from './modules/auth-guard.js';\nimport type { AuthConfig } from './modules/auth-mode.js';\nimport { createErrorHandler } from './modules/error-handler.js';\nimport logger from './modules/logger.js';\nimport { createApiRouter, createAuthPagesRouter, createClientRouter, createGraphqlRouter } from './routes/index.js';\n\nexport const createApp = (authConfig: AuthConfig) => {\n const mcpAdminService = createMcpAdminService();\n return createAppWithMcpAuth(authConfig, mcpAdminService);\n};\n\nexport const createAppWithMcpAuth = (authConfig: AuthConfig, mcpAdminService: McpAdminService) => {\n const app = express();\n app.locals.authConfig = authConfig;\n\n void Promise.all([purgeExpiredNoteSnapshots(), purgeExpiredTrashedNotes()]).catch((error) => {\n const message = error instanceof Error ? error.message : 'Unknown recovery cleanup error';\n process.stderr.write(`[recovery] Startup cleanup failed: ${message}\\n`);\n });\n\n app.use(logger)\n .use(createSessionMiddleware(authConfig))\n .use(express.urlencoded({ extended: false }))\n .use(express.json({ limit: '50mb' }))\n .use('/api', createApiRouter(authConfig, mcpAdminService))\n .use(createAuthPagesRouter(authConfig))\n .use('/graphql', createGraphqlRouter(authConfig, mcpAdminService))\n .use(createClientRouter(authConfig))\n .use(createErrorHandler());\n\n return app;\n};\n\nexport default createApp;\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,SAAS,6BAAmD;AAC5D,SAAS,iCAAiC;AAC1C,SAAS,gCAAgC;AACzC,SAAS,+BAA+B;AAExC,SAAS,0BAA0B;AACnC,OAAO,YAAY;AACnB,SAAS,iBAAiB,uBAAuB,oBAAoB,2BAA2B;AAEzF,MAAM,YAAY,CAAC,eAA2B;AACjD,QAAM,kBAAkB,sBAAsB;AAC9C,SAAO,qBAAqB,YAAY,eAAe;AAC3D;AAEO,MAAM,uBAAuB,CAAC,YAAwB,oBAAqC;AAC9F,QAAM,MAAM,QAAQ;AACpB,MAAI,OAAO,aAAa;AAExB,OAAK,QAAQ,IAAI,CAAC,0BAA0B,GAAG,yBAAyB,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU;AACzF,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,YAAQ,OAAO,MAAM,sCAAsC,OAAO;AAAA,CAAI;AAAA,EAC1E,CAAC;AAED,MAAI,IAAI,MAAM,EACT,IAAI,wBAAwB,UAAU,CAAC,EACvC,IAAI,QAAQ,WAAW,EAAE,UAAU,MAAM,CAAC,CAAC,EAC3C,IAAI,QAAQ,KAAK,EAAE,OAAO,OAAO,CAAC,CAAC,EACnC,IAAI,QAAQ,gBAAgB,YAAY,eAAe,CAAC,EACxD,IAAI,sBAAsB,UAAU,CAAC,EACrC,IAAI,YAAY,oBAAoB,YAAY,eAAe,CAAC,EAChE,IAAI,mBAAmB,UAAU,CAAC,EAClC,IAAI,mBAAmB,CAAC;AAE7B,SAAO;AACX;AAEA,IAAO,cAAQ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/auth/http/api.ts"],"sourcesContent":["import { buildAuthSessionResponse } from '@baejino/auth';\nimport type { AuthConfig } from '~/modules/auth-mode.js';\nimport { createAppError } from '~/modules/error-handler.js';\nimport type { Controller } from '~/types/index.js';\nimport {\n assertPasswordLoginAvailable,\n buildSessionResponse,\n compareSharedSecret,\n destroySession,\n refreshCsrfToken,\n regenerateSession,\n setSessionStatusHeaders,\n} from '../service.js';\n\nexport const createLoginHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n const expectedPassword = assertPasswordLoginAvailable(authConfig);\n const password = typeof req.body?.password === 'string' ? req.body.password : '';\n\n if (!password || !compareSharedSecret(expectedPassword, password)) {\n throw createAppError(401, 'UNAUTHORIZED', 'Invalid password');\n }\n\n await regenerateSession(req);\n req.session.authenticated = true;\n refreshCsrfToken(req);\n\n res.status(200).json(buildSessionResponse(authConfig, req)).end();\n };\n};\n\nexport const createLogoutHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n if (authConfig.mode === 'password') {\n await destroySession(req);\n }\n\n res.status(200).json(buildAuthSessionResponse(authConfig, false)).end();\n };\n};\n\nexport const createSessionStatusHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n setSessionStatusHeaders(res);\n res.status(200).json(buildSessionResponse(authConfig, req)).end();\n };\n};\n"],"mappings":"AAAA,SAAS,gCAAgC;AAEzC,SAAS,sBAAsB;AAE/B;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AAEA,MAAM,qBAAqB,CAAC,eAAuC;AACtE,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,mBAAmB,6BAA6B,UAAU;AAChE,UAAM,WAAW,OAAO,IAAI,MAAM,aAAa,WAAW,IAAI,KAAK,WAAW;AAE9E,QAAI,CAAC,YAAY,CAAC,oBAAoB,kBAAkB,QAAQ,GAAG;AAC/D,YAAM,eAAe,KAAK,gBAAgB,kBAAkB;AAAA,IAChE;AAEA,UAAM,kBAAkB,GAAG;AAC3B,QAAI,QAAQ,gBAAgB;AAC5B,qBAAiB,GAAG;AAEpB,QAAI,OAAO,GAAG,EAAE,KAAK,qBAAqB,YAAY,GAAG,CAAC,EAAE,IAAI;AAAA,EACpE;AACJ;AAEO,MAAM,sBAAsB,CAAC,eAAuC;AACvE,SAAO,OAAO,KAAK,QAAQ;AACvB,QAAI,WAAW,SAAS,YAAY;AAChC,YAAM,eAAe,GAAG;AAAA,IAC5B;AAEA,QAAI,OAAO,GAAG,EAAE,KAAK,yBAAyB,YAAY,KAAK,CAAC,EAAE,IAAI;AAAA,EAC1E;AACJ;AAEO,MAAM,6BAA6B,CAAC,eAAuC;AAC9E,SAAO,OAAO,KAAK,QAAQ;AACvB,4BAAwB,GAAG;AAC3B,QAAI,OAAO,GAAG,EAAE,KAAK,qBAAqB,YAAY,GAAG,CAAC,EAAE,IAAI;AAAA,EACpE;AACJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/auth/http/login-page.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\n\ndeclare const __LOGIN_PAGE_TEMPLATE__: string | undefined;\n\nconst LOGIN_ERROR_TOKEN = '<!-- OCEAN_BRAIN_LOGIN_ERROR -->';\nconst NEXT_PATH_TOKEN = 'OCEAN_BRAIN_NEXT_PATH';\n\nconst readLoginPageTemplate = () => {\n if (typeof __LOGIN_PAGE_TEMPLATE__ === 'string') {\n return __LOGIN_PAGE_TEMPLATE__;\n }\n\n return readFileSync(new URL('./login-page.html', import.meta.url), 'utf8');\n};\n\nconst LOGIN_PAGE_TEMPLATE = readLoginPageTemplate();\n\nconst escapeHtml = (value: string) =>\n value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;');\n\ninterface LoginPageParams {\n nextPath: string;\n errorMessage?: string;\n csrfToken?: string;\n}\n\nconst renderLoginError = (errorMessage?: string) => {\n if (!errorMessage) {\n return '';\n }\n\n return `<div class=\"error\" role=\"alert\">${escapeHtml(errorMessage)}</div>`;\n};\n\nconst renderCsrfInput = (csrfToken?: string) => {\n if (!csrfToken) {\n return '';\n }\n\n return `<input type=\"hidden\" name=\"_csrf\" value=\"${escapeHtml(csrfToken)}\" />`;\n};\n\nconst renderLoginTemplate = (values: { errorBlock: string; nextPath: string; csrfInput: string }) => {\n return LOGIN_PAGE_TEMPLATE.replace(LOGIN_ERROR_TOKEN, values.errorBlock)\n .replace(NEXT_PATH_TOKEN, values.nextPath)\n .replace('<!-- OCEAN_BRAIN_CSRF_INPUT -->', values.csrfInput);\n};\n\nexport const renderLoginPage = ({ nextPath, errorMessage, csrfToken }: LoginPageParams) =>\n renderLoginTemplate({\n errorBlock: renderLoginError(errorMessage),\n nextPath: escapeHtml(nextPath),\n csrfInput: renderCsrfInput(csrfToken),\n });\n"],"mappings":"AAAA,SAAS,oBAAoB;AAI7B,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;AAExB,MAAM,wBAAwB,MAAM;AAChC,MAAI,MAA6C;AAC7C,WAAO;AAAA,EACX;AAEA,SAAO,aAAa,IAAI,IAAI,qBAAqB,YAAY,GAAG,GAAG,MAAM;AAC7E;AAEA,MAAM,sBAAsB,sBAAsB;AAElD,MAAM,aAAa,CAAC,UAChB,MACK,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,OAAO;AAQhC,MAAM,mBAAmB,CAAC,iBAA0B;AAChD,MAAI,CAAC,cAAc;AACf,WAAO;AAAA,EACX;AAEA,SAAO,mCAAmC,WAAW,YAAY,CAAC;AACtE;AAEA,MAAM,kBAAkB,CAAC,cAAuB;AAC5C,MAAI,CAAC,WAAW;AACZ,WAAO;AAAA,EACX;AAEA,SAAO,4CAA4C,WAAW,SAAS,CAAC;AAC5E;AAEA,MAAM,sBAAsB,CAAC,WAAwE;AACjG,SAAO,oBAAoB,QAAQ,mBAAmB,OAAO,UAAU,EAClE,QAAQ,iBAAiB,OAAO,QAAQ,EACxC,QAAQ,mCAAmC,OAAO,SAAS;AACpE;AAEO,MAAM,kBAAkB,CAAC,EAAE,UAAU,cAAc,UAAU,MAChE,oBAAoB;AAAA,EAChB,YAAY,iBAAiB,YAAY;AAAA,EACzC,UAAU,WAAW,QAAQ;AAAA,EAC7B,WAAW,gBAAgB,SAAS;AACxC,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/auth/http/pages.ts"],"sourcesContent":["import type { AuthConfig } from '~/modules/auth-mode.js';\nimport type { Controller } from '~/types/index.js';\nimport {\n compareSharedSecret,\n destroySession,\n refreshCsrfToken,\n regenerateSession,\n sanitizeRedirectPath,\n} from '../service.js';\nimport { renderLoginPage } from './login-page.js';\n\nexport const createLoginPageHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n if (authConfig.mode !== 'password' || req.session?.authenticated) {\n res.redirect(303, sanitizeRedirectPath(req.query.next));\n return;\n }\n\n const nextPath = sanitizeRedirectPath(req.query.next);\n res.status(200)\n .type('html')\n .send(renderLoginPage({ nextPath, csrfToken: res.locals._csrf }))\n .end();\n };\n};\n\nexport const createLoginPageSubmitHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n const nextPath = sanitizeRedirectPath(req.body?.next);\n\n if (authConfig.mode !== 'password' || !authConfig.password) {\n res.redirect(303, nextPath);\n return;\n }\n\n const password = typeof req.body?.password === 'string' ? req.body.password : '';\n\n if (!password || !compareSharedSecret(authConfig.password, password)) {\n res.status(401)\n .type('html')\n .send(\n renderLoginPage({\n nextPath,\n errorMessage: 'Invalid password',\n csrfToken: res.locals._csrf,\n }),\n )\n .end();\n return;\n }\n\n await regenerateSession(req);\n req.session.authenticated = true;\n refreshCsrfToken(req);\n\n res.redirect(303, nextPath);\n };\n};\n\nexport const createLogoutPageHandler = (authConfig: AuthConfig): Controller => {\n return async (req, res) => {\n if (authConfig.mode === 'password') {\n await destroySession(req);\n res.redirect(303, '/login');\n return;\n }\n\n res.redirect(303, '/');\n };\n};\n"],"mappings":"AAEA;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,uBAAuB;AAEzB,MAAM,yBAAyB,CAAC,eAAuC;AAC1E,SAAO,OAAO,KAAK,QAAQ;AACvB,QAAI,WAAW,SAAS,cAAc,IAAI,SAAS,eAAe;AAC9D,UAAI,SAAS,KAAK,qBAAqB,IAAI,MAAM,IAAI,CAAC;AACtD;AAAA,IACJ;AAEA,UAAM,WAAW,qBAAqB,IAAI,MAAM,IAAI;AACpD,QAAI,OAAO,GAAG,EACT,KAAK,MAAM,EACX,KAAK,gBAAgB,EAAE,UAAU,WAAW,IAAI,OAAO,MAAM,CAAC,CAAC,EAC/D,IAAI;AAAA,EACb;AACJ;AAEO,MAAM,+BAA+B,CAAC,eAAuC;AAChF,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,WAAW,qBAAqB,IAAI,MAAM,IAAI;AAEpD,QAAI,WAAW,SAAS,cAAc,CAAC,WAAW,UAAU;AACxD,UAAI,SAAS,KAAK,QAAQ;AAC1B;AAAA,IACJ;AAEA,UAAM,WAAW,OAAO,IAAI,MAAM,aAAa,WAAW,IAAI,KAAK,WAAW;AAE9E,QAAI,CAAC,YAAY,CAAC,oBAAoB,WAAW,UAAU,QAAQ,GAAG;AAClE,UAAI,OAAO,GAAG,EACT,KAAK,MAAM,EACX;AAAA,QACG,gBAAgB;AAAA,UACZ;AAAA,UACA,cAAc;AAAA,UACd,WAAW,IAAI,OAAO;AAAA,QAC1B,CAAC;AAAA,MACL,EACC,IAAI;AACT;AAAA,IACJ;AAEA,UAAM,kBAAkB,GAAG;AAC3B,QAAI,QAAQ,gBAAgB;AAC5B,qBAAiB,GAAG;AAEpB,QAAI,SAAS,KAAK,QAAQ;AAAA,EAC9B;AACJ;AAEO,MAAM,0BAA0B,CAAC,eAAuC;AAC3E,SAAO,OAAO,KAAK,QAAQ;AACvB,QAAI,WAAW,SAAS,YAAY;AAChC,YAAM,eAAe,GAAG;AACxB,UAAI,SAAS,KAAK,QAAQ;AAC1B;AAAA,IACJ;AAEA,QAAI,SAAS,KAAK,GAAG;AAAA,EACzB;AACJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/features/auth/service.ts"],"sourcesContent":["import { buildAuthSessionResponse } from '@baejino/auth';\nimport { compareSharedSecret as compareCommonSharedSecret } from '@baejino/auth/crypto';\nimport crypto from 'crypto';\nimport type { Request, Response } from 'express';\nimport type { AuthConfig } from '~/modules/auth-mode.js';\nimport { sanitizeRedirectPath } from '~/modules/auth-redirect.js';\nimport { createAppError } from '~/modules/error-handler.js';\n\nexport const AUTH_SESSION_GENERATION_HEADER = 'X-Ocean-Brain-Session-Generation';\nconst AUTH_SESSION_GENERATION = crypto.randomUUID();\n\nexport const createPasswordHash = async (password: string) => {\n const salt = crypto.randomBytes(16).toString('hex');\n const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return `${salt}:${hash}`;\n};\n\nexport const comparePassword = async (password: string, storedHash: string) => {\n const [salt, hash] = storedHash.split(':');\n const newHash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return hash === newHash;\n};\n\nexport const compareSharedSecret = compareCommonSharedSecret;\n\nexport const buildSessionResponse = (authConfig: AuthConfig, req: Request) =>\n buildAuthSessionResponse(authConfig, Boolean(req.session?.authenticated));\n\nexport const setSessionStatusHeaders = (res: Response) => {\n res.set(AUTH_SESSION_GENERATION_HEADER, AUTH_SESSION_GENERATION);\n res.set('Cache-Control', 'no-store');\n};\n\nexport const refreshCsrfToken = (req: Request) => {\n const requestWithCsrf = req as Request & { csrfToken?: () => string };\n requestWithCsrf.csrfToken?.();\n};\n\nexport const assertPasswordLoginAvailable = (authConfig: AuthConfig) => {\n if (authConfig.mode !== 'password') {\n throw createAppError(409, 'AUTH_OPEN_MODE', 'Login is unavailable while auth mode is open.');\n }\n\n return authConfig.password;\n};\n\nexport const regenerateSession = async (req: Request) => {\n await new Promise<void>((resolve, reject) => {\n req.session.regenerate((error) => {\n if (error) {\n reject(error);\n return;\n }\n\n resolve();\n });\n });\n};\n\nexport const destroySession = async (req: Request) => {\n if (!req.session) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n req.session.destroy((error) => {\n if (error) {\n reject(error);\n return;\n }\n\n resolve();\n });\n });\n};\n\nexport { sanitizeRedirectPath };\n"],"mappings":"AAAA,SAAS,gCAAgC;AACzC,SAAS,uBAAuB,iCAAiC;AACjE,OAAO,YAAY;AAGnB,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAExB,MAAM,iCAAiC;AAC9C,MAAM,0BAA0B,OAAO,WAAW;AAE3C,MAAM,qBAAqB,OAAO,aAAqB;AAC1D,QAAM,OAAO,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAClD,QAAM,OAAO,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACjF,SAAO,GAAG,IAAI,IAAI,IAAI;AAC1B;AAEO,MAAM,kBAAkB,OAAO,UAAkB,eAAuB;AAC3E,QAAM,CAAC,MAAM,IAAI,IAAI,WAAW,MAAM,GAAG;AACzC,QAAM,UAAU,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACpF,SAAO,SAAS;AACpB;AAEO,MAAM,sBAAsB;AAE5B,MAAM,uBAAuB,CAAC,YAAwB,QACzD,yBAAyB,YAAY,QAAQ,IAAI,SAAS,aAAa,CAAC;AAErE,MAAM,0BAA0B,CAAC,QAAkB;AACtD,MAAI,IAAI,gCAAgC,uBAAuB;AAC/D,MAAI,IAAI,iBAAiB,UAAU;AACvC;AAEO,MAAM,mBAAmB,CAAC,QAAiB;AAC9C,QAAM,kBAAkB;AACxB,kBAAgB,YAAY;AAChC;AAEO,MAAM,+BAA+B,CAAC,eAA2B;AACpE,MAAI,WAAW,SAAS,YAAY;AAChC,UAAM,eAAe,KAAK,kBAAkB,+CAA+C;AAAA,EAC/F;AAEA,SAAO,WAAW;AACtB;AAEO,MAAM,oBAAoB,OAAO,QAAiB;AACrD,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AACzC,QAAI,QAAQ,WAAW,CAAC,UAAU;AAC9B,UAAI,OAAO;AACP,eAAO,KAAK;AACZ;AAAA,MACJ;AAEA,cAAQ;AAAA,IACZ,CAAC;AAAA,EACL,CAAC;AACL;AAEO,MAAM,iBAAiB,OAAO,QAAiB;AAClD,MAAI,CAAC,IAAI,SAAS;AACd;AAAA,EACJ;AAEA,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AACzC,QAAI,QAAQ,QAAQ,CAAC,UAAU;AAC3B,UAAI,OAAO;AACP,eAAO,KAAK;AACZ;AAAA,MACJ;AAEA,cAAQ;AAAA,IACZ,CAAC;AAAA,EACL,CAAC;AACL;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/cache/graphql/cache.field.resolver.ts"],"sourcesContent":["export const cacheFieldResolvers = {};\n"],"mappings":"AAAO,MAAM,sBAAsB,CAAC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/cache/graphql/cache.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport models, { type Cache } from '~/models.js';\n\ntype CacheRecord = Pick<Cache, 'id' | 'key' | 'value'>;\ntype CacheMutationInput = Pick<Cache, 'key' | 'value'>;\n\ntype CacheMutationService = {\n findCacheByKey: (key: string) => Promise<CacheRecord | null>;\n createCache: (input: CacheMutationInput) => Promise<CacheRecord>;\n updateCache: (input: CacheMutationInput) => Promise<CacheRecord>;\n deleteCache: (key: string) => Promise<void>;\n};\n\nconst createCacheMutationService = (): CacheMutationService => ({\n findCacheByKey: (key) => models.cache.findFirst({ where: { key } }),\n createCache: ({ key, value }) =>\n models.cache.create({\n data: {\n key,\n value,\n },\n }),\n updateCache: ({ key, value }) =>\n models.cache.update({\n where: { key },\n data: { value },\n }),\n deleteCache: async (key) => {\n await models.cache.delete({ where: { key } });\n },\n});\n\nexport const createSetCacheMutationResolver = (service: CacheMutationService = createCacheMutationService()) => {\n return async (_: unknown, { key, value }: CacheMutationInput) => {\n const decodedValue = decodeURIComponent(value);\n const existingCache = await service.findCacheByKey(key);\n\n if (existingCache) {\n return service.updateCache({\n key,\n value: decodedValue,\n });\n }\n\n return service.createCache({\n key,\n value: decodedValue,\n });\n };\n};\n\nexport const createDeleteCacheMutationResolver = (service: CacheMutationService = createCacheMutationService()) => {\n return async (_: unknown, { key }: Pick<Cache, 'key'>) => {\n await service.deleteCache(key);\n return true;\n };\n};\n\nexport const cacheMutationResolvers: NonNullable<IResolvers['Mutation']> = {\n setCache: createSetCacheMutationResolver(),\n deleteCache: createDeleteCacheMutationResolver(),\n};\n"],"mappings":"AAEA,OAAO,YAA4B;AAYnC,MAAM,6BAA6B,OAA6B;AAAA,EAC5D,gBAAgB,CAAC,QAAQ,OAAO,MAAM,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA,EAClE,aAAa,CAAC,EAAE,KAAK,MAAM,MACvB,OAAO,MAAM,OAAO;AAAA,IAChB,MAAM;AAAA,MACF;AAAA,MACA;AAAA,IACJ;AAAA,EACJ,CAAC;AAAA,EACL,aAAa,CAAC,EAAE,KAAK,MAAM,MACvB,OAAO,MAAM,OAAO;AAAA,IAChB,OAAO,EAAE,IAAI;AAAA,IACb,MAAM,EAAE,MAAM;AAAA,EAClB,CAAC;AAAA,EACL,aAAa,OAAO,QAAQ;AACxB,UAAM,OAAO,MAAM,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAAA,EAChD;AACJ;AAEO,MAAM,iCAAiC,CAAC,UAAgC,2BAA2B,MAAM;AAC5G,SAAO,OAAO,GAAY,EAAE,KAAK,MAAM,MAA0B;AAC7D,UAAM,eAAe,mBAAmB,KAAK;AAC7C,UAAM,gBAAgB,MAAM,QAAQ,eAAe,GAAG;AAEtD,QAAI,eAAe;AACf,aAAO,QAAQ,YAAY;AAAA,QACvB;AAAA,QACA,OAAO;AAAA,MACX,CAAC;AAAA,IACL;AAEA,WAAO,QAAQ,YAAY;AAAA,MACvB;AAAA,MACA,OAAO;AAAA,IACX,CAAC;AAAA,EACL;AACJ;AAEO,MAAM,oCAAoC,CAAC,UAAgC,2BAA2B,MAAM;AAC/G,SAAO,OAAO,GAAY,EAAE,IAAI,MAA0B;AACtD,UAAM,QAAQ,YAAY,GAAG;AAC7B,WAAO;AAAA,EACX;AACJ;AAEO,MAAM,yBAA8D;AAAA,EACvE,UAAU,+BAA+B;AAAA,EACzC,aAAa,kCAAkC;AACnD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/cache/graphql/cache.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport models, { type Cache } from '~/models.js';\n\ntype CacheRecord = Pick<Cache, 'id' | 'key' | 'value'>;\n\ntype CacheQueryService = {\n findCaches: () => Promise<CacheRecord[]>;\n findCacheByKey: (key: string) => Promise<CacheRecord | null>;\n};\n\nconst createCacheQueryService = (): CacheQueryService => ({\n findCaches: () => models.cache.findMany(),\n findCacheByKey: (key) => models.cache.findUnique({ where: { key } }),\n});\n\nexport const createAllCachesQueryResolver = (service: CacheQueryService = createCacheQueryService()) => {\n return async () => service.findCaches();\n};\n\nexport const createCacheQueryResolver = (service: CacheQueryService = createCacheQueryService()) => {\n return async (_: unknown, { key }: { key: string }) => service.findCacheByKey(key);\n};\n\nexport const cacheQueryResolvers: NonNullable<IResolvers['Query']> = {\n allCaches: createAllCachesQueryResolver(),\n cache: createCacheQueryResolver(),\n};\n"],"mappings":"AAEA,OAAO,YAA4B;AASnC,MAAM,0BAA0B,OAA0B;AAAA,EACtD,YAAY,MAAM,OAAO,MAAM,SAAS;AAAA,EACxC,gBAAgB,CAAC,QAAQ,OAAO,MAAM,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE;AAEO,MAAM,+BAA+B,CAAC,UAA6B,wBAAwB,MAAM;AACpG,SAAO,YAAY,QAAQ,WAAW;AAC1C;AAEO,MAAM,2BAA2B,CAAC,UAA6B,wBAAwB,MAAM;AAChG,SAAO,OAAO,GAAY,EAAE,IAAI,MAAuB,QAAQ,eAAe,GAAG;AACrF;AAEO,MAAM,sBAAwD;AAAA,EACjE,WAAW,6BAA6B;AAAA,EACxC,OAAO,yBAAyB;AACpC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/cache/graphql/cache.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\n\nimport { cacheFieldResolvers } from './cache.field.resolver.js';\nimport { cacheMutationResolvers } from './cache.mutation.resolver.js';\nimport { cacheQueryResolvers } from './cache.query.resolver.js';\n\nexport const cacheResolvers: IResolvers = {\n Cache: cacheFieldResolvers,\n Query: cacheQueryResolvers,\n Mutation: cacheMutationResolvers,\n};\n"],"mappings":"AAEA,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AAE7B,MAAM,iBAA6B;AAAA,EACtC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AACd;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/cache/graphql/cache.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const cacheType = gql`\n type Cache {\n id: ID!\n key: String!\n value: String!\n }\n`;\n\nexport const cacheQuery = gql`\n type Query {\n allCaches: [Cache!]!\n cache(key: String!): Cache!\n }\n`;\n\nexport const cacheMutation = gql`\n type Mutation {\n setCache(key: String, value: String): Cache!\n deleteCache(key: String!): Boolean!\n }\n`;\n\nexport const cacheTypeDefs = `\n ${cacheType}\n ${cacheQuery}\n ${cacheMutation}\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQlB,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAOnB,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOtB,MAAM,gBAAgB;AAAA,MACvB,SAAS;AAAA,MACT,UAAU;AAAA,MACV,aAAa;AAAA;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/cache/graphql/index.ts"],"sourcesContent":["export { cacheFieldResolvers } from './cache.field.resolver.js';\nexport {\n cacheMutationResolvers,\n createDeleteCacheMutationResolver,\n createSetCacheMutationResolver,\n} from './cache.mutation.resolver.js';\nexport { cacheQueryResolvers, createAllCachesQueryResolver, createCacheQueryResolver } from './cache.query.resolver.js';\nexport { cacheResolvers } from './cache.resolver.js';\nexport { cacheTypeDefs } from './cache.type-defs.js';\n"],"mappings":"AAAA,SAAS,2BAA2B;AACpC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,qBAAqB,8BAA8B,gCAAgC;AAC5F,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/graphql/image.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport models, { type Image } from '~/models.js';\n\ntype ImageFieldResolvers = NonNullable<IResolvers['Image']>;\n\ntype CountImageReferences = (url: string) => Promise<number>;\n\nexport const createImageReferenceCountFieldResolver = (\n countReferences: CountImageReferences = async (url) => models.note.count({ where: { content: { contains: url } } }),\n) => {\n return async (image: Image) => {\n return countReferences(image.url);\n };\n};\n\nexport const imageFieldResolvers: ImageFieldResolvers = {\n referenceCount: createImageReferenceCountFieldResolver(),\n};\n"],"mappings":"AACA,OAAO,YAA4B;AAM5B,MAAM,yCAAyC,CAClD,kBAAwC,OAAO,QAAQ,OAAO,KAAK,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,IAAI,EAAE,EAAE,CAAC,MACjH;AACD,SAAO,OAAO,UAAiB;AAC3B,WAAO,gBAAgB,MAAM,GAAG;AAAA,EACpC;AACJ;AAEO,MAAM,sBAA2C;AAAA,EACpD,gBAAgB,uCAAuC;AAC3D;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/graphql/image.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { deleteImageById } from '../services/delete.js';\n\ntype ImageMutationResolvers = NonNullable<IResolvers['Mutation']>;\n\ntype DeleteImage = (id: number) => Promise<boolean>;\n\nexport const createDeleteImageMutationResolver = (deleteImage: DeleteImage = deleteImageById) => {\n return async (_: unknown, { id }: { id: string | number }) => {\n try {\n return await deleteImage(Number(id));\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[image] Delete failed: ${message}\\n`);\n return false;\n }\n };\n};\n\nexport const imageMutationResolvers: ImageMutationResolvers = {\n deleteImage: createDeleteImageMutationResolver(),\n};\n"],"mappings":"AACA,SAAS,uBAAuB;AAMzB,MAAM,oCAAoC,CAAC,cAA2B,oBAAoB;AAC7F,SAAO,OAAO,GAAY,EAAE,GAAG,MAA+B;AAC1D,QAAI;AACA,aAAO,MAAM,YAAY,OAAO,EAAE,CAAC;AAAA,IACvC,SAAS,OAAO;AACZ,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,cAAQ,OAAO,MAAM,0BAA0B,OAAO;AAAA,CAAI;AAC1D,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAEO,MAAM,yBAAiD;AAAA,EAC1D,aAAa,kCAAkC;AACnD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/graphql/image.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport models from '~/models.js';\nimport type { Pagination } from '~/types/index.js';\n\ntype ImageQueryResolvers = NonNullable<IResolvers['Query']>;\n\ninterface ImageQueryDeps {\n countImages: () => Promise<number>;\n findImageById: (id: number) => Promise<unknown>;\n findImages: (input: { orderBy: { createdAt: 'desc' }; skip: number; take: number }) => Promise<unknown[]>;\n}\n\nexport const createAllImagesQueryResolver = (\n deps: ImageQueryDeps = {\n countImages: async () => models.image.count(),\n findImageById: async (id) => models.image.findFirst({ where: { id } }),\n findImages: async (input) => models.image.findMany(input),\n },\n) => {\n return async (\n _: unknown,\n {\n pagination,\n }: {\n pagination: Pagination;\n },\n ) => {\n const [totalCount, images] = await Promise.all([\n deps.countImages(),\n deps.findImages({\n skip: pagination.offset,\n take: pagination.limit,\n orderBy: { createdAt: 'desc' },\n }),\n ]);\n\n return {\n totalCount,\n images,\n };\n };\n};\n\nexport const createImageQueryResolver = (\n deps: Pick<ImageQueryDeps, 'findImageById'> = {\n findImageById: async (id) => models.image.findFirst({ where: { id } }),\n },\n) => {\n return async (_: unknown, { id }: { id: string | number }) => {\n return deps.findImageById(Number(id));\n };\n};\n\nexport const imageQueryResolvers: ImageQueryResolvers = {\n allImages: createAllImagesQueryResolver(),\n image: createImageQueryResolver(),\n};\n"],"mappings":"AACA,OAAO,YAAY;AAWZ,MAAM,+BAA+B,CACxC,OAAuB;AAAA,EACnB,aAAa,YAAY,OAAO,MAAM,MAAM;AAAA,EAC5C,eAAe,OAAO,OAAO,OAAO,MAAM,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAAA,EACrE,YAAY,OAAO,UAAU,OAAO,MAAM,SAAS,KAAK;AAC5D,MACC;AACD,SAAO,OACH,GACA;AAAA,IACI;AAAA,EACJ,MAGC;AACD,UAAM,CAAC,YAAY,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3C,KAAK,YAAY;AAAA,MACjB,KAAK,WAAW;AAAA,QACZ,MAAM,WAAW;AAAA,QACjB,MAAM,WAAW;AAAA,QACjB,SAAS,EAAE,WAAW,OAAO;AAAA,MACjC,CAAC;AAAA,IACL,CAAC;AAED,WAAO;AAAA,MACH;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AACJ;AAEO,MAAM,2BAA2B,CACpC,OAA8C;AAAA,EAC1C,eAAe,OAAO,OAAO,OAAO,MAAM,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AACzE,MACC;AACD,SAAO,OAAO,GAAY,EAAE,GAAG,MAA+B;AAC1D,WAAO,KAAK,cAAc,OAAO,EAAE,CAAC;AAAA,EACxC;AACJ;AAEO,MAAM,sBAA2C;AAAA,EACpD,WAAW,6BAA6B;AAAA,EACxC,OAAO,yBAAyB;AACpC;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/graphql/image.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { imageFieldResolvers } from './image.field.resolver.js';\nimport { imageMutationResolvers } from './image.mutation.resolver.js';\nimport { imageQueryResolvers } from './image.query.resolver.js';\n\nexport const imageResolvers: IResolvers = {\n Query: imageQueryResolvers,\n Mutation: imageMutationResolvers,\n Image: imageFieldResolvers,\n};\n"],"mappings":"AACA,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AAE7B,MAAM,iBAA6B;AAAA,EACtC,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AACX;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/graphql/image.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const imageTypeDefs = gql`\n input PaginationInput {\n limit: Int!\n offset: Int!\n }\n\n type Image {\n id: ID!\n url: String!\n referenceCount: Int!\n }\n\n type Images {\n totalCount: Int!\n images: [Image!]!\n }\n\n type Query {\n allImages(pagination: PaginationInput): Images!\n image(id: ID!): Image!\n }\n\n type Mutation {\n deleteImage(id: ID!): Boolean!\n }\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/graphql/index.ts"],"sourcesContent":["export { imageFieldResolvers } from './image.field.resolver.js';\nexport { createDeleteImageMutationResolver, imageMutationResolvers } from './image.mutation.resolver.js';\nexport { createAllImagesQueryResolver, createImageQueryResolver, imageQueryResolvers } from './image.query.resolver.js';\nexport { imageResolvers } from './image.resolver.js';\nexport { imageTypeDefs } from './image.type-defs.js';\n"],"mappings":"AAAA,SAAS,2BAA2B;AACpC,SAAS,mCAAmC,8BAA8B;AAC1E,SAAS,8BAA8B,0BAA0B,2BAA2B;AAC5F,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/http/upload.ts"],"sourcesContent":["import { createAppError } from '~/modules/error-handler.js';\nimport type { Controller } from '~/types/index.js';\nimport { type PersistedImageSummary, type PersistImageInput, persistUploadedImage } from '../services/upload.js';\nimport { ImageValidationError, normalizeImageContentType, validateImagePayload } from '../services/validation.js';\n\ntype PersistImage = (input: PersistImageInput) => Promise<PersistedImageSummary>;\n\nconst IMAGE_DATA_URL_PATTERN = /^data:(image\\/[a-z0-9.+-]+);base64,([A-Za-z0-9+/]+={0,2})$/i;\n\nconst createUploadResponse = (image: PersistedImageSummary) => {\n return {\n id: image.id,\n url: image.url,\n };\n};\n\nconst parseImageDataUrl = (image: string) => {\n const match = IMAGE_DATA_URL_PATTERN.exec(image);\n\n if (!match || !match[2] || match[2].length % 4 !== 0) {\n return null;\n }\n\n return {\n base64: match[2],\n contentType: normalizeImageContentType(match[1]),\n };\n};\n\nconst createUploadValidationError = (error: ImageValidationError) => {\n if (error.code === 'IMAGE_UNSUPPORTED_CONTENT_TYPE') {\n return createAppError(415, 'IMAGE_UPLOAD_UNSUPPORTED_TYPE', 'Uploaded image content type is not supported.');\n }\n\n return createAppError(400, 'INVALID_IMAGE_UPLOAD', 'Uploaded image content is invalid.');\n};\n\nexport const createUploadImageHandler = (persistImage: PersistImage = persistUploadedImage): Controller => {\n return async (req, res) => {\n const { image } = req.body ?? {};\n\n if (typeof image !== 'string') {\n throw createAppError(400, 'INVALID_IMAGE_UPLOAD', 'No image uploaded');\n }\n\n const parsedImage = parseImageDataUrl(image);\n\n if (!parsedImage) {\n throw createAppError(400, 'INVALID_IMAGE_UPLOAD', 'No image uploaded');\n }\n\n let validatedImage;\n\n try {\n validatedImage = validateImagePayload({\n buffer: Buffer.from(parsedImage.base64, 'base64'),\n contentType: parsedImage.contentType,\n });\n } catch (error) {\n if (error instanceof ImageValidationError) {\n throw createUploadValidationError(error);\n }\n\n throw error;\n }\n\n const uploadedImage = await persistImage({\n buffer: validatedImage.buffer,\n extension: validatedImage.extension,\n });\n\n res.status(200).json(createUploadResponse(uploadedImage)).end();\n };\n};\n"],"mappings":"AAAA,SAAS,sBAAsB;AAE/B,SAA6D,4BAA4B;AACzF,SAAS,sBAAsB,2BAA2B,4BAA4B;AAItF,MAAM,yBAAyB;AAE/B,MAAM,uBAAuB,CAAC,UAAiC;AAC3D,SAAO;AAAA,IACH,IAAI,MAAM;AAAA,IACV,KAAK,MAAM;AAAA,EACf;AACJ;AAEA,MAAM,oBAAoB,CAAC,UAAkB;AACzC,QAAM,QAAQ,uBAAuB,KAAK,KAAK;AAE/C,MAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,EAAE,SAAS,MAAM,GAAG;AAClD,WAAO;AAAA,EACX;AAEA,SAAO;AAAA,IACH,QAAQ,MAAM,CAAC;AAAA,IACf,aAAa,0BAA0B,MAAM,CAAC,CAAC;AAAA,EACnD;AACJ;AAEA,MAAM,8BAA8B,CAAC,UAAgC;AACjE,MAAI,MAAM,SAAS,kCAAkC;AACjD,WAAO,eAAe,KAAK,iCAAiC,+CAA+C;AAAA,EAC/G;AAEA,SAAO,eAAe,KAAK,wBAAwB,oCAAoC;AAC3F;AAEO,MAAM,2BAA2B,CAAC,eAA6B,yBAAqC;AACvG,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,EAAE,MAAM,IAAI,IAAI,QAAQ,CAAC;AAE/B,QAAI,OAAO,UAAU,UAAU;AAC3B,YAAM,eAAe,KAAK,wBAAwB,mBAAmB;AAAA,IACzE;AAEA,UAAM,cAAc,kBAAkB,KAAK;AAE3C,QAAI,CAAC,aAAa;AACd,YAAM,eAAe,KAAK,wBAAwB,mBAAmB;AAAA,IACzE;AAEA,QAAI;AAEJ,QAAI;AACA,uBAAiB,qBAAqB;AAAA,QAClC,QAAQ,OAAO,KAAK,YAAY,QAAQ,QAAQ;AAAA,QAChD,aAAa,YAAY;AAAA,MAC7B,CAAC;AAAA,IACL,SAAS,OAAO;AACZ,UAAI,iBAAiB,sBAAsB;AACvC,cAAM,4BAA4B,KAAK;AAAA,MAC3C;AAEA,YAAM;AAAA,IACV;AAEA,UAAM,gBAAgB,MAAM,aAAa;AAAA,MACrC,QAAQ,eAAe;AAAA,MACvB,WAAW,eAAe;AAAA,IAC9B,CAAC;AAED,QAAI,OAAO,GAAG,EAAE,KAAK,qBAAqB,aAAa,CAAC,EAAE,IAAI;AAAA,EAClE;AACJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/services/delete.ts"],"sourcesContent":["import fs from 'fs';\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport models from '~/models.js';\nimport { paths } from '~/paths.js';\n\ninterface DeleteImageRecord {\n id: number;\n url: string;\n}\n\ninterface ImageDeleteDeps {\n deleteImageRecord: (id: number) => Promise<void>;\n fileExists: (filePath: string) => boolean;\n findImageById: (id: number) => Promise<DeleteImageRecord | null>;\n removeFile: (filePath: string) => Promise<void>;\n resolveImagePath: (url: string) => string;\n}\n\nexport const resolveStoredImagePath = (url: string) => {\n return path.resolve(paths.imageDir, url.replace('/assets/images/', ''));\n};\n\nexport const createImageDeleteService = (deps: ImageDeleteDeps) => {\n return {\n deleteImageById: async (id: number) => {\n const image = await deps.findImageById(id);\n\n if (!image) {\n return false;\n }\n\n const imagePath = deps.resolveImagePath(image.url);\n\n if (deps.fileExists(imagePath)) {\n await deps.removeFile(imagePath);\n }\n\n await deps.deleteImageRecord(id);\n\n return true;\n },\n };\n};\n\nconst defaultImageDeleteService = createImageDeleteService({\n deleteImageRecord: async (id) => {\n await models.image.delete({ where: { id } });\n },\n fileExists: (filePath) => fs.existsSync(filePath),\n findImageById: async (id) => {\n return models.image.findFirst({\n where: { id },\n select: {\n id: true,\n url: true,\n },\n });\n },\n removeFile: async (filePath) => {\n await fsPromises.unlink(filePath);\n },\n resolveImagePath: resolveStoredImagePath,\n});\n\nexport const deleteImageById = async (id: number) => {\n return defaultImageDeleteService.deleteImageById(id);\n};\n"],"mappings":"AAAA,OAAO,QAAQ;AACf,OAAO,gBAAgB;AACvB,OAAO,UAAU;AAEjB,OAAO,YAAY;AACnB,SAAS,aAAa;AAef,MAAM,yBAAyB,CAAC,QAAgB;AACnD,SAAO,KAAK,QAAQ,MAAM,UAAU,IAAI,QAAQ,mBAAmB,EAAE,CAAC;AAC1E;AAEO,MAAM,2BAA2B,CAAC,SAA0B;AAC/D,SAAO;AAAA,IACH,iBAAiB,OAAO,OAAe;AACnC,YAAM,QAAQ,MAAM,KAAK,cAAc,EAAE;AAEzC,UAAI,CAAC,OAAO;AACR,eAAO;AAAA,MACX;AAEA,YAAM,YAAY,KAAK,iBAAiB,MAAM,GAAG;AAEjD,UAAI,KAAK,WAAW,SAAS,GAAG;AAC5B,cAAM,KAAK,WAAW,SAAS;AAAA,MACnC;AAEA,YAAM,KAAK,kBAAkB,EAAE;AAE/B,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAEA,MAAM,4BAA4B,yBAAyB;AAAA,EACvD,mBAAmB,OAAO,OAAO;AAC7B,UAAM,OAAO,MAAM,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAAA,EAC/C;AAAA,EACA,YAAY,CAAC,aAAa,GAAG,WAAW,QAAQ;AAAA,EAChD,eAAe,OAAO,OAAO;AACzB,WAAO,OAAO,MAAM,UAAU;AAAA,MAC1B,OAAO,EAAE,GAAG;AAAA,MACZ,QAAQ;AAAA,QACJ,IAAI;AAAA,QACJ,KAAK;AAAA,MACT;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EACA,YAAY,OAAO,aAAa;AAC5B,UAAM,WAAW,OAAO,QAAQ;AAAA,EACpC;AAAA,EACA,kBAAkB;AACtB,CAAC;AAEM,MAAM,kBAAkB,OAAO,OAAe;AACjD,SAAO,0BAA0B,gBAAgB,EAAE;AACvD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/services/upload.ts"],"sourcesContent":["import crypto from 'crypto';\nimport fs from 'fs/promises';\nimport path from 'path';\n\nimport models from '~/models.js';\nimport { paths } from '~/paths.js';\n\ninterface ImageRecord {\n id: number;\n url: string;\n}\n\ninterface ImageUploadDeps {\n createImage: (input: { hash: string; url: string }) => Promise<ImageRecord>;\n ensureDir: (dirPath: string) => Promise<void>;\n findImageByHash: (hash: string) => Promise<ImageRecord | null>;\n removeFile: (filePath: string) => Promise<void>;\n writeFile: (filePath: string, buffer: Buffer) => Promise<void>;\n}\n\nexport interface PersistImageInput {\n buffer: Buffer;\n extension: string;\n}\n\nexport interface PersistedImageSummary {\n id: number;\n url: string;\n}\n\nexport const hashImageBuffer = (buffer: Buffer) => {\n return crypto.createHash('sha512').update(buffer).digest('hex');\n};\n\nconst buildImageTarget = (hash: string, extension: string, now = new Date()) => {\n const normalizedExtension = extension.trim().toLowerCase().replace(/^\\./, '');\n const currentPath = [now.getFullYear().toString(), (now.getMonth() + 1).toString(), now.getDate().toString()];\n const targetDir = path.resolve(paths.imageDir, ...currentPath);\n const fileName = `${hash}.${normalizedExtension}`;\n\n return {\n targetDir,\n absolutePath: path.resolve(targetDir, fileName),\n url: `/assets/images/${currentPath.join('/')}/${fileName}`,\n };\n};\n\nconst isImageHashUniqueConflict = (error: unknown) => {\n if (typeof error !== 'object' || error === null || !('code' in error)) {\n return false;\n }\n\n if (error.code !== 'P2002') {\n return false;\n }\n\n if (!('meta' in error) || typeof error.meta !== 'object' || error.meta === null) {\n return true;\n }\n\n const rawTarget = 'target' in error.meta ? error.meta.target : undefined;\n const targets = Array.isArray(rawTarget)\n ? rawTarget.filter((value): value is string => typeof value === 'string')\n : typeof rawTarget === 'string'\n ? [rawTarget]\n : [];\n\n return targets.length === 0 || targets.includes('hash');\n};\n\nconst removeFileIfPresent = async (deps: Pick<ImageUploadDeps, 'removeFile'>, filePath: string) => {\n try {\n await deps.removeFile(filePath);\n } catch (error) {\n if (typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT') {\n return;\n }\n\n throw error;\n }\n};\n\nexport const createImageUploadService = (deps: ImageUploadDeps) => {\n return {\n persistImage: async (input: PersistImageInput): Promise<PersistedImageSummary> => {\n const hash = hashImageBuffer(input.buffer);\n const existingImage = await deps.findImageByHash(hash);\n\n if (existingImage) {\n return existingImage;\n }\n\n const target = buildImageTarget(hash, input.extension);\n\n await deps.ensureDir(target.targetDir);\n await deps.writeFile(target.absolutePath, input.buffer);\n\n try {\n return await deps.createImage({\n hash,\n url: target.url,\n });\n } catch (error) {\n await removeFileIfPresent(deps, target.absolutePath);\n\n if (isImageHashUniqueConflict(error)) {\n const conflictedImage = await deps.findImageByHash(hash);\n\n if (conflictedImage) {\n return conflictedImage;\n }\n }\n\n throw error;\n }\n },\n };\n};\n\nconst defaultImageUploadService = createImageUploadService({\n createImage: async (input) => {\n return models.image.create({\n data: input,\n select: {\n id: true,\n url: true,\n },\n });\n },\n ensureDir: async (dirPath) => {\n await fs.mkdir(dirPath, { recursive: true });\n },\n findImageByHash: async (hash) => {\n return models.image.findFirst({\n where: { hash },\n select: {\n id: true,\n url: true,\n },\n });\n },\n removeFile: async (filePath) => {\n await fs.unlink(filePath);\n },\n writeFile: async (filePath, buffer) => {\n await fs.writeFile(filePath, buffer);\n },\n});\n\nexport const persistUploadedImage = async (input: PersistImageInput) => {\n return defaultImageUploadService.persistImage(input);\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/image/services/validation.ts"],"sourcesContent":["export type ImageValidationErrorCode = 'IMAGE_INVALID_CONTENT' | 'IMAGE_UNSUPPORTED_CONTENT_TYPE';\n\nexport class ImageValidationError extends Error {\n public readonly code: ImageValidationErrorCode;\n public readonly status: number;\n\n constructor(code: ImageValidationErrorCode, status: number, message: string) {\n super(message);\n this.name = 'ImageValidationError';\n this.code = code;\n this.status = status;\n }\n}\n\ninterface ValidateImagePayloadInput {\n buffer: Buffer;\n contentType: string;\n}\n\nexport interface ValidatedImagePayload {\n buffer: Buffer;\n contentType: string;\n extension: string;\n}\n\nconst SUPPORTED_IMAGE_TYPES = new Map([\n ['image/png', 'png'],\n ['image/jpeg', 'jpg'],\n ['image/gif', 'gif'],\n ['image/webp', 'webp'],\n ['image/bmp', 'bmp'],\n ['image/avif', 'avif'],\n]);\n\nconst startsWithBytes = (buffer: Buffer, bytes: number[]) => {\n return bytes.every((byte, index) => buffer[index] === byte);\n};\n\nconst readAscii = (buffer: Buffer, start: number, end: number) => {\n return buffer.subarray(start, end).toString('ascii');\n};\n\nconst hasPngSignature = (buffer: Buffer) => {\n return buffer.length >= 8 && startsWithBytes(buffer, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n};\n\nconst hasJpegSignature = (buffer: Buffer) => {\n return buffer.length >= 3 && startsWithBytes(buffer, [0xff, 0xd8, 0xff]);\n};\n\nconst hasGifSignature = (buffer: Buffer) => {\n return buffer.length >= 6 && ['GIF87a', 'GIF89a'].includes(readAscii(buffer, 0, 6));\n};\n\nconst hasWebpSignature = (buffer: Buffer) => {\n return buffer.length >= 12 && readAscii(buffer, 0, 4) === 'RIFF' && readAscii(buffer, 8, 12) === 'WEBP';\n};\n\nconst hasBmpSignature = (buffer: Buffer) => {\n return buffer.length >= 2 && readAscii(buffer, 0, 2) === 'BM';\n};\n\nconst hasAvifSignature = (buffer: Buffer) => {\n if (buffer.length < 16 || readAscii(buffer, 4, 8) !== 'ftyp') {\n return false;\n }\n\n const ftypBoxSize = buffer.readUInt32BE(0);\n\n if (ftypBoxSize !== 0 && (ftypBoxSize < 16 || ftypBoxSize > buffer.length)) {\n return false;\n }\n\n const isAvifBrand = (brand: string) => brand === 'avif' || brand === 'avis';\n const majorBrand = readAscii(buffer, 8, 12);\n\n if (isAvifBrand(majorBrand)) {\n return true;\n }\n\n const boxEnd = ftypBoxSize === 0 ? buffer.length : ftypBoxSize;\n\n for (let offset = 16; offset + 4 <= boxEnd; offset += 4) {\n const brand = readAscii(buffer, offset, offset + 4);\n\n if (isAvifBrand(brand)) {\n return true;\n }\n }\n\n return false;\n};\n\nconst hasExpectedImageSignature = (contentType: string, buffer: Buffer) => {\n switch (contentType) {\n case 'image/png':\n return hasPngSignature(buffer);\n case 'image/jpeg':\n return hasJpegSignature(buffer);\n case 'image/gif':\n return hasGifSignature(buffer);\n case 'image/webp':\n return hasWebpSignature(buffer);\n case 'image/bmp':\n return hasBmpSignature(buffer);\n case 'image/avif':\n return hasAvifSignature(buffer);\n default:\n return false;\n }\n};\n\nexport const normalizeImageContentType = (contentType: string | null | undefined) => {\n return contentType?.split(';')[0]?.trim().toLowerCase() ?? '';\n};\n\nexport const getSupportedImageExtension = (contentType: string) => {\n return SUPPORTED_IMAGE_TYPES.get(normalizeImageContentType(contentType));\n};\n\nexport const validateImagePayload = ({\n buffer,\n contentType: rawContentType,\n}: ValidateImagePayloadInput): ValidatedImagePayload => {\n const contentType = normalizeImageContentType(rawContentType);\n const extension = SUPPORTED_IMAGE_TYPES.get(contentType);\n\n if (!extension) {\n throw new ImageValidationError('IMAGE_UNSUPPORTED_CONTENT_TYPE', 415, 'Image content type is not supported.');\n }\n\n const hasExpectedSignature = hasExpectedImageSignature(contentType, buffer);\n\n if (!hasExpectedSignature) {\n throw new ImageValidationError('IMAGE_INVALID_CONTENT', 415, 'Image content does not match the declared type.');\n }\n\n return {\n buffer,\n contentType,\n extension,\n };\n};\n"],"mappings":"AAEO,MAAM,6BAA6B,MAAM;AAAA,EAC5B;AAAA,EACA;AAAA,EAEhB,YAAY,MAAgC,QAAgB,SAAiB;AACzE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAClB;AACJ;AAaA,MAAM,wBAAwB,oBAAI,IAAI;AAAA,EAClC,CAAC,aAAa,KAAK;AAAA,EACnB,CAAC,cAAc,KAAK;AAAA,EACpB,CAAC,aAAa,KAAK;AAAA,EACnB,CAAC,cAAc,MAAM;AAAA,EACrB,CAAC,aAAa,KAAK;AAAA,EACnB,CAAC,cAAc,MAAM;AACzB,CAAC;AAED,MAAM,kBAAkB,CAAC,QAAgB,UAAoB;AACzD,SAAO,MAAM,MAAM,CAAC,MAAM,UAAU,OAAO,KAAK,MAAM,IAAI;AAC9D;AAEA,MAAM,YAAY,CAAC,QAAgB,OAAe,QAAgB;AAC9D,SAAO,OAAO,SAAS,OAAO,GAAG,EAAE,SAAS,OAAO;AACvD;AAEA,MAAM,kBAAkB,CAAC,WAAmB;AACxC,SAAO,OAAO,UAAU,KAAK,gBAAgB,QAAQ,CAAC,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,EAAI,CAAC;AACzG;AAEA,MAAM,mBAAmB,CAAC,WAAmB;AACzC,SAAO,OAAO,UAAU,KAAK,gBAAgB,QAAQ,CAAC,KAAM,KAAM,GAAI,CAAC;AAC3E;AAEA,MAAM,kBAAkB,CAAC,WAAmB;AACxC,SAAO,OAAO,UAAU,KAAK,CAAC,UAAU,QAAQ,EAAE,SAAS,UAAU,QAAQ,GAAG,CAAC,CAAC;AACtF;AAEA,MAAM,mBAAmB,CAAC,WAAmB;AACzC,SAAO,OAAO,UAAU,MAAM,UAAU,QAAQ,GAAG,CAAC,MAAM,UAAU,UAAU,QAAQ,GAAG,EAAE,MAAM;AACrG;AAEA,MAAM,kBAAkB,CAAC,WAAmB;AACxC,SAAO,OAAO,UAAU,KAAK,UAAU,QAAQ,GAAG,CAAC,MAAM;AAC7D;AAEA,MAAM,mBAAmB,CAAC,WAAmB;AACzC,MAAI,OAAO,SAAS,MAAM,UAAU,QAAQ,GAAG,CAAC,MAAM,QAAQ;AAC1D,WAAO;AAAA,EACX;AAEA,QAAM,cAAc,OAAO,aAAa,CAAC;AAEzC,MAAI,gBAAgB,MAAM,cAAc,MAAM,cAAc,OAAO,SAAS;AACxE,WAAO;AAAA,EACX;AAEA,QAAM,cAAc,CAAC,UAAkB,UAAU,UAAU,UAAU;AACrE,QAAM,aAAa,UAAU,QAAQ,GAAG,EAAE;AAE1C,MAAI,YAAY,UAAU,GAAG;AACzB,WAAO;AAAA,EACX;AAEA,QAAM,SAAS,gBAAgB,IAAI,OAAO,SAAS;AAEnD,WAAS,SAAS,IAAI,SAAS,KAAK,QAAQ,UAAU,GAAG;AACrD,UAAM,QAAQ,UAAU,QAAQ,QAAQ,SAAS,CAAC;AAElD,QAAI,YAAY,KAAK,GAAG;AACpB,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AACX;AAEA,MAAM,4BAA4B,CAAC,aAAqB,WAAmB;AACvE,UAAQ,aAAa;AAAA,IACjB,KAAK;AACD,aAAO,gBAAgB,MAAM;AAAA,IACjC,KAAK;AACD,aAAO,iBAAiB,MAAM;AAAA,IAClC,KAAK;AACD,aAAO,gBAAgB,MAAM;AAAA,IACjC,KAAK;AACD,aAAO,iBAAiB,MAAM;AAAA,IAClC,KAAK;AACD,aAAO,gBAAgB,MAAM;AAAA,IACjC,KAAK;AACD,aAAO,iBAAiB,MAAM;AAAA,IAClC;AACI,aAAO;AAAA,EACf;AACJ;AAEO,MAAM,4BAA4B,CAAC,gBAA2C;AACjF,SAAO,aAAa,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,EAAE,YAAY,KAAK;AAC/D;AAEO,MAAM,6BAA6B,CAAC,gBAAwB;AAC/D,SAAO,sBAAsB,IAAI,0BAA0B,WAAW,CAAC;AAC3E;AAEO,MAAM,uBAAuB,CAAC;AAAA,EACjC;AAAA,EACA,aAAa;AACjB,MAAwD;AACpD,QAAM,cAAc,0BAA0B,cAAc;AAC5D,QAAM,YAAY,sBAAsB,IAAI,WAAW;AAEvD,MAAI,CAAC,WAAW;AACZ,UAAM,IAAI,qBAAqB,kCAAkC,KAAK,sCAAsC;AAAA,EAChH;AAEA,QAAM,uBAAuB,0BAA0B,aAAa,MAAM;AAE1E,MAAI,CAAC,sBAAsB;AACvB,UAAM,IAAI,qBAAqB,yBAAyB,KAAK,iDAAiD;AAAA,EAClH;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EACJ;AACJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/mcp-admin/http/handlers.ts"],"sourcesContent":["import { getOceanBrainVersionInfo } from '~/modules/app-version.js';\nimport { createAppError } from '~/modules/error-handler.js';\nimport type { Controller } from '~/types/index.js';\nimport { createMcpAdminService, type McpAdminService } from '../service.js';\n\ntype McpAdminControllerService = Pick<\n McpAdminService,\n 'getStatus' | 'setEnabled' | 'rotateToken' | 'revokeActiveToken'\n>;\n\nconst createMcpAdminStatusResponse = async (service: McpAdminControllerService) => {\n const status = await service.getStatus();\n\n return {\n ...status,\n server: getOceanBrainVersionInfo(),\n };\n};\n\nexport const createMcpAdminStatusHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (_req, res) => {\n const status = await createMcpAdminStatusResponse(service);\n res.status(200).json(status).end();\n };\n};\n\nexport const createMcpAdminSetEnabledHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (req, res) => {\n const enabled = req.body?.enabled;\n if (typeof enabled !== 'boolean') {\n throw createAppError(400, 'INVALID_MCP_ENABLED', 'enabled must be a boolean.');\n }\n\n await service.setEnabled(enabled);\n const status = await createMcpAdminStatusResponse(service);\n res.status(200).json(status).end();\n };\n};\n\nexport const createMcpAdminRotateTokenHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (_req, res) => {\n const result = await service.rotateToken();\n res.status(200)\n .json({\n token: result.token,\n message: 'Save this token now. It is shown only once.',\n })\n .end();\n };\n};\n\nexport const createMcpAdminRevokeTokenHandler = (\n service: McpAdminControllerService = createMcpAdminService(),\n): Controller => {\n return async (_req, res) => {\n await service.revokeActiveToken();\n const status = await createMcpAdminStatusResponse(service);\n res.status(200).json(status).end();\n };\n};\n"],"mappings":"AAAA,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAE/B,SAAS,6BAAmD;AAO5D,MAAM,+BAA+B,OAAO,YAAuC;AAC/E,QAAM,SAAS,MAAM,QAAQ,UAAU;AAEvC,SAAO;AAAA,IACH,GAAG;AAAA,IACH,QAAQ,yBAAyB;AAAA,EACrC;AACJ;AAEO,MAAM,8BAA8B,CACvC,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,MAAM,QAAQ;AACxB,UAAM,SAAS,MAAM,6BAA6B,OAAO;AACzD,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,IAAI;AAAA,EACrC;AACJ;AAEO,MAAM,kCAAkC,CAC3C,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,UAAU,IAAI,MAAM;AAC1B,QAAI,OAAO,YAAY,WAAW;AAC9B,YAAM,eAAe,KAAK,uBAAuB,4BAA4B;AAAA,IACjF;AAEA,UAAM,QAAQ,WAAW,OAAO;AAChC,UAAM,SAAS,MAAM,6BAA6B,OAAO;AACzD,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,IAAI;AAAA,EACrC;AACJ;AAEO,MAAM,mCAAmC,CAC5C,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,MAAM,QAAQ;AACxB,UAAM,SAAS,MAAM,QAAQ,YAAY;AACzC,QAAI,OAAO,GAAG,EACT,KAAK;AAAA,MACF,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,IACb,CAAC,EACA,IAAI;AAAA,EACb;AACJ;AAEO,MAAM,mCAAmC,CAC5C,UAAqC,sBAAsB,MAC9C;AACb,SAAO,OAAO,MAAM,QAAQ;AACxB,UAAM,QAAQ,kBAAkB;AAChC,UAAM,SAAS,MAAM,6BAA6B,OAAO;AACzD,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,IAAI;AAAA,EACrC;AACJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/features/mcp-admin/service.ts"],"sourcesContent":["import models from '~/models.js';\nimport { issueMcpToken, verifyMcpToken } from '~/modules/mcp-token.js';\n\nconst MCP_ENABLED_CACHE_KEY = 'MCP_ENABLED';\n\nexport interface McpTokenSummary {\n id: string;\n createdAt: string;\n lastUsedAt: string | null;\n}\n\nexport interface McpAdminStatus {\n enabled: boolean;\n hasActiveToken: boolean;\n token: McpTokenSummary | null;\n}\n\nexport interface McpTokenValidationResult {\n ok: boolean;\n reason?: 'not_configured' | 'forbidden';\n}\n\nexport interface McpAdminService {\n getStatus: () => Promise<McpAdminStatus>;\n setEnabled: (enabled: boolean) => Promise<void>;\n rotateToken: () => Promise<{ token: string }>;\n revokeActiveToken: () => Promise<void>;\n validatePresentedToken: (token: string) => Promise<McpTokenValidationResult>;\n}\n\ninterface McpAdminDb {\n cache: {\n findUnique: (args: { where: { key: string } }) => Promise<{ value: string } | null>;\n upsert: (args: {\n where: { key: string };\n update: { value: string };\n create: { key: string; value: string };\n }) => Promise<unknown>;\n };\n mcpToken: {\n findFirst: (args: { where: { revokedAt: Date | null }; orderBy: { createdAt: 'desc' | 'asc' } }) => Promise<{\n id: number;\n tokenHash: string;\n createdAt: Date;\n lastUsedAt: Date | null;\n revokedAt: Date | null;\n } | null>;\n updateMany: (args: {\n where: { revokedAt: Date | null };\n data: { revokedAt: Date };\n }) => Promise<{ count: number }>;\n create: (args: { data: { tokenHash: string } }) => Promise<unknown>;\n update: (args: { where: { id: number }; data: { lastUsedAt: Date } }) => Promise<unknown>;\n };\n $transaction: <T>(callback: (tx: McpAdminDb) => Promise<T>) => Promise<T>;\n}\n\nconst toStatus = (\n enabled: boolean,\n activeToken: {\n id: number;\n createdAt: Date;\n lastUsedAt: Date | null;\n } | null,\n): McpAdminStatus => {\n if (!activeToken) {\n return {\n enabled,\n hasActiveToken: false,\n token: null,\n };\n }\n\n return {\n enabled,\n hasActiveToken: true,\n token: {\n id: String(activeToken.id),\n createdAt: activeToken.createdAt.toISOString(),\n lastUsedAt: activeToken.lastUsedAt ? activeToken.lastUsedAt.toISOString() : null,\n },\n };\n};\n\nexport const createMcpAdminService = (db: McpAdminDb = models as unknown as McpAdminDb): McpAdminService => {\n return {\n async getStatus() {\n const enabledCache = await db.cache.findUnique({ where: { key: MCP_ENABLED_CACHE_KEY } });\n const activeToken = await db.mcpToken.findFirst({\n where: { revokedAt: null },\n orderBy: { createdAt: 'desc' },\n });\n\n return toStatus(enabledCache?.value === 'true', activeToken);\n },\n async setEnabled(enabled) {\n await db.cache.upsert({\n where: { key: MCP_ENABLED_CACHE_KEY },\n update: { value: String(enabled) },\n create: {\n key: MCP_ENABLED_CACHE_KEY,\n value: String(enabled),\n },\n });\n },\n async rotateToken() {\n const now = new Date();\n const issued = issueMcpToken();\n\n await db.$transaction(async (transaction) => {\n await transaction.mcpToken.updateMany({\n where: { revokedAt: null },\n data: { revokedAt: now },\n });\n await transaction.mcpToken.create({ data: { tokenHash: issued.hash } });\n });\n\n return { token: issued.plaintext };\n },\n async revokeActiveToken() {\n await db.mcpToken.updateMany({\n where: { revokedAt: null },\n data: { revokedAt: new Date() },\n });\n },\n async validatePresentedToken(token) {\n const activeToken = await db.mcpToken.findFirst({\n where: { revokedAt: null },\n orderBy: { createdAt: 'desc' },\n });\n\n if (!activeToken) {\n return {\n ok: false,\n reason: 'not_configured',\n };\n }\n\n if (!verifyMcpToken(activeToken.tokenHash, token)) {\n return {\n ok: false,\n reason: 'forbidden',\n };\n }\n\n await db.mcpToken.update({\n where: { id: activeToken.id },\n data: { lastUsedAt: new Date() },\n });\n\n return { ok: true };\n },\n };\n};\n"],"mappings":"AAAA,OAAO,YAAY;AACnB,SAAS,eAAe,sBAAsB;AAE9C,MAAM,wBAAwB;AAsD9B,MAAM,WAAW,CACb,SACA,gBAKiB;AACjB,MAAI,CAAC,aAAa;AACd,WAAO;AAAA,MACH;AAAA,MACA,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AAAA,IACH;AAAA,IACA,gBAAgB;AAAA,IAChB,OAAO;AAAA,MACH,IAAI,OAAO,YAAY,EAAE;AAAA,MACzB,WAAW,YAAY,UAAU,YAAY;AAAA,MAC7C,YAAY,YAAY,aAAa,YAAY,WAAW,YAAY,IAAI;AAAA,IAChF;AAAA,EACJ;AACJ;AAEO,MAAM,wBAAwB,CAAC,KAAiB,WAAqD;AACxG,SAAO;AAAA,IACH,MAAM,YAAY;AACd,YAAM,eAAe,MAAM,GAAG,MAAM,WAAW,EAAE,OAAO,EAAE,KAAK,sBAAsB,EAAE,CAAC;AACxF,YAAM,cAAc,MAAM,GAAG,SAAS,UAAU;AAAA,QAC5C,OAAO,EAAE,WAAW,KAAK;AAAA,QACzB,SAAS,EAAE,WAAW,OAAO;AAAA,MACjC,CAAC;AAED,aAAO,SAAS,cAAc,UAAU,QAAQ,WAAW;AAAA,IAC/D;AAAA,IACA,MAAM,WAAW,SAAS;AACtB,YAAM,GAAG,MAAM,OAAO;AAAA,QAClB,OAAO,EAAE,KAAK,sBAAsB;AAAA,QACpC,QAAQ,EAAE,OAAO,OAAO,OAAO,EAAE;AAAA,QACjC,QAAQ;AAAA,UACJ,KAAK;AAAA,UACL,OAAO,OAAO,OAAO;AAAA,QACzB;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,IACA,MAAM,cAAc;AAChB,YAAM,MAAM,oBAAI,KAAK;AACrB,YAAM,SAAS,cAAc;AAE7B,YAAM,GAAG,aAAa,OAAO,gBAAgB;AACzC,cAAM,YAAY,SAAS,WAAW;AAAA,UAClC,OAAO,EAAE,WAAW,KAAK;AAAA,UACzB,MAAM,EAAE,WAAW,IAAI;AAAA,QAC3B,CAAC;AACD,cAAM,YAAY,SAAS,OAAO,EAAE,MAAM,EAAE,WAAW,OAAO,KAAK,EAAE,CAAC;AAAA,MAC1E,CAAC;AAED,aAAO,EAAE,OAAO,OAAO,UAAU;AAAA,IACrC;AAAA,IACA,MAAM,oBAAoB;AACtB,YAAM,GAAG,SAAS,WAAW;AAAA,QACzB,OAAO,EAAE,WAAW,KAAK;AAAA,QACzB,MAAM,EAAE,WAAW,oBAAI,KAAK,EAAE;AAAA,MAClC,CAAC;AAAA,IACL;AAAA,IACA,MAAM,uBAAuB,OAAO;AAChC,YAAM,cAAc,MAAM,GAAG,SAAS,UAAU;AAAA,QAC5C,OAAO,EAAE,WAAW,KAAK;AAAA,QACzB,SAAS,EAAE,WAAW,OAAO;AAAA,MACjC,CAAC;AAED,UAAI,CAAC,aAAa;AACd,eAAO;AAAA,UACH,IAAI;AAAA,UACJ,QAAQ;AAAA,QACZ;AAAA,MACJ;AAEA,UAAI,CAAC,eAAe,YAAY,WAAW,KAAK,GAAG;AAC/C,eAAO;AAAA,UACH,IAAI;AAAA,UACJ,QAAQ;AAAA,QACZ;AAAA,MACJ;AAEA,YAAM,GAAG,SAAS,OAAO;AAAA,QACrB,OAAO,EAAE,IAAI,YAAY,GAAG;AAAA,QAC5B,MAAM,EAAE,YAAY,oBAAI,KAAK,EAAE;AAAA,MACnC,CAAC;AAED,aAAO,EAAE,IAAI,KAAK;AAAA,IACtB;AAAA,EACJ;AACJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/index.ts"],"sourcesContent":["export { createAllNotesQueryResolver, noteResolvers } from './note.resolver.js';\nexport { noteTypeDefs } from './note.type-defs.js';\n"],"mappings":"AAAA,SAAS,6BAA6B,qBAAqB;AAC3D,SAAS,oBAAoB;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/note.field.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { listNoteProperties } from '~/features/note/services/properties.js';\nimport { renderNoteSnapshotContentAsMarkdown } from '~/features/note/services/snapshot.js';\nimport type { Note } from '~/models.js';\nimport models from '~/models.js';\n\ntype NoteFieldResolvers = NonNullable<IResolvers['Note']>;\ntype NoteSnapshotFieldResolvers = NonNullable<IResolvers['NoteSnapshot']>;\n\ninterface NoteSnapshotSource {\n contentAsMarkdown?: string;\n payload?: string;\n}\n\nexport const noteFieldResolvers: NoteFieldResolvers = {\n tags: async (note: Note) => models.tag.findMany({ where: { notes: { some: { id: note.id } } } }),\n properties: async (note: Note) => listNoteProperties(note.id),\n contentAsMarkdown: async (note: Note) => {\n const { blocksToMarkdown } = await import('~/modules/blocknote.js');\n return blocksToMarkdown(note.content);\n },\n};\n\nexport const noteSnapshotFieldResolvers: NoteSnapshotFieldResolvers = {\n contentAsMarkdown: async (snapshot: NoteSnapshotSource) => {\n if (typeof snapshot.contentAsMarkdown === 'string') {\n return snapshot.contentAsMarkdown;\n }\n\n if (typeof snapshot.payload === 'string') {\n return renderNoteSnapshotContentAsMarkdown(snapshot.payload);\n }\n\n return '';\n },\n};\n"],"mappings":"AACA,SAAS,0BAA0B;AACnC,SAAS,2CAA2C;AAEpD,OAAO,YAAY;AAUZ,MAAM,qBAAyC;AAAA,EAClD,MAAM,OAAO,SAAe,OAAO,IAAI,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC;AAAA,EAC/F,YAAY,OAAO,SAAe,mBAAmB,KAAK,EAAE;AAAA,EAC5D,mBAAmB,OAAO,SAAe;AACrC,UAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAwB;AAClE,WAAO,iBAAiB,KAAK,OAAO;AAAA,EACxC;AACJ;AAEO,MAAM,6BAAyD;AAAA,EAClE,mBAAmB,OAAO,aAAiC;AACvD,QAAI,OAAO,SAAS,sBAAsB,UAAU;AAChD,aAAO,SAAS;AAAA,IACpB;AAEA,QAAI,OAAO,SAAS,YAAY,UAAU;AACtC,aAAO,oCAAoC,SAAS,OAAO;AAAA,IAC/D;AAEA,WAAO;AAAA,EACX;AACJ;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/note.mutation.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport type { Request } from 'express';\nimport { GraphQLError } from 'graphql';\nimport { extractBlocksByType, parseNoteContent } from '~/features/note/services/content-blocks.js';\nimport {\n createNotePropertyDefinition,\n deleteNotePropertyDefinition,\n InvalidNotePropertyInputError,\n type NotePropertiesPatchInput,\n type NotePropertyDefinitionInput,\n type NotePropertyDefinitionUpdateInput,\n NotePropertyDeleteConfirmationRequiredError,\n updateNotePropertiesWithVersionGuard,\n updateNotePropertyDefinition,\n} from '~/features/note/services/properties.js';\nimport { buildNoteSearchProjection } from '~/features/note/services/search.js';\nimport { createSnapshotMetaFromUserAgent, restoreNoteSnapshot } from '~/features/note/services/snapshot.js';\nimport { purgeTrashedNoteById, restoreTrashedNoteById, trashNoteById } from '~/features/note/services/trash.js';\nimport { updateNoteWithVersionGuard } from '~/features/note/services/write.js';\nimport {\n isInvalidNoteVersionError,\n isMissingNoteVersionError,\n isNoteVersionConflictError,\n} from '~/features/note/services/write-conflict.js';\nimport models from '~/models.js';\nimport type { NoteInput } from '~/types/index.js';\n\nconst PLACEHOLDER_PREFIX = '{%';\nconst PLACEHOLDER_SUFFIX = '%}';\n\nconst replacePlaceholders = async (content: string) => {\n const placeholders = content.matchAll(new RegExp(`${PLACEHOLDER_PREFIX}([^}]+)${PLACEHOLDER_SUFFIX}`, 'g'));\n const resolvedPlaceholders = await models.placeholder.findMany({\n select: {\n template: true,\n replacement: true,\n },\n where: { template: { in: Array.from(new Set(Array.from(placeholders, (match) => match[1]))) } },\n });\n\n let replacedContent = content;\n\n for (const placeholder of resolvedPlaceholders) {\n replacedContent = replacedContent.replace(\n new RegExp(`${PLACEHOLDER_PREFIX}${placeholder.template}${PLACEHOLDER_SUFFIX}`, 'g'),\n placeholder.replacement,\n );\n }\n\n return replacedContent;\n};\n\nconst getRequestUserAgent = (req?: Request) => {\n const userAgentHeader = req?.headers['user-agent'];\n return Array.isArray(userAgentHeader) ? userAgentHeader[0] : userAgentHeader;\n};\n\nconst toTagConnections = (blocks: Array<{ props?: { id?: string | number } }>) => {\n return blocks\n .map((block) => block.props?.id)\n .filter((id) => id !== undefined && id !== null && String(id).trim() !== '')\n .map((id) => Number(id))\n .filter(Number.isFinite)\n .map((id) => ({ id }));\n};\n\nconst parseRequiredNoteContent = (content: string) => {\n const parsedContent = parseNoteContent(content);\n\n if (!parsedContent) {\n throw new GraphQLError('Note content must be valid BlockNote JSON.', {\n extensions: {\n code: 'INVALID_NOTE_CONTENT',\n },\n });\n }\n\n return parsedContent;\n};\n\ntype NoteMutationResolvers = NonNullable<IResolvers['Mutation']>;\ntype CreateNoteInput = Required<Pick<NoteInput, 'title' | 'content'>> & Pick<NoteInput, 'layout'>;\n\nexport const noteMutationResolvers: NoteMutationResolvers = {\n createNote: async (_, { note }: { note: CreateNoteInput }) => {\n const replacedTitle = await replacePlaceholders(note.title);\n const replacedContent = await replacePlaceholders(note.content);\n const parsedContent = replacedContent ? parseRequiredNoteContent(replacedContent) : null;\n const blocks = parsedContent ? extractBlocksByType<{ id: string }>('tag', parsedContent) : [];\n const createdNote = await models.note.create({\n data: {\n title: replacedTitle,\n content: replacedContent,\n ...buildNoteSearchProjection({\n title: replacedTitle,\n content: replacedContent,\n }),\n ...(note.layout && { layout: note.layout }),\n },\n });\n\n if (!replacedContent) {\n return createdNote;\n }\n\n return models.note.update({\n where: { id: createdNote.id },\n data: { tags: { set: toTagConnections(blocks) } },\n });\n },\n updateNote: async (\n _,\n {\n id,\n note,\n editSessionId,\n expectedUpdatedAt,\n force,\n }: {\n id: number;\n note: NoteInput;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n force?: boolean;\n },\n context: {\n req?: Request;\n },\n ) => {\n const parsedContent = note.content ? parseRequiredNoteContent(note.content) : null;\n const blocks = parsedContent ? extractBlocksByType<{ id: string }>('tag', parsedContent) : [];\n try {\n const updatedNote = await updateNoteWithVersionGuard({\n id: Number(id),\n data: {\n ...(note.title !== undefined ? { title: note.title } : {}),\n ...(note.content !== undefined ? { content: note.content } : {}),\n ...(note.layout !== undefined ? { layout: note.layout } : {}),\n ...(note.content !== undefined ? { tagIds: toTagConnections(blocks).map((tag) => tag.id) } : {}),\n },\n ...(editSessionId ? { editSessionId } : {}),\n ...(expectedUpdatedAt ? { expectedUpdatedAt } : {}),\n ...(force ? { force: true } : {}),\n snapshotMeta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n if (!updatedNote) {\n throw 'NOT FOUND';\n }\n\n return updatedNote;\n } catch (error) {\n if (isNoteVersionConflictError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n currentUpdatedAt: error.currentUpdatedAt,\n expectedUpdatedAt: error.expectedUpdatedAt,\n },\n });\n }\n\n if (isInvalidNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n if (isMissingNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n throw error;\n }\n },\n deleteNote: async (_, { id }: { id: string }) => {\n const trashedNote = await trashNoteById(Number(id));\n\n if (!trashedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n restoreNoteSnapshot: async (\n _,\n { id }: { id: string },\n context: {\n req?: Request;\n },\n ) => {\n const note = await restoreNoteSnapshot(Number(id), {\n meta: createSnapshotMetaFromUserAgent(getRequestUserAgent(context.req)),\n });\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n restoreTrashedNote: async (_, { id }: { id: string }) => {\n const note = await restoreTrashedNoteById(Number(id));\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n },\n purgeTrashedNote: async (_, { id }: { id: string }) => {\n const purgedNote = await purgeTrashedNoteById(Number(id));\n\n if (!purgedNote) {\n throw 'NOT FOUND';\n }\n\n return true;\n },\n createNotePropertyKey: async (_, { input }: { input: NotePropertyDefinitionInput }) => {\n try {\n return await createNotePropertyDefinition(input);\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n throw error;\n }\n },\n updateNotePropertyKey: async (_, { key, input }: { key: string; input: NotePropertyDefinitionUpdateInput }) => {\n try {\n const result = await updateNotePropertyDefinition({ key, input });\n\n if (!result) {\n throw 'NOT FOUND';\n }\n\n return result;\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n throw error;\n }\n },\n deleteNotePropertyKey: async (_, { key, confirmImpact }: { key: string; confirmImpact?: boolean }) => {\n try {\n const result = await deleteNotePropertyDefinition({ key, confirmImpact });\n\n if (!result) {\n throw 'NOT FOUND';\n }\n\n return result;\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n if (error instanceof NotePropertyDeleteConfirmationRequiredError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n affectedNoteCount: error.affectedNoteCount,\n },\n });\n }\n\n throw error;\n }\n },\n updateNoteProperties: async (\n _,\n {\n id,\n patch,\n editSessionId,\n expectedUpdatedAt,\n force,\n }: {\n id: string;\n patch: NotePropertiesPatchInput;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n force?: boolean;\n },\n ) => {\n try {\n const note = await updateNotePropertiesWithVersionGuard({\n id: Number(id),\n patch,\n ...(editSessionId ? { editSessionId } : {}),\n ...(expectedUpdatedAt ? { expectedUpdatedAt } : {}),\n ...(force ? { force: true } : {}),\n });\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n return note;\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n if (isNoteVersionConflictError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n currentUpdatedAt: error.currentUpdatedAt,\n expectedUpdatedAt: error.expectedUpdatedAt,\n },\n });\n }\n\n if (isInvalidNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n if (isMissingNoteVersionError(error)) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: error.code,\n },\n });\n }\n\n throw error;\n }\n },\n pinNote: (_, { id, pinned }: { id: string; pinned: boolean }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { pinned: Boolean(pinned) },\n }),\n reorderNotes: async (_, { notes }: { notes: Array<{ id: string; order: number }> }) => {\n const updatePromises = notes.map(({ id, order }) =>\n models.note.update({\n where: { id: Number(id) },\n data: { order },\n }),\n );\n\n return Promise.all(updatePromises);\n },\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/note.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { getNoteCleanupPreview, listNoteCleanupCandidates } from '~/features/note/services/cleanup.js';\nimport {\n buildNoteGraph,\n contentReferencesNote,\n extractReferenceBlocksFromContent,\n normalizeReferenceId,\n syncReferenceTitlesInContent,\n} from '~/features/note/services/content-blocks.js';\nimport { listNotePropertyKeys } from '~/features/note/services/properties.js';\nimport {\n buildNoteSearchProjection,\n filterNotesBySearchQuery,\n NOTE_SEARCH_TEXT_SCHEMA_VERSION,\n parseNoteSearchQuery,\n} from '~/features/note/services/search.js';\nimport { diffNoteSnapshot, getNoteSnapshot, listNoteSnapshots } from '~/features/note/services/snapshot.js';\nimport {\n buildNoteTagNamesWhere,\n type NoteTagMatchMode,\n normalizeNoteTagNames,\n} from '~/features/note/services/tag-filter.js';\nimport { getTrashedNoteById, listTrashedNotes } from '~/features/note/services/trash.js';\nimport type { Note, Prisma } from '~/models.js';\nimport models from '~/models.js';\nimport { runDataMaintenanceInBackground } from '~/modules/data-maintenance.js';\nimport type { Pagination, SearchFilter } from '~/types/index.js';\n\ninterface AllNotesResolverDeps {\n countNotes: (args: { where?: Prisma.NoteWhereInput }) => Promise<number>;\n findNotes: (args: {\n orderBy: Prisma.NoteOrderByWithRelationInput[];\n where?: Prisma.NoteWhereInput;\n take?: number;\n skip?: number;\n }) => Promise<Note[]>;\n triggerSearchBackfill: () => void;\n}\n\nconst MAX_SQLITE_INT_ID = 2_147_483_647;\n\nconst isValidNoteIdString = (id: string) => {\n if (!/^[1-9]\\d*$/.test(id)) {\n return false;\n }\n\n const numericId = Number(id);\n\n return Number.isSafeInteger(numericId) && numericId <= MAX_SQLITE_INT_ID;\n};\n\ninterface NoteResolverDeps {\n findNote: (id: number) => Promise<Note | null>;\n findReferenceNotes: (ids: number[]) => Promise<Note[]>;\n updateNoteContent: (input: {\n id: number;\n updatedAt: Date;\n content: string;\n searchableText: string;\n searchableTextVersion: number;\n }) => Promise<Note>;\n isRecordNotFoundError: (error: unknown) => boolean;\n}\n\nconst buildAllNotesOrderBy = (searchFilter: SearchFilter) => {\n const sortBy = searchFilter.sortBy || 'updatedAt';\n const sortOrder = searchFilter.sortOrder || 'desc';\n const pinnedFirst = searchFilter.pinnedFirst || false;\n const orderBy: Prisma.NoteOrderByWithRelationInput[] = [];\n\n if (pinnedFirst) {\n orderBy.push({ pinned: 'desc' });\n }\n\n if (sortBy === 'createdAt') {\n orderBy.push({ createdAt: sortOrder as 'asc' | 'desc' });\n } else {\n orderBy.push({ updatedAt: sortOrder as 'asc' | 'desc' });\n }\n\n return orderBy;\n};\n\nconst buildAllNotesSearchWhere = (searchFilter: SearchFilter) => {\n const parsedQuery = parseNoteSearchQuery(searchFilter.query);\n\n if (!parsedQuery.hasFilters) {\n return undefined;\n }\n\n return {\n AND: [\n { searchableTextVersion: NOTE_SEARCH_TEXT_SCHEMA_VERSION },\n ...parsedQuery.included.map((keyword) => ({ searchableText: { contains: keyword } })),\n ...parsedQuery.excluded.map((keyword) => ({ NOT: { searchableText: { contains: keyword } } })),\n ],\n } satisfies Prisma.NoteWhereInput;\n};\n\nconst buildAllNotesStaleCandidateWhere = (searchFilter: SearchFilter) => {\n const parsedQuery = parseNoteSearchQuery(searchFilter.query);\n\n return {\n AND: [\n { searchableTextVersion: { not: NOTE_SEARCH_TEXT_SCHEMA_VERSION } },\n ...parsedQuery.included.map((keyword) => ({\n OR: [{ title: { contains: keyword } }, { content: { contains: keyword } }],\n })),\n ],\n } satisfies Prisma.NoteWhereInput;\n};\n\nconst compareNotesForSearch = (left: Note, right: Note, searchFilter: SearchFilter) => {\n if (searchFilter.pinnedFirst && left.pinned !== right.pinned) {\n return left.pinned ? -1 : 1;\n }\n\n const sortBy = searchFilter.sortBy || 'updatedAt';\n const sortOrder = searchFilter.sortOrder || 'desc';\n const leftValue = sortBy === 'createdAt' ? left.createdAt.getTime() : left.updatedAt.getTime();\n const rightValue = sortBy === 'createdAt' ? right.createdAt.getTime() : right.updatedAt.getTime();\n\n if (leftValue === rightValue) {\n return 0;\n }\n\n return sortOrder === 'asc' ? leftValue - rightValue : rightValue - leftValue;\n};\n\nconst mergeSortedNotesForSearch = (left: Note[], right: Note[], searchFilter: SearchFilter) => {\n const merged: Note[] = [];\n let leftIndex = 0;\n let rightIndex = 0;\n\n while (leftIndex < left.length && rightIndex < right.length) {\n if (compareNotesForSearch(left[leftIndex], right[rightIndex], searchFilter) <= 0) {\n merged.push(left[leftIndex]);\n leftIndex += 1;\n } else {\n merged.push(right[rightIndex]);\n rightIndex += 1;\n }\n }\n\n while (leftIndex < left.length) {\n merged.push(left[leftIndex]);\n leftIndex += 1;\n }\n\n while (rightIndex < right.length) {\n merged.push(right[rightIndex]);\n rightIndex += 1;\n }\n\n return merged;\n};\n\nconst isRecordNotFoundError = (error: unknown) => {\n return (\n typeof error === 'object' && error !== null && 'code' in error && (error as { code?: unknown }).code === 'P2025'\n );\n};\n\nexport const createAllNotesQueryResolver = (\n deps: AllNotesResolverDeps = {\n countNotes: ({ where }) => models.note.count({ where }),\n findNotes: ({ orderBy, where, take, skip }) =>\n models.note.findMany({\n orderBy,\n where,\n take,\n skip,\n }),\n triggerSearchBackfill: () => {\n void runDataMaintenanceInBackground();\n },\n },\n) => {\n return async (\n _: unknown,\n {\n searchFilter,\n pagination,\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n },\n ) => {\n const orderBy = buildAllNotesOrderBy(searchFilter);\n const limit = Number(pagination.limit);\n const offset = Number(pagination.offset);\n const where = buildAllNotesSearchWhere(searchFilter);\n\n if (!where) {\n const [notes, totalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n take: limit,\n skip: offset,\n }),\n deps.countNotes({}),\n ]);\n\n return {\n totalCount,\n notes,\n };\n }\n\n const staleCandidateNotes = await deps.findNotes({\n orderBy,\n where: buildAllNotesStaleCandidateWhere(searchFilter),\n });\n\n if (staleCandidateNotes.length === 0) {\n const [notes, totalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n where,\n take: limit,\n skip: offset,\n }),\n deps.countNotes({ where }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n }\n\n deps.triggerSearchBackfill();\n\n const parsedQuery = parseNoteSearchQuery(searchFilter.query);\n const staleNotes = filterNotesBySearchQuery(staleCandidateNotes, parsedQuery);\n\n if (staleNotes.length === 0) {\n const [notes, totalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n where,\n take: limit,\n skip: offset,\n }),\n deps.countNotes({ where }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n }\n\n const [freshNotesPrefix, freshTotalCount] = await Promise.all([\n deps.findNotes({\n orderBy,\n where,\n take: offset + limit,\n }),\n deps.countNotes({ where }),\n ]);\n\n const mergedNotes = mergeSortedNotesForSearch(freshNotesPrefix, staleNotes, searchFilter);\n\n return {\n totalCount: freshTotalCount + staleNotes.length,\n notes: mergedNotes.slice(offset, offset + limit),\n };\n };\n};\n\ntype NoteQueryResolvers = NonNullable<IResolvers['Query']>;\n\ninterface BackReferencesQueryResolverDeps {\n findCandidateNotes: (noteId: number) => Promise<Note[]>;\n}\n\nexport const createBackReferencesQueryResolver = (\n deps: BackReferencesQueryResolverDeps = {\n findCandidateNotes: (noteId) =>\n models.note.findMany({\n orderBy: [{ pinned: 'desc' }, { updatedAt: 'desc' }],\n where: {\n NOT: { id: noteId },\n },\n }),\n },\n) => {\n return async (_: unknown, { id }: { id: string }) => {\n const notes = await deps.findCandidateNotes(Number(id));\n\n return notes.filter((note) => contentReferencesNote(note.content, id));\n };\n};\n\ninterface NoteGraphQueryResolverDeps {\n findNotes: () => Promise<Array<Pick<Note, 'id' | 'title' | 'content'>>>;\n}\n\nexport const createNoteGraphQueryResolver = (\n deps: NoteGraphQueryResolverDeps = {\n findNotes: () =>\n models.note.findMany({\n select: {\n id: true,\n title: true,\n content: true,\n },\n }),\n },\n) => {\n return async () => {\n const notes = await deps.findNotes();\n\n return buildNoteGraph(notes);\n };\n};\n\nexport const createNoteQueryResolver = (\n deps: NoteResolverDeps = {\n findNote: (id) => models.note.findUnique({ where: { id } }),\n findReferenceNotes: (ids) => models.note.findMany({ where: { id: { in: ids } } }),\n updateNoteContent: ({ id, updatedAt, content, searchableText, searchableTextVersion }) =>\n models.note.update({\n where: {\n id,\n updatedAt,\n },\n data: {\n content,\n searchableText,\n searchableTextVersion,\n },\n }),\n isRecordNotFoundError,\n },\n) => {\n return async (_: unknown, { id }: { id: string }) => {\n const note = await deps.findNote(Number(id));\n\n if (!note) {\n throw 'NOT FOUND';\n }\n\n if (!note.content) {\n return note;\n }\n\n const blocks = extractReferenceBlocksFromContent(note.content);\n\n if (blocks.length === 0) {\n return note;\n }\n\n const referenceIds = Array.from(\n new Set(\n blocks\n .map((block) => normalizeReferenceId(block.props?.id))\n .filter(\n (referenceId): referenceId is string =>\n referenceId !== null && isValidNoteIdString(referenceId),\n )\n .map(Number),\n ),\n );\n const references = await deps.findReferenceNotes(referenceIds);\n const titlesById = new Map(\n references.map((referenceNote: Note) => [String(referenceNote.id), referenceNote.title]),\n );\n const newContent = syncReferenceTitlesInContent(note.content, titlesById);\n\n if (!newContent || newContent === note.content) {\n return note;\n }\n\n try {\n return await deps.updateNoteContent({\n id: note.id,\n updatedAt: note.updatedAt,\n content: newContent,\n ...buildNoteSearchProjection({\n title: note.title,\n content: newContent,\n }),\n });\n } catch (error) {\n if (deps.isRecordNotFoundError(error)) {\n return note;\n }\n\n throw error;\n }\n };\n};\n\nexport const noteQueryResolvers: NoteQueryResolvers = {\n allNotes: createAllNotesQueryResolver(),\n notesInDateRange: async (\n _,\n {\n dateRange,\n }: {\n dateRange: {\n start: string;\n end: string;\n };\n },\n ) => {\n const where: Prisma.NoteWhereInput = {\n OR: [\n {\n updatedAt: {\n gte: new Date(dateRange.start),\n lte: new Date(dateRange.end),\n },\n },\n {\n createdAt: {\n gte: new Date(dateRange.start),\n lte: new Date(dateRange.end),\n },\n },\n ],\n };\n\n return models.note.findMany({\n orderBy: { createdAt: 'asc' },\n where,\n });\n },\n tagNotes: async (\n _,\n {\n searchFilter,\n pagination,\n }: {\n searchFilter: SearchFilter;\n pagination: Pagination;\n },\n ) => {\n const where: Prisma.NoteWhereInput = { tags: { some: { id: Number(searchFilter.query) } } };\n const notes = models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset),\n });\n\n return {\n totalCount: models.note.count({ where }),\n notes,\n };\n },\n notesByTagNames: async (\n _,\n {\n tagNames,\n mode,\n pagination,\n }: {\n tagNames: string[];\n mode: NoteTagMatchMode;\n pagination: Pagination;\n },\n ) => {\n const normalizedTagNames = normalizeNoteTagNames(tagNames);\n\n if (normalizedTagNames.length === 0) {\n return {\n totalCount: 0,\n notes: [],\n };\n }\n\n const where = buildNoteTagNamesWhere(normalizedTagNames, mode);\n const notes = models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset),\n });\n\n return {\n totalCount: models.note.count({ where }),\n notes,\n };\n },\n pinnedNotes: async () =>\n models.note.findMany({\n orderBy: [{ order: 'asc' }, { updatedAt: 'desc' }],\n where: { pinned: true },\n }),\n imageNotes: async (_, { src }: { src: string }) =>\n models.note.findMany({\n orderBy: { updatedAt: 'desc' },\n where: { content: { contains: src } },\n }),\n backReferences: createBackReferencesQueryResolver(),\n note: createNoteQueryResolver(),\n noteCleanupCandidates: async (\n _,\n {\n query,\n pagination = {\n limit: 20,\n offset: 0,\n },\n }: {\n query?: string;\n pagination: Pagination;\n },\n ) => {\n const result = await listNoteCleanupCandidates({\n keywords: query\n ? query\n .split(/[,\\s]+/)\n .map((keyword) => keyword.trim())\n .filter(Boolean)\n : undefined,\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n\n return result.notes;\n },\n noteCleanupPreview: async (_, { id }: { id: string }) => {\n return getNoteCleanupPreview(Number(id));\n },\n noteSnapshots: async (\n _,\n {\n id,\n limit = 10,\n }: {\n id: string;\n limit?: number;\n },\n ) => {\n return listNoteSnapshots(Number(id), Number(limit));\n },\n noteSnapshot: async (_, { id }: { id: string }) => {\n return getNoteSnapshot(Number(id));\n },\n noteSnapshotDiff: async (\n _,\n {\n id,\n compareToSnapshotId,\n target,\n contextLines,\n }: {\n id: string;\n compareToSnapshotId?: string;\n target?: 'NEXT' | 'PREVIOUS' | 'CURRENT';\n contextLines?: number | null;\n },\n ) => {\n const normalizedContextLines = contextLines ?? 3;\n\n if (normalizedContextLines < 0 || normalizedContextLines > 20) {\n throw new Error('INVALID_SNAPSHOT_DIFF_CONTEXT_LINES');\n }\n\n return diffNoteSnapshot(Number(id), {\n ...(compareToSnapshotId !== undefined ? { compareToSnapshotId: Number(compareToSnapshotId) } : {}),\n ...(target !== undefined ? { target: target.toLowerCase() as 'next' | 'previous' | 'current' } : {}),\n contextLines: normalizedContextLines,\n });\n },\n notePropertyKeys: async (\n _,\n {\n query,\n pagination = {\n limit: 50,\n offset: 0,\n },\n }: {\n query?: string;\n pagination: Pagination;\n },\n ) => {\n return listNotePropertyKeys({\n query,\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n },\n trashedNote: async (_, { id }: { id: string }) => {\n return getTrashedNoteById(Number(id));\n },\n trashedNotes: async (\n _,\n {\n pagination = {\n limit: 25,\n offset: 0,\n },\n }: {\n pagination: Pagination;\n },\n ) => {\n return listTrashedNotes({\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n },\n noteGraph: createNoteGraphQueryResolver(),\n};\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/note.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { noteFieldResolvers, noteSnapshotFieldResolvers } from './note.field.resolver.js';\nimport { noteMutationResolvers } from './note.mutation.resolver.js';\nimport { createAllNotesQueryResolver, noteQueryResolvers } from './note.query.resolver.js';\n\nexport { createAllNotesQueryResolver } from './note.query.resolver.js';\n\nexport const noteResolvers: IResolvers = {\n Query: noteQueryResolvers,\n Mutation: noteMutationResolvers,\n Note: noteFieldResolvers,\n NoteSnapshot: noteSnapshotFieldResolvers,\n};\n"],"mappings":"AACA,SAAS,oBAAoB,kCAAkC;AAC/D,SAAS,6BAA6B;AACtC,SAAsC,0BAA0B;AAEhE,SAAS,+BAAAA,oCAAmC;AAErC,MAAM,gBAA4B;AAAA,EACrC,OAAO;AAAA,EACP,UAAU;AAAA,EACV,MAAM;AAAA,EACN,cAAc;AAClB;","names":["createAllNotesQueryResolver"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/features/note/graphql/note.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const noteType = gql`\n input PaginationInput {\n limit: Int!\n offset: Int!\n }\n\n input SearchFilterInput {\n query: String!\n sortBy: String\n sortOrder: String\n pinnedFirst: Boolean\n }\n\n input DateRangeInput {\n start: String!\n end: String!\n }\n\n enum NoteLayout {\n narrow\n wide\n full\n }\n\n enum TagMatchMode {\n and\n or\n }\n\n enum NotePropertyValueType {\n text\n url\n number\n date\n boolean\n select\n }\n\n input NoteInput {\n title: String\n content: String\n layout: NoteLayout\n }\n\n input NotePropertySetInput {\n key: String!\n name: String\n value: String!\n valueType: NotePropertyValueType!\n }\n\n input NotePropertyOptionInput {\n label: String!\n value: String\n color: String\n order: Int\n }\n\n input NotePropertyOptionUpdateInput {\n id: ID\n label: String!\n value: String\n color: String\n order: Int\n }\n\n input NotePropertyDefinitionInput {\n key: String!\n name: String\n valueType: NotePropertyValueType!\n options: [NotePropertyOptionInput!]\n }\n\n input NotePropertyDefinitionUpdateInput {\n name: String\n options: [NotePropertyOptionUpdateInput!]\n }\n\n input NotePropertiesPatchInput {\n set: [NotePropertySetInput!]\n deleteKeys: [String!]\n }\n\n input NoteOrderInput {\n id: ID!\n order: Int!\n }\n\n type Tag {\n id: ID!\n name: String!\n createdAt: String!\n updatedAt: String!\n }\n\n type Note {\n id: ID!\n title: String!\n content: String!\n contentAsMarkdown: String!\n createdAt: String!\n updatedAt: String!\n pinned: Boolean!\n order: Int!\n layout: NoteLayout!\n tags: [Tag!]!\n properties: [NoteProperty!]!\n }\n\n type NoteProperty {\n key: String!\n name: String!\n value: String!\n valueType: NotePropertyValueType!\n createdAt: String!\n option: NotePropertyOption\n updatedAt: String!\n }\n\n type NotePropertyOption {\n id: ID!\n label: String!\n value: String!\n color: String\n order: Int!\n }\n\n type NotePropertyKey {\n key: String!\n name: String!\n valueType: NotePropertyValueType!\n noteCount: Int!\n options: [NotePropertyOption!]!\n updatedAt: String!\n }\n\n type NotePropertyKeys {\n totalCount: Int!\n keys: [NotePropertyKey!]!\n }\n\n type NotePropertyDeleteResult {\n key: String!\n name: String!\n valueType: NotePropertyValueType!\n affectedNoteCount: Int!\n deleted: Boolean!\n }\n\n type Notes {\n totalCount: Int!\n notes: [Note!]!\n }\n\n type NoteCleanupBackReference {\n id: ID!\n title: String!\n }\n\n type NoteCleanupCandidate {\n id: ID!\n title: String!\n updatedAt: String!\n pinned: Boolean!\n tagNames: [String!]!\n reminderCount: Int!\n backReferenceCount: Int!\n matchedTerms: [String!]!\n reasons: [String!]!\n requiresForce: Boolean!\n forceReasons: [String!]!\n }\n\n type NoteCleanupPreview {\n id: ID!\n title: String!\n updatedAt: String!\n pinned: Boolean!\n tagNames: [String!]!\n reminderCount: Int!\n backReferences: [NoteCleanupBackReference!]!\n orphanedTagNames: [String!]!\n requiresForce: Boolean!\n forceReasons: [String!]!\n }\n\n type NoteSnapshotMeta {\n entrypoint: String\n label: String\n }\n\n type NoteSnapshot {\n id: ID!\n title: String!\n contentPreview: String!\n contentAsMarkdown: String!\n createdAt: String!\n meta: NoteSnapshotMeta!\n }\n\n enum NoteSnapshotDiffTarget {\n NEXT\n PREVIOUS\n CURRENT\n }\n\n type NoteSnapshotDiffEndpoint {\n kind: String!\n id: ID!\n title: String!\n createdAt: String\n updatedAt: String\n meta: NoteSnapshotMeta\n }\n\n type NoteSnapshotDiffDetail {\n markdown: String!\n changedLineCount: Int!\n changedCharCount: Int!\n beforeMarkdownSha256: String!\n afterMarkdownSha256: String!\n }\n\n type NoteSnapshotDiff {\n noteId: ID!\n mode: String!\n before: NoteSnapshotDiffEndpoint!\n after: NoteSnapshotDiffEndpoint!\n diff: NoteSnapshotDiffDetail!\n }\n\n type DeletedNote {\n id: ID!\n title: String!\n createdAt: String!\n updatedAt: String!\n deletedAt: String!\n contentPreview: String!\n contentAsMarkdown: String\n pinned: Boolean!\n order: Int!\n layout: NoteLayout!\n tagNames: [String!]!\n }\n\n type DeletedNotes {\n totalCount: Int!\n notes: [DeletedNote!]!\n }\n`;\n\nexport const noteQuery = gql`\n type GraphNode {\n id: ID!\n title: String!\n connections: Int!\n }\n\n type GraphLink {\n source: ID!\n target: ID!\n }\n\n type NoteGraph {\n nodes: [GraphNode!]!\n links: [GraphLink!]!\n }\n\n type Query {\n allNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!\n tagNotes(searchFilter: SearchFilterInput, pagination: PaginationInput): Notes!\n notesByTagNames(tagNames: [String!]!, mode: TagMatchMode!, pagination: PaginationInput): Notes!\n notesInDateRange(dateRange: DateRangeInput): [Note!]!\n pinnedNotes: [Note!]!\n imageNotes(src: String!): [Note!]!\n backReferences(id: ID!): [Note]!\n note(id: ID!): Note!\n noteCleanupCandidates(query: String, pagination: PaginationInput): [NoteCleanupCandidate!]!\n noteCleanupPreview(id: ID!): NoteCleanupPreview\n noteSnapshots(id: ID!, limit: Int): [NoteSnapshot!]!\n noteSnapshot(id: ID!): NoteSnapshot\n noteSnapshotDiff(id: ID!, compareToSnapshotId: ID, target: NoteSnapshotDiffTarget, contextLines: Int): NoteSnapshotDiff\n notePropertyKeys(query: String, pagination: PaginationInput): NotePropertyKeys!\n trashedNote(id: ID!): DeletedNote\n trashedNotes(pagination: PaginationInput): DeletedNotes!\n noteGraph: NoteGraph!\n }\n`;\n\nexport const noteMutation = gql`\n type Mutation {\n createNote(note: NoteInput!): Note!\n updateNote(id: ID!, note: NoteInput!, editSessionId: String, expectedUpdatedAt: String, force: Boolean): Note!\n deleteNote(id: ID!): Boolean!\n restoreNoteSnapshot(id: ID!): Note!\n restoreTrashedNote(id: ID!): Note!\n purgeTrashedNote(id: ID!): Boolean!\n createNotePropertyKey(input: NotePropertyDefinitionInput!): NotePropertyKey!\n updateNotePropertyKey(key: String!, input: NotePropertyDefinitionUpdateInput!): NotePropertyKey!\n deleteNotePropertyKey(key: String!, confirmImpact: Boolean): NotePropertyDeleteResult!\n updateNoteProperties(\n id: ID!\n patch: NotePropertiesPatchInput!\n editSessionId: String\n expectedUpdatedAt: String!\n force: Boolean\n ): Note!\n pinNote(id: ID!, pinned: Boolean!): Note!\n reorderNotes(notes: [NoteOrderInput!]!): [Note!]!\n }\n`;\n\nexport const noteTypeDefs = `\n ${noteType}\n ${noteQuery}\n ${noteMutation}\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2PjB,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsClB,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBrB,MAAM,eAAe;AAAA,MACtB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA;","names":[]}
@@ -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 resolveMetadataPropertyPatch = (value: unknown): NotePropertiesByKeyPatchInput | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Properties patch must be an object.');\n }\n\n const set =\n value.set === undefined\n ? undefined\n : Array.isArray(value.set)\n ? value.set.map((item) => {\n if (!isRecord(item) || typeof item.key !== 'string' || typeof item.value !== 'string') {\n throw createAppError(\n 400,\n 'INVALID_NOTE_PROPERTIES',\n 'Property set items must include string key and value fields.',\n );\n }\n\n return {\n key: item.key,\n value: item.value,\n };\n })\n : null;\n\n if (set === null) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property set must be an array.');\n }\n\n const deleteKeys =\n value.deleteKeys === undefined\n ? undefined\n : Array.isArray(value.deleteKeys)\n ? value.deleteKeys.map((key) => {\n if (typeof key !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property deleteKeys must be strings.');\n }\n\n return key;\n })\n : null;\n\n if (deleteKeys === null) {\n throw createAppError(400, 'INVALID_NOTE_PROPERTIES', 'Property deleteKeys must be an array.');\n }\n\n return {\n ...(set !== undefined ? { set } : {}),\n ...(deleteKeys !== undefined ? { deleteKeys } : {}),\n };\n};\n\nconst resolvePositiveNoteId = (value: unknown) => {\n const noteId = Number(value);\n\n if (!Number.isInteger(noteId) || noteId <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n return noteId;\n};\n\nconst serializeNoteWriteBaseline = (note: { id: number | string; updatedAt: Date | string }) => {\n return {\n id: String(note.id),\n updatedAt: note.updatedAt instanceof Date ? note.updatedAt.toISOString() : note.updatedAt,\n };\n};\n\nconst isNonNegativeInteger = (value: unknown): value is number => {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n};\n\nconst isPositiveInteger = (value: unknown): value is number => {\n return typeof value === 'number' && Number.isInteger(value) && value > 0;\n};\n\nconst resolveMarkdownWritePolicy = (value: unknown): MarkdownChangePolicy | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'Markdown write policy must be an object.');\n }\n\n const policy: MarkdownChangePolicy = {};\n\n if (value.allowNoop !== undefined) {\n if (typeof value.allowNoop !== 'boolean') {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'allowNoop must be a boolean.');\n }\n policy.allowNoop = value.allowNoop;\n }\n\n if (value.preserveTags !== undefined) {\n if (!PRESERVATION_POLICIES.has(value.preserveTags)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'preserveTags must be boolean or warn.');\n }\n policy.preserveTags = value.preserveTags as MarkdownChangePolicy['preserveTags'];\n }\n\n if (value.preserveReferences !== undefined) {\n if (!PRESERVATION_POLICIES.has(value.preserveReferences)) {\n throw createAppError(400, 'INVALID_MARKDOWN_POLICY', 'preserveReferences must be boolean or warn.');\n }\n policy.preserveReferences = value.preserveReferences as MarkdownChangePolicy['preserveReferences'];\n }\n\n return policy;\n};\n\nconst resolvePatchSelector = (value: unknown): MarkdownPatchSelector => {\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Patch selector must be an object.');\n }\n\n if (value.type === 'exact_text') {\n if (typeof value.text !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Exact text selector requires text.');\n }\n\n return {\n type: 'exact_text',\n text: value.text,\n ...(value.before !== undefined\n ? {\n before: resolveOptionalString(\n value.before,\n 'INVALID_PATCH_SELECTOR',\n 'Selector before must be a string.',\n ),\n }\n : {}),\n ...(value.after !== undefined\n ? {\n after: resolveOptionalString(\n value.after,\n 'INVALID_PATCH_SELECTOR',\n 'Selector after must be a string.',\n ),\n }\n : {}),\n };\n }\n\n if (value.type === 'match_candidate') {\n const { text, matchIndex, lineStart, matchSha256, surroundingHash, positionHint } = value;\n\n if (\n typeof text !== 'string' ||\n !isNonNegativeInteger(matchIndex) ||\n !isPositiveInteger(lineStart) ||\n typeof matchSha256 !== 'string' ||\n typeof surroundingHash !== 'string'\n ) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Match candidate selector has invalid fields.');\n }\n\n if (positionHint !== undefined && !POSITION_HINTS.has(String(positionHint))) {\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'positionHint must be first or last.');\n }\n\n return {\n type: 'match_candidate',\n text,\n matchIndex,\n lineStart,\n matchSha256,\n surroundingHash,\n ...(positionHint ? { positionHint: positionHint as 'first' | 'last' } : {}),\n };\n }\n\n throw createAppError(400, 'INVALID_PATCH_SELECTOR', 'Unsupported patch selector type.');\n};\n\nconst resolvePatchOperation = (value: unknown): MarkdownPatchOperation => {\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Patch operation must be an object.');\n }\n\n if (value.type === 'replace') {\n if (typeof value.replacement !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Replace operation requires replacement.');\n }\n\n return {\n type: 'replace',\n replacement: value.replacement,\n };\n }\n\n if (value.type === 'insert_before' || value.type === 'insert_after') {\n if (typeof value.insertion !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Insert operation requires insertion.');\n }\n\n return {\n type: value.type,\n insertion: value.insertion,\n };\n }\n\n throw createAppError(400, 'INVALID_PATCH_OPERATION', 'Unsupported patch operation type.');\n};\n\nconst resolveAppendPlacement = (value: unknown): MarkdownAppendPlacement | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (!isRecord(value)) {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Append placement must be an object.');\n }\n\n if (value.type === 'end') {\n return { type: 'end' };\n }\n\n if (value.type === 'after_heading') {\n if (typeof value.heading !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'after_heading placement requires heading.');\n }\n\n const { heading, level } = value;\n\n if (typeof heading !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'after_heading placement requires heading.');\n }\n\n if (level !== undefined && (typeof level !== 'number' || !Number.isInteger(level) || level < 1 || level > 6)) {\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Heading level must be an integer from 1 to 6.');\n }\n\n return {\n type: 'after_heading',\n heading,\n ...(level !== undefined ? { level } : {}),\n };\n }\n\n throw createAppError(400, 'INVALID_APPEND_PLACEMENT', 'Unsupported append placement type.');\n};\n\nconst resolveSeparator = (value: unknown) => {\n if (value === undefined) {\n return undefined;\n }\n\n if (value === '\\n' || value === '\\n\\n') {\n return value;\n }\n\n throw createAppError(400, 'INVALID_SEPARATOR', 'Separator must be \"\\\\n\" or \"\\\\n\\\\n\".');\n};\n\ntype EmitServerEvent = (event: ServerEventInput) => unknown;\n\nexport const createMcpCreateNoteHandler = (\n createNote = createNoteFromMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { title, markdown, layout } = req.body ?? {};\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'A note title is required.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n try {\n const note = await createNote({\n title,\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n });\n\n emitEvent({\n type: 'mcp.note.created',\n source: 'mcp',\n noteId: note.id,\n updatedAt: note.updatedAt,\n });\n\n res.status(200)\n .json({\n created: true,\n note,\n })\n .end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpUpdateNoteHandler = (\n updateNote = updateNoteFromMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, title, markdown, layout, editSessionId } = req.body ?? {};\n const noteId = Number(id);\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (!Number.isInteger(noteId) || noteId <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n if (title !== undefined && typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'Note title must be a string.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n if (editSessionId !== undefined && typeof editSessionId !== 'string') {\n throw createAppError(400, 'INVALID_EDIT_SESSION_ID', 'Edit session id must be a string.');\n }\n\n if (title === undefined && markdown === undefined && layout === undefined) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', 'At least one note field must be provided for update.');\n }\n\n try {\n const note = await updateNote({\n id: noteId,\n ...(title !== undefined ? { title } : {}),\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n ...(editSessionId !== undefined ? { editSessionId } : {}),\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n\n if (!note) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: note.id,\n updatedAt: note.updatedAt,\n });\n\n res.status(200)\n .json({\n updated: true,\n note,\n })\n .end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpNoteWriteBaselineHandler = (\n findNoteBaseline: (id: number) => Promise<{ id: number; updatedAt: Date } | null> = async (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n id: true,\n updatedAt: true,\n },\n }),\n): Controller => {\n return async (req, res) => {\n const noteId = resolvePositiveNoteId(req.body?.id);\n const note = await findNoteBaseline(noteId);\n\n if (!note) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n res.status(200)\n .json({\n note: serializeNoteWriteBaseline(note),\n })\n .end();\n };\n};\n\nexport const createMcpPatchNoteMarkdownHandler = (\n patchMarkdown = patchNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, selector, operation, policy } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_PATCH_INTENT', 'Patch intent must be a string.');\n }\n\n const result = await patchMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n selector: resolvePatchSelector(selector),\n operation: resolvePatchOperation(operation),\n policy: resolveMarkdownWritePolicy(policy),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpAppendNoteMarkdownHandler = (\n appendMarkdown = appendNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, insertion, placement, separator, policy } =\n req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_INTENT', 'Append intent must be a string.');\n }\n\n if (typeof insertion !== 'string') {\n throw createAppError(400, 'INVALID_APPEND_INSERTION', 'Append insertion must be a string.');\n }\n\n const result = await appendMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n insertion,\n placement: resolveAppendPlacement(placement),\n separator: resolveSeparator(separator),\n policy: resolveMarkdownWritePolicy(policy),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpReplaceNoteMarkdownHandler = (\n replaceMarkdown = replaceNoteMarkdown,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, baseMarkdownSha256, intent, replacement, policy } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedExpectedUpdatedAt = resolveOptionalString(\n expectedUpdatedAt,\n 'INVALID_NOTE_VERSION',\n 'expectedUpdatedAt must be a string.',\n );\n const resolvedBaseMarkdownSha256 = resolveOptionalString(\n baseMarkdownSha256,\n 'INVALID_MARKDOWN_HASH',\n 'baseMarkdownSha256 must be a string.',\n );\n\n if (typeof intent !== 'string') {\n throw createAppError(400, 'INVALID_REPLACE_INTENT', 'Replace intent must be a string.');\n }\n\n if (typeof replacement !== 'string') {\n throw createAppError(400, 'INVALID_REPLACE_MARKDOWN', 'Replacement markdown must be a string.');\n }\n\n const result = await replaceMarkdown({\n id: noteId,\n expectedUpdatedAt: resolvedExpectedUpdatedAt,\n baseMarkdownSha256: resolvedBaseMarkdownSha256,\n intent,\n replacement,\n policy: resolveMarkdownWritePolicy(policy),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpUpdateNoteMetadataHandler = (\n updateMetadata = updateNoteMetadata,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const { id, expectedUpdatedAt, title, layout, properties } = req.body ?? {};\n const noteId = resolvePositiveNoteId(id);\n const resolvedLayout = resolveNoteLayout(layout);\n const resolvedProperties = resolveMetadataPropertyPatch(properties);\n\n if (typeof expectedUpdatedAt !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_VERSION', 'expectedUpdatedAt must be a string.');\n }\n\n if (title !== undefined && typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'Note title must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n if (title === undefined && layout === undefined && resolvedProperties === undefined) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', 'At least one metadata field must be provided.');\n }\n\n const result = await updateMetadata({\n id: noteId,\n expectedUpdatedAt,\n ...(title !== undefined ? { title } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n ...(resolvedProperties !== undefined ? { properties: resolvedProperties } : {}),\n });\n\n if (result.status === 'applied') {\n emitEvent({\n type: 'mcp.note.updated',\n source: 'mcp',\n noteId: result.note.id,\n updatedAt: result.note.updatedAt,\n });\n }\n\n res.status(200).json(result).end();\n };\n};\n\nexport const createMcpDeleteNoteHandler = (\n deleteNote = deleteNoteById,\n emitEvent: EmitServerEvent = emitServerEvent,\n): Controller => {\n return async (req, res) => {\n const id = Number(req.body?.id);\n\n if (!Number.isInteger(id) || id <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n const deletedNote = await deleteNote(id);\n\n if (!deletedNote) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n emitEvent({\n type: 'mcp.note.deleted',\n source: 'mcp',\n noteId: deletedNote.id,\n });\n\n res.status(200)\n .json({\n deleted: true,\n note: deletedNote,\n })\n .end();\n };\n};\n"],"mappings":"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","names":[]}
@@ -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":[]}