ocean-brain 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +66 -2
  2. package/dist/mcp.js +439 -480
  3. package/package.json +2 -2
  4. package/server/client/dist/assets/{ArrowRight.es-BlNsZQFs.js → ArrowRight.es-nBJws3ws.js} +1 -1
  5. package/server/client/dist/assets/{AuxiliaryPanelHeader-HAttwVby.js → AuxiliaryPanelHeader-BN_2axQu.js} +1 -1
  6. package/server/client/dist/assets/{Calendar-DH3eSSRs.js → Calendar-vA4Xa4Qr.js} +1 -1
  7. package/server/client/dist/assets/{Callout-CcYyvHQP.js → Callout-Bo2P960P.js} +1 -1
  8. package/server/client/dist/assets/{CaretUp.es-C8iY5OlB.js → CaretUp.es-DDbqKn78.js} +1 -1
  9. package/server/client/dist/assets/{CheckCircle.es-Dqr3WImn.js → CheckCircle.es-CyY9aLBF.js} +1 -1
  10. package/server/client/dist/assets/{Copy.es-fMv0ETir.js → Copy.es-BwX4tEoU.js} +1 -1
  11. package/server/client/dist/assets/{File.es-CHtXXafR.js → File.es-DeTgvUnI.js} +1 -1
  12. package/server/client/dist/assets/{FileText.es-DPIwDCEe.js → FileText.es-C-bpw8dQ.js} +1 -1
  13. package/server/client/dist/assets/{FlowArrow.es-Dxee98ir.js → FlowArrow.es-CKcMBOmX.js} +1 -1
  14. package/server/client/dist/assets/{Graph-hVj6vAYJ.js → Graph-BNoSRJP3.js} +1 -1
  15. package/server/client/dist/assets/{Graph-CdEu8eAn.js → Graph-DYYxNAWn.js} +1 -1
  16. package/server/client/dist/assets/{Image.es-CjXY1JVf.js → Image.es-d4jWnijM.js} +1 -1
  17. package/server/client/dist/assets/{Info.es-CYxg-Vku.js → Info.es-DVzN2joA.js} +1 -1
  18. package/server/client/dist/assets/Integration-gDDtIJYJ.js +1 -0
  19. package/server/client/dist/assets/{LinkSimple.es-B-P-7Iem.js → LinkSimple.es-BD4aGTc3.js} +1 -1
  20. package/server/client/dist/assets/Note-FL31jb2U.js +19 -0
  21. package/server/client/dist/assets/{NotePropertiesPanel-6P7UfRBR.js → NotePropertiesPanel-os22Ppnu.js} +1 -1
  22. package/server/client/dist/assets/{Plus.es-Dy6onD-Z.js → Plus.es-Cl8zOOZa.js} +1 -1
  23. package/server/client/dist/assets/Reminders-Dt2JuGci.js +1 -0
  24. package/server/client/dist/assets/{Search-D9detGWk.js → Search-CPPTW2PO.js} +2 -2
  25. package/server/client/dist/assets/{SurfaceCard-D5QxOte_.js → SurfaceCard-jL2qzrzF.js} +1 -1
  26. package/server/client/dist/assets/{Tag-Bquqtx23.js → Tag-3DbGmuxa.js} +1 -1
  27. package/server/client/dist/assets/{TagNotes-CbjfxqAN.js → TagNotes-DLwRF8pT.js} +1 -1
  28. package/server/client/dist/assets/Trash.es-fxukjAO6.js +1 -0
  29. package/server/client/dist/assets/{ViewChip-BVkZAXyx.js → ViewChip-CVe_HNyd.js} +1 -1
  30. package/server/client/dist/assets/Views-8ghLRax7.js +1 -0
  31. package/server/client/dist/assets/{abnfDiagram-N423BO3Z-D44O4T7q.js → abnfDiagram-N423BO3Z-CPgGmCbn.js} +1 -1
  32. package/server/client/dist/assets/app-Bcl0U7r9.css +1 -0
  33. package/server/client/dist/assets/app-DzChh-V0.js +151 -0
  34. package/server/client/dist/assets/{arc-Cil9fXuW.js → arc-DqTgRpuF.js} +1 -1
  35. package/server/client/dist/assets/{architectureDiagram-T3A2C74G-Cjq6ayF2.js → architectureDiagram-T3A2C74G-rTYMAQ-J.js} +1 -1
  36. package/server/client/dist/assets/{blockDiagram-VBNYF7ZC-B2Xm5N0i.js → blockDiagram-VBNYF7ZC-BOluytVz.js} +1 -1
  37. package/server/client/dist/assets/{c4Diagram-5PPSVZJV-Dp0y-lCR.js → c4Diagram-5PPSVZJV-C8gbPpcb.js} +1 -1
  38. package/server/client/dist/assets/{calendar-data-wB0ifXdo.js → calendar-data-BQ24n348.js} +1 -1
  39. package/server/client/dist/assets/channel--aI9fJWX.js +1 -0
  40. package/server/client/dist/assets/{chunk-2GRJ4B5K-CWaaNkFx.js → chunk-2GRJ4B5K-CRER5Ka0.js} +1 -1
  41. package/server/client/dist/assets/{chunk-2Q5K7J3B-Ces-xz2S.js → chunk-2Q5K7J3B-hxmJWGu_.js} +1 -1
  42. package/server/client/dist/assets/{chunk-5RXB4S5H-B-4Chlfc.js → chunk-5RXB4S5H-Bpr5Us8Y.js} +1 -1
  43. package/server/client/dist/assets/{chunk-5VM5RSS4-C8_HVwuz.js → chunk-5VM5RSS4-CKUz9fAb.js} +1 -1
  44. package/server/client/dist/assets/{chunk-6Q2QTUOP-xZ8ofRqk.js → chunk-6Q2QTUOP-B8XEOHPN.js} +1 -1
  45. package/server/client/dist/assets/{chunk-GF5L2VYU-Bpa43a7s.js → chunk-GF5L2VYU-BBUD-HJi.js} +1 -1
  46. package/server/client/dist/assets/{chunk-JWPE2WC7-DsyTXbuq.js → chunk-JWPE2WC7-Dq6_B8-d.js} +1 -1
  47. package/server/client/dist/assets/{chunk-KBJHAD2P-BDhjm9-8.js → chunk-KBJHAD2P--2VZEwNJ.js} +1 -1
  48. package/server/client/dist/assets/{chunk-RYQCIY6F-Cn-jdp6I.js → chunk-RYQCIY6F-CKCwnKiC.js} +1 -1
  49. package/server/client/dist/assets/{chunk-XXDRQBXY-BKsc_0wh.js → chunk-XXDRQBXY-VDufR1Oi.js} +1 -1
  50. package/server/client/dist/assets/classDiagram-JCYQIIEL-BsbwE38V.js +1 -0
  51. package/server/client/dist/assets/classDiagram-v2-OCEON4UE-BsbwE38V.js +1 -0
  52. package/server/client/dist/assets/{cose-bilkent-JH36ORCC-jmn3Q4zj.js → cose-bilkent-JH36ORCC-D4nqOx8S.js} +1 -1
  53. package/server/client/dist/assets/{cynefinDiagram-MW4NZA55-BpLstuBJ.js → cynefinDiagram-MW4NZA55-CUuLMs-6.js} +1 -1
  54. package/server/client/dist/assets/{dagre-VZM6K2ZE-B5ft5Yzm.js → dagre-VZM6K2ZE-CUSkhZgS.js} +1 -1
  55. package/server/client/dist/assets/{diagram-7IWD3JNH-B_W9IsWZ.js → diagram-7IWD3JNH-D95eVcKx.js} +4 -4
  56. package/server/client/dist/assets/{diagram-B4RE2ZJO-32chS3GX.js → diagram-B4RE2ZJO-7OH_fb0K.js} +1 -1
  57. package/server/client/dist/assets/{diagram-LBJQPF4R-Bqupdb3-.js → diagram-LBJQPF4R-D9UwEIMd.js} +1 -1
  58. package/server/client/dist/assets/{diagram-Q27KOJAE-BD8jKBsv.js → diagram-Q27KOJAE-Dx7WFDuX.js} +1 -1
  59. package/server/client/dist/assets/{diagram-UB23O5K3-C40jAU8D.js → diagram-UB23O5K3-D2BYwAKT.js} +1 -1
  60. package/server/client/dist/assets/{ebnfDiagram-BXEA7PRR-BQfW9mFA.js → ebnfDiagram-BXEA7PRR-DbeA-fk4.js} +1 -1
  61. package/server/client/dist/assets/{erDiagram-JOGREHBK-ILZnLzz2.js → erDiagram-JOGREHBK-jWW-ICGO.js} +1 -1
  62. package/server/client/dist/assets/{flowDiagram-UKHOOZJN-CDmwr2dI.js → flowDiagram-UKHOOZJN-68iZdxxw.js} +1 -1
  63. package/server/client/dist/assets/{ganttDiagram-PKOTCBZU-DBcrUc49.js → ganttDiagram-PKOTCBZU-CxwY3VXg.js} +1 -1
  64. package/server/client/dist/assets/{gitGraphDiagram-DS77QQ5N-_xO6__FK.js → gitGraphDiagram-DS77QQ5N-Ci6o5MfZ.js} +1 -1
  65. package/server/client/dist/assets/{image.api-ChvlfuZg.js → image.api-CyIIEGkG.js} +1 -1
  66. package/server/client/dist/assets/index-Iy_BgP1R.js +1 -0
  67. package/server/client/dist/assets/{infoDiagram-6WML65LV-CuIIcu-L.js → infoDiagram-6WML65LV-DY7aMdjV.js} +1 -1
  68. package/server/client/dist/assets/integrations-LOdTs1T6.js +1 -0
  69. package/server/client/dist/assets/{ishikawaDiagram-WSZJBQD7-BVtRGTOj.js → ishikawaDiagram-WSZJBQD7-Bh5qKh1d.js} +1 -1
  70. package/server/client/dist/assets/{journeyDiagram-NVQOT4AX-D5-Nzb2N.js → journeyDiagram-NVQOT4AX-B_svXTTT.js} +1 -1
  71. package/server/client/dist/assets/{kanban-definition-27J2QSJJ-DmOhgC8H.js → kanban-definition-27J2QSJJ-CaJslHGg.js} +1 -1
  72. package/server/client/dist/assets/manage-image-DXedfr7U.js +1 -0
  73. package/server/client/dist/assets/{manage-image-detail-BIDHUR5-.js → manage-image-detail-DmifOPNU.js} +1 -1
  74. package/server/client/dist/assets/{mcp-admin-adapter-D0R8p9az.js → mcp-admin-adapter-CmKsg1vd.js} +1 -1
  75. package/server/client/dist/assets/mcp-d8IzsoRz.js +5 -0
  76. package/server/client/dist/assets/{mermaid.core-C2tL9tq7.js → mermaid.core-BfUQM5vF.js} +4 -4
  77. package/server/client/dist/assets/{mindmap-definition-FAOFIHXS-DuvsPqrh.js → mindmap-definition-FAOFIHXS-DGbkJda2.js} +1 -1
  78. package/server/client/dist/assets/{note-core-YhYEmI_R.js → note-core-DRBsBf9H.js} +9 -9
  79. package/server/client/dist/assets/{note-runtime-B3ats-dC.js → note-runtime-ztZvXRIu.js} +16 -16
  80. package/server/client/dist/assets/{note-ui-BtzPk8gA.js → note-ui-DRnAnwKv.js} +1 -1
  81. package/server/client/dist/assets/{pegDiagram-VL7TDLO6-D64crEZV.js → pegDiagram-VL7TDLO6-Bip6zQct.js} +1 -1
  82. package/server/client/dist/assets/{pieDiagram-7S7Q4E2Y--kl7vopC.js → pieDiagram-7S7Q4E2Y-CjfjGY8Y.js} +1 -1
  83. package/server/client/dist/assets/{placeholder-DKgxoe_f.js → placeholder-G6iEBbDy.js} +1 -1
  84. package/server/client/dist/assets/{properties-B7Fv95qY.js → properties-BPobx_Qk.js} +1 -1
  85. package/server/client/dist/assets/{quadrantDiagram-CIZ2JOQS-DuvJ4M2y.js → quadrantDiagram-CIZ2JOQS-B6F1yUT-.js} +1 -1
  86. package/server/client/dist/assets/{railroadDiagram-AXF67PYL-BMKmCOY7.js → railroadDiagram-AXF67PYL-D-0fLe0F.js} +1 -1
  87. package/server/client/dist/assets/{requirementDiagram-LRYGKXZP-DY1eJcy0.js → requirementDiagram-LRYGKXZP-tm4mnCyJ.js} +1 -1
  88. package/server/client/dist/assets/route-preload.js +2 -2
  89. package/server/client/dist/assets/{sankeyDiagram-W5VNT64P-C3Ka4GKM.js → sankeyDiagram-W5VNT64P-DSBWFurb.js} +1 -1
  90. package/server/client/dist/assets/{search-ddYl3qH-.js → search-DanMKCC8.js} +1 -1
  91. package/server/client/dist/assets/{search-admin-adapter-Daf7e2YQ.js → search-admin-adapter-7sTcWTmv.js} +1 -1
  92. package/server/client/dist/assets/{sequenceDiagram-SI44F4Z6-Bhpwtk2s.js → sequenceDiagram-SI44F4Z6-KrwjjJIV.js} +1 -1
  93. package/server/client/dist/assets/{sizeCapture-X5ZJPWSS-BFta71TW.js → sizeCapture-X5ZJPWSS-DL2cn_Q6.js} +1 -1
  94. package/server/client/dist/assets/{stateDiagram-OKZ733FA-D6GCn1io.js → stateDiagram-OKZ733FA-CKuNDH85.js} +1 -1
  95. package/server/client/dist/assets/{stateDiagram-v2-UEYNNEHI-g4yinGDn.js → stateDiagram-v2-UEYNNEHI-BFaj2wbM.js} +1 -1
  96. package/server/client/dist/assets/{swimlanes-SLNWSIFB-DLFvOjUl.js → swimlanes-SLNWSIFB-5S17nqhK.js} +2 -2
  97. package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-dexsHJq2.js +8 -0
  98. package/server/client/dist/assets/{tag.api-DEUROVKQ.js → tag.api-kKZl04yY.js} +1 -1
  99. package/server/client/dist/assets/{time-PxWila4-.js → time-BwwQGbK-.js} +38 -38
  100. package/server/client/dist/assets/{timeline-definition-Z64GVDOM-DTSIekSM.js → timeline-definition-Z64GVDOM-CU1hp_7d.js} +3 -3
  101. package/server/client/dist/assets/{trash-BHida7BG.js → trash-Cepvi7G8.js} +1 -1
  102. package/server/client/dist/assets/url-Bp8sXMb4.js +1 -0
  103. package/server/client/dist/assets/{useNoteMutate-BjvZUmtX.js → useNoteMutate-D-mZKXjl.js} +3 -3
  104. package/server/client/dist/assets/{useReminderMutate-C5n-5LM-.js → useReminderMutate-Cexk1ydX.js} +1 -1
  105. package/server/client/dist/assets/{vennDiagram-T6HMQDX7-CMNqSI2X.js → vennDiagram-T6HMQDX7-MoiVLnil.js} +1 -1
  106. package/server/client/dist/assets/{wardleyDiagram-T6FBY63Y-DV-JOYqB.js → wardleyDiagram-T6FBY63Y-cZkfdI6C.js} +1 -1
  107. package/server/client/dist/assets/{xychartDiagram-ELKLHX3M-BUIniF4m.js → xychartDiagram-ELKLHX3M-B63hxD-r.js} +1 -1
  108. package/server/client/dist/index.html +6 -6
  109. package/server/dist/app.js +2 -0
  110. package/server/dist/features/integration/auth.js +21 -0
  111. package/server/dist/features/integration/manifest.js +87 -0
  112. package/server/dist/features/integration/schema.js +54 -0
  113. package/server/dist/features/integration/service.js +173 -0
  114. package/server/dist/features/mcp-admin/service.js +19 -83
  115. package/server/dist/features/note/graphql/note.field.resolver.js +1 -1
  116. package/server/dist/features/note/graphql/note.query.resolver.js +6 -0
  117. package/server/dist/features/note/graphql/note.type-defs.js +13 -1
  118. package/server/dist/features/note/http/authoring.js +493 -0
  119. package/server/dist/features/note/http/mcp.js +15 -463
  120. package/server/dist/features/note/services/authoring.js +17 -1
  121. package/server/dist/features/note/services/markdown-intent-write.js +1 -0
  122. package/server/dist/features/note/services/markdown-read.js +85 -0
  123. package/server/dist/features/note/services/properties.js +36 -30
  124. package/server/dist/features/note/services/search.js +17 -0
  125. package/server/dist/features/search/graphql/resolver.js +10 -1
  126. package/server/dist/features/search/graphql/type-defs.js +3 -0
  127. package/server/dist/features/tag/graphql/tag.query.resolver.js +10 -0
  128. package/server/dist/features/tag/graphql/tag.type-defs.js +1 -0
  129. package/server/dist/features/view/graphql/mcp.query.resolver.js +22 -0
  130. package/server/dist/features/view/graphql/view.query.resolver.js +2 -0
  131. package/server/dist/features/view/graphql/view.type-defs.js +27 -0
  132. package/server/dist/features/view/services/mcp-read.js +142 -0
  133. package/server/dist/features/view/services/workspace.js +12 -4
  134. package/server/dist/modules/auth-guard.js +24 -1
  135. package/server/dist/modules/integration-token.js +27 -0
  136. package/server/dist/modules/mcp-auth.js +37 -23
  137. package/server/dist/modules/mcp-token.js +6 -25
  138. package/server/dist/routes/api.js +2 -0
  139. package/server/dist/routes/graphql.js +2 -1
  140. package/server/dist/routes/integrations.js +117 -0
  141. package/server/dist/routes/mcp.js +29 -9
  142. package/server/prisma/migrations/20260917120000_0020_plugin_installations/migration.sql +39 -0
  143. package/server/prisma/migrations/20260917150000_0021_integration_connections/migration.sql +29 -0
  144. package/server/prisma/schema.prisma +34 -10
  145. package/server/client/dist/assets/Note-CpzLtGVM.js +0 -19
  146. package/server/client/dist/assets/Reminders-DnuALBL6.js +0 -1
  147. package/server/client/dist/assets/Trash.es-XEcWG_aq.js +0 -1
  148. package/server/client/dist/assets/Views-L4eC96Lx.js +0 -1
  149. package/server/client/dist/assets/app-BFM2V_Rx.js +0 -151
  150. package/server/client/dist/assets/app-DjDwGe0t.css +0 -1
  151. package/server/client/dist/assets/channel-DZFalh-h.js +0 -1
  152. package/server/client/dist/assets/classDiagram-JCYQIIEL-CkEhFvVV.js +0 -1
  153. package/server/client/dist/assets/classDiagram-v2-OCEON4UE-CkEhFvVV.js +0 -1
  154. package/server/client/dist/assets/index-CJPl3MJE.js +0 -1
  155. package/server/client/dist/assets/manage-image-CSJ3Flgc.js +0 -1
  156. package/server/client/dist/assets/mcp-D8footel.js +0 -5
  157. package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-Ci4mcvmo.js +0 -8
  158. package/server/client/dist/assets/url-DKUMNVCI.js +0 -1
@@ -1,466 +1,18 @@
1
- import { createNoteFromMarkdown, InvalidNoteAuthoringInputError } from "../../../features/note/services/authoring.js";
2
- import { deleteNoteById } from "../../../features/note/services/cleanup.js";
3
1
  import {
4
- appendNoteMarkdown,
5
- patchNoteMarkdown,
6
- replaceNoteMarkdown,
7
- updateNoteMetadata
8
- } from "../../../features/note/services/markdown-intent-write.js";
9
- import models from "../../../models.js";
10
- import { createAppError } from "../../../modules/error-handler.js";
11
- import { emitServerEvent } from "../../../modules/server-events.js";
12
- const NOTE_LAYOUTS = /* @__PURE__ */ new Set(["narrow", "wide", "full"]);
13
- const POSITION_HINTS = /* @__PURE__ */ new Set(["first", "last"]);
14
- const PRESERVATION_POLICIES = /* @__PURE__ */ new Set([true, false, "warn"]);
15
- const isRecord = (value) => {
16
- return typeof value === "object" && value !== null;
17
- };
18
- const resolveNoteLayout = (value) => {
19
- if (value === void 0) {
20
- return void 0;
21
- }
22
- if (typeof value === "string" && NOTE_LAYOUTS.has(value)) {
23
- return value;
24
- }
25
- return null;
26
- };
27
- const resolveOptionalString = (value, code, message) => {
28
- if (value === void 0) {
29
- return void 0;
30
- }
31
- if (typeof value === "string") {
32
- return value;
33
- }
34
- throw createAppError(400, code, message);
35
- };
36
- const resolveMetadataPropertyPatch = (value) => {
37
- if (value === void 0) {
38
- return void 0;
39
- }
40
- if (!isRecord(value)) {
41
- throw createAppError(400, "INVALID_NOTE_PROPERTIES", "Properties patch must be an object.");
42
- }
43
- const set = value.set === void 0 ? void 0 : Array.isArray(value.set) ? value.set.map((item) => {
44
- if (!isRecord(item) || typeof item.key !== "string" || typeof item.value !== "string") {
45
- throw createAppError(
46
- 400,
47
- "INVALID_NOTE_PROPERTIES",
48
- "Property set items must include string key and value fields."
49
- );
50
- }
51
- return {
52
- key: item.key,
53
- value: item.value
54
- };
55
- }) : null;
56
- if (set === null) {
57
- throw createAppError(400, "INVALID_NOTE_PROPERTIES", "Property set must be an array.");
58
- }
59
- const deleteKeys = value.deleteKeys === void 0 ? void 0 : Array.isArray(value.deleteKeys) ? value.deleteKeys.map((key) => {
60
- if (typeof key !== "string") {
61
- throw createAppError(400, "INVALID_NOTE_PROPERTIES", "Property deleteKeys must be strings.");
62
- }
63
- return key;
64
- }) : null;
65
- if (deleteKeys === null) {
66
- throw createAppError(400, "INVALID_NOTE_PROPERTIES", "Property deleteKeys must be an array.");
67
- }
68
- return {
69
- ...set !== void 0 ? { set } : {},
70
- ...deleteKeys !== void 0 ? { deleteKeys } : {}
71
- };
72
- };
73
- const resolvePositiveNoteId = (value) => {
74
- const noteId = Number(value);
75
- if (!Number.isInteger(noteId) || noteId <= 0) {
76
- throw createAppError(400, "INVALID_NOTE_ID", "A valid note id is required.");
77
- }
78
- return noteId;
79
- };
80
- const serializeNoteWriteBaseline = (note) => {
81
- return {
82
- id: String(note.id),
83
- updatedAt: note.updatedAt instanceof Date ? note.updatedAt.toISOString() : note.updatedAt
84
- };
85
- };
86
- const isNonNegativeInteger = (value) => {
87
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
88
- };
89
- const isPositiveInteger = (value) => {
90
- return typeof value === "number" && Number.isInteger(value) && value > 0;
91
- };
92
- const resolveMarkdownWritePolicy = (value) => {
93
- if (value === void 0) {
94
- return void 0;
95
- }
96
- if (!isRecord(value)) {
97
- throw createAppError(400, "INVALID_MARKDOWN_POLICY", "Markdown write policy must be an object.");
98
- }
99
- const policy = {};
100
- if (value.allowNoop !== void 0) {
101
- if (typeof value.allowNoop !== "boolean") {
102
- throw createAppError(400, "INVALID_MARKDOWN_POLICY", "allowNoop must be a boolean.");
103
- }
104
- policy.allowNoop = value.allowNoop;
105
- }
106
- if (value.preserveTags !== void 0) {
107
- if (!PRESERVATION_POLICIES.has(value.preserveTags)) {
108
- throw createAppError(400, "INVALID_MARKDOWN_POLICY", "preserveTags must be boolean or warn.");
109
- }
110
- policy.preserveTags = value.preserveTags;
111
- }
112
- if (value.preserveReferences !== void 0) {
113
- if (!PRESERVATION_POLICIES.has(value.preserveReferences)) {
114
- throw createAppError(400, "INVALID_MARKDOWN_POLICY", "preserveReferences must be boolean or warn.");
115
- }
116
- policy.preserveReferences = value.preserveReferences;
117
- }
118
- return policy;
119
- };
120
- const resolvePatchSelector = (value) => {
121
- if (!isRecord(value)) {
122
- throw createAppError(400, "INVALID_PATCH_SELECTOR", "Patch selector must be an object.");
123
- }
124
- if (value.type === "exact_text") {
125
- if (typeof value.text !== "string") {
126
- throw createAppError(400, "INVALID_PATCH_SELECTOR", "Exact text selector requires text.");
127
- }
128
- return {
129
- type: "exact_text",
130
- text: value.text,
131
- ...value.before !== void 0 ? {
132
- before: resolveOptionalString(
133
- value.before,
134
- "INVALID_PATCH_SELECTOR",
135
- "Selector before must be a string."
136
- )
137
- } : {},
138
- ...value.after !== void 0 ? {
139
- after: resolveOptionalString(
140
- value.after,
141
- "INVALID_PATCH_SELECTOR",
142
- "Selector after must be a string."
143
- )
144
- } : {}
145
- };
146
- }
147
- if (value.type === "match_candidate") {
148
- const { text, matchIndex, lineStart, matchSha256, surroundingHash, positionHint } = value;
149
- if (typeof text !== "string" || !isNonNegativeInteger(matchIndex) || !isPositiveInteger(lineStart) || typeof matchSha256 !== "string" || typeof surroundingHash !== "string") {
150
- throw createAppError(400, "INVALID_PATCH_SELECTOR", "Match candidate selector has invalid fields.");
151
- }
152
- if (positionHint !== void 0 && !POSITION_HINTS.has(String(positionHint))) {
153
- throw createAppError(400, "INVALID_PATCH_SELECTOR", "positionHint must be first or last.");
154
- }
155
- return {
156
- type: "match_candidate",
157
- text,
158
- matchIndex,
159
- lineStart,
160
- matchSha256,
161
- surroundingHash,
162
- ...positionHint ? { positionHint } : {}
163
- };
164
- }
165
- throw createAppError(400, "INVALID_PATCH_SELECTOR", "Unsupported patch selector type.");
166
- };
167
- const resolvePatchOperation = (value) => {
168
- if (!isRecord(value)) {
169
- throw createAppError(400, "INVALID_PATCH_OPERATION", "Patch operation must be an object.");
170
- }
171
- if (value.type === "replace") {
172
- if (typeof value.replacement !== "string") {
173
- throw createAppError(400, "INVALID_PATCH_OPERATION", "Replace operation requires replacement.");
174
- }
175
- return {
176
- type: "replace",
177
- replacement: value.replacement
178
- };
179
- }
180
- if (value.type === "insert_before" || value.type === "insert_after") {
181
- if (typeof value.insertion !== "string") {
182
- throw createAppError(400, "INVALID_PATCH_OPERATION", "Insert operation requires insertion.");
183
- }
184
- return {
185
- type: value.type,
186
- insertion: value.insertion
187
- };
188
- }
189
- throw createAppError(400, "INVALID_PATCH_OPERATION", "Unsupported patch operation type.");
190
- };
191
- const resolveAppendPlacement = (value) => {
192
- if (value === void 0) {
193
- return void 0;
194
- }
195
- if (!isRecord(value)) {
196
- throw createAppError(400, "INVALID_APPEND_PLACEMENT", "Append placement must be an object.");
197
- }
198
- if (value.type === "end") {
199
- return { type: "end" };
200
- }
201
- if (value.type === "after_heading") {
202
- if (typeof value.heading !== "string") {
203
- throw createAppError(400, "INVALID_APPEND_PLACEMENT", "after_heading placement requires heading.");
204
- }
205
- const { heading, level } = value;
206
- if (typeof heading !== "string") {
207
- throw createAppError(400, "INVALID_APPEND_PLACEMENT", "after_heading placement requires heading.");
208
- }
209
- if (level !== void 0 && (typeof level !== "number" || !Number.isInteger(level) || level < 1 || level > 6)) {
210
- throw createAppError(400, "INVALID_APPEND_PLACEMENT", "Heading level must be an integer from 1 to 6.");
211
- }
212
- return {
213
- type: "after_heading",
214
- heading,
215
- ...level !== void 0 ? { level } : {}
216
- };
217
- }
218
- throw createAppError(400, "INVALID_APPEND_PLACEMENT", "Unsupported append placement type.");
219
- };
220
- const resolveSeparator = (value) => {
221
- if (value === void 0) {
222
- return void 0;
223
- }
224
- if (value === "\n" || value === "\n\n") {
225
- return value;
226
- }
227
- throw createAppError(400, "INVALID_SEPARATOR", 'Separator must be "\\n" or "\\n\\n".');
228
- };
229
- const createMcpCreateNoteHandler = (createNote = createNoteFromMarkdown, emitEvent = emitServerEvent) => {
230
- return async (req, reply) => {
231
- const { title, markdown, layout } = req.body ?? {};
232
- const resolvedLayout = resolveNoteLayout(layout);
233
- if (typeof title !== "string") {
234
- throw createAppError(400, "INVALID_NOTE_TITLE", "A note title is required.");
235
- }
236
- if (markdown !== void 0 && typeof markdown !== "string") {
237
- throw createAppError(400, "INVALID_NOTE_MARKDOWN", "Note markdown must be a string.");
238
- }
239
- if (layout !== void 0 && resolvedLayout === null) {
240
- throw createAppError(400, "INVALID_NOTE_LAYOUT", "Note layout must be one of narrow, wide, or full.");
241
- }
242
- try {
243
- const note = await createNote({
244
- title,
245
- ...markdown !== void 0 ? { markdown } : {},
246
- ...resolvedLayout ? { layout: resolvedLayout } : {}
247
- });
248
- emitEvent({
249
- type: "mcp.note.created",
250
- source: "mcp",
251
- noteId: note.id,
252
- updatedAt: note.updatedAt
253
- });
254
- return reply.status(200).send({
255
- created: true,
256
- note
257
- });
258
- } catch (error) {
259
- if (error instanceof InvalidNoteAuthoringInputError) {
260
- throw createAppError(400, "INVALID_NOTE_INPUT", error.message);
261
- }
262
- throw error;
263
- }
264
- };
265
- };
266
- const createMcpNoteWriteBaselineHandler = (findNoteBaseline = async (id) => models.note.findUnique({
267
- where: { id },
268
- select: {
269
- id: true,
270
- updatedAt: true
271
- }
272
- })) => {
273
- return async (req, reply) => {
274
- const noteId = resolvePositiveNoteId(req.body?.id);
275
- const note = await findNoteBaseline(noteId);
276
- if (!note) {
277
- throw createAppError(404, "NOTE_NOT_FOUND", "The requested note was not found.");
278
- }
279
- return reply.status(200).send({
280
- note: serializeNoteWriteBaseline(note)
281
- });
282
- };
283
- };
284
- const createMcpPatchNoteMarkdownHandler = (patchMarkdown = patchNoteMarkdown, emitEvent = emitServerEvent) => {
285
- return async (req, reply) => {
286
- const { id, expectedUpdatedAt, baseMarkdownSha256, intent, selector, operation, policy } = req.body ?? {};
287
- const noteId = resolvePositiveNoteId(id);
288
- const resolvedExpectedUpdatedAt = resolveOptionalString(
289
- expectedUpdatedAt,
290
- "INVALID_NOTE_VERSION",
291
- "expectedUpdatedAt must be a string."
292
- );
293
- const resolvedBaseMarkdownSha256 = resolveOptionalString(
294
- baseMarkdownSha256,
295
- "INVALID_MARKDOWN_HASH",
296
- "baseMarkdownSha256 must be a string."
297
- );
298
- if (typeof intent !== "string") {
299
- throw createAppError(400, "INVALID_PATCH_INTENT", "Patch intent must be a string.");
300
- }
301
- const result = await patchMarkdown({
302
- id: noteId,
303
- expectedUpdatedAt: resolvedExpectedUpdatedAt,
304
- baseMarkdownSha256: resolvedBaseMarkdownSha256,
305
- intent,
306
- selector: resolvePatchSelector(selector),
307
- operation: resolvePatchOperation(operation),
308
- policy: resolveMarkdownWritePolicy(policy)
309
- });
310
- if (result.status === "applied") {
311
- emitEvent({
312
- type: "mcp.note.updated",
313
- source: "mcp",
314
- noteId: result.note.id,
315
- updatedAt: result.note.updatedAt
316
- });
317
- }
318
- return reply.status(200).send(result);
319
- };
320
- };
321
- const createMcpAppendNoteMarkdownHandler = (appendMarkdown = appendNoteMarkdown, emitEvent = emitServerEvent) => {
322
- return async (req, reply) => {
323
- const { id, expectedUpdatedAt, baseMarkdownSha256, intent, insertion, placement, separator, policy } = req.body ?? {};
324
- const noteId = resolvePositiveNoteId(id);
325
- const resolvedExpectedUpdatedAt = resolveOptionalString(
326
- expectedUpdatedAt,
327
- "INVALID_NOTE_VERSION",
328
- "expectedUpdatedAt must be a string."
329
- );
330
- const resolvedBaseMarkdownSha256 = resolveOptionalString(
331
- baseMarkdownSha256,
332
- "INVALID_MARKDOWN_HASH",
333
- "baseMarkdownSha256 must be a string."
334
- );
335
- if (typeof intent !== "string") {
336
- throw createAppError(400, "INVALID_APPEND_INTENT", "Append intent must be a string.");
337
- }
338
- if (typeof insertion !== "string") {
339
- throw createAppError(400, "INVALID_APPEND_INSERTION", "Append insertion must be a string.");
340
- }
341
- const result = await appendMarkdown({
342
- id: noteId,
343
- expectedUpdatedAt: resolvedExpectedUpdatedAt,
344
- baseMarkdownSha256: resolvedBaseMarkdownSha256,
345
- intent,
346
- insertion,
347
- placement: resolveAppendPlacement(placement),
348
- separator: resolveSeparator(separator),
349
- policy: resolveMarkdownWritePolicy(policy)
350
- });
351
- if (result.status === "applied") {
352
- emitEvent({
353
- type: "mcp.note.updated",
354
- source: "mcp",
355
- noteId: result.note.id,
356
- updatedAt: result.note.updatedAt
357
- });
358
- }
359
- return reply.status(200).send(result);
360
- };
361
- };
362
- const createMcpReplaceNoteMarkdownHandler = (replaceMarkdown = replaceNoteMarkdown, emitEvent = emitServerEvent) => {
363
- return async (req, reply) => {
364
- const { id, expectedUpdatedAt, baseMarkdownSha256, intent, replacement, policy } = req.body ?? {};
365
- const noteId = resolvePositiveNoteId(id);
366
- const resolvedExpectedUpdatedAt = resolveOptionalString(
367
- expectedUpdatedAt,
368
- "INVALID_NOTE_VERSION",
369
- "expectedUpdatedAt must be a string."
370
- );
371
- const resolvedBaseMarkdownSha256 = resolveOptionalString(
372
- baseMarkdownSha256,
373
- "INVALID_MARKDOWN_HASH",
374
- "baseMarkdownSha256 must be a string."
375
- );
376
- if (typeof intent !== "string") {
377
- throw createAppError(400, "INVALID_REPLACE_INTENT", "Replace intent must be a string.");
378
- }
379
- if (typeof replacement !== "string") {
380
- throw createAppError(400, "INVALID_REPLACE_MARKDOWN", "Replacement markdown must be a string.");
381
- }
382
- const result = await replaceMarkdown({
383
- id: noteId,
384
- expectedUpdatedAt: resolvedExpectedUpdatedAt,
385
- baseMarkdownSha256: resolvedBaseMarkdownSha256,
386
- intent,
387
- replacement,
388
- policy: resolveMarkdownWritePolicy(policy)
389
- });
390
- if (result.status === "applied") {
391
- emitEvent({
392
- type: "mcp.note.updated",
393
- source: "mcp",
394
- noteId: result.note.id,
395
- updatedAt: result.note.updatedAt
396
- });
397
- }
398
- return reply.status(200).send(result);
399
- };
400
- };
401
- const createMcpUpdateNoteMetadataHandler = (updateMetadata = updateNoteMetadata, emitEvent = emitServerEvent) => {
402
- return async (req, reply) => {
403
- const { id, expectedUpdatedAt, title, layout, properties } = req.body ?? {};
404
- const noteId = resolvePositiveNoteId(id);
405
- const resolvedLayout = resolveNoteLayout(layout);
406
- const resolvedProperties = resolveMetadataPropertyPatch(properties);
407
- if (typeof expectedUpdatedAt !== "string") {
408
- throw createAppError(400, "INVALID_NOTE_VERSION", "expectedUpdatedAt must be a string.");
409
- }
410
- if (title !== void 0 && typeof title !== "string") {
411
- throw createAppError(400, "INVALID_NOTE_TITLE", "Note title must be a string.");
412
- }
413
- if (layout !== void 0 && resolvedLayout === null) {
414
- throw createAppError(400, "INVALID_NOTE_LAYOUT", "Note layout must be one of narrow, wide, or full.");
415
- }
416
- if (title === void 0 && layout === void 0 && resolvedProperties === void 0) {
417
- throw createAppError(400, "INVALID_NOTE_INPUT", "At least one metadata field must be provided.");
418
- }
419
- const result = await updateMetadata({
420
- id: noteId,
421
- expectedUpdatedAt,
422
- ...title !== void 0 ? { title } : {},
423
- ...resolvedLayout ? { layout: resolvedLayout } : {},
424
- ...resolvedProperties !== void 0 ? { properties: resolvedProperties } : {}
425
- });
426
- if (result.status === "applied") {
427
- emitEvent({
428
- type: "mcp.note.updated",
429
- source: "mcp",
430
- noteId: result.note.id,
431
- updatedAt: result.note.updatedAt
432
- });
433
- }
434
- return reply.status(200).send(result);
435
- };
436
- };
437
- const createMcpDeleteNoteHandler = (deleteNote = deleteNoteById, emitEvent = emitServerEvent) => {
438
- return async (req, reply) => {
439
- const id = Number(req.body?.id);
440
- if (!Number.isInteger(id) || id <= 0) {
441
- throw createAppError(400, "INVALID_NOTE_ID", "A valid note id is required.");
442
- }
443
- const deletedNote = await deleteNote(id);
444
- if (!deletedNote) {
445
- throw createAppError(404, "NOTE_NOT_FOUND", "The requested note was not found.");
446
- }
447
- emitEvent({
448
- type: "mcp.note.deleted",
449
- source: "mcp",
450
- noteId: deletedNote.id
451
- });
452
- return reply.status(200).send({
453
- deleted: true,
454
- note: deletedNote
455
- });
456
- };
457
- };
2
+ createAppendNoteMarkdownHandler,
3
+ createCreateNoteHandler,
4
+ createDeleteNoteHandler,
5
+ createNoteWriteBaselineHandler,
6
+ createPatchNoteMarkdownHandler,
7
+ createReplaceNoteMarkdownHandler,
8
+ createUpdateNoteMetadataHandler
9
+ } from "./authoring.js";
458
10
  export {
459
- createMcpAppendNoteMarkdownHandler,
460
- createMcpCreateNoteHandler,
461
- createMcpDeleteNoteHandler,
462
- createMcpNoteWriteBaselineHandler,
463
- createMcpPatchNoteMarkdownHandler,
464
- createMcpReplaceNoteMarkdownHandler,
465
- createMcpUpdateNoteMetadataHandler
11
+ createAppendNoteMarkdownHandler as createMcpAppendNoteMarkdownHandler,
12
+ createCreateNoteHandler as createMcpCreateNoteHandler,
13
+ createDeleteNoteHandler as createMcpDeleteNoteHandler,
14
+ createNoteWriteBaselineHandler as createMcpNoteWriteBaselineHandler,
15
+ createPatchNoteMarkdownHandler as createMcpPatchNoteMarkdownHandler,
16
+ createReplaceNoteMarkdownHandler as createMcpReplaceNoteMarkdownHandler,
17
+ createUpdateNoteMetadataHandler as createMcpUpdateNoteMetadataHandler
466
18
  };
@@ -1,6 +1,12 @@
1
1
  import models from "../../../models.js";
2
2
  import { extractTagIdsFromContentJson, markdownToBlocksJson } from "../../../modules/blocknote.js";
3
3
  import { replaceNoteReferences } from "./note-reference-index.js";
4
+ import {
5
+ resolveNotePropertiesPatchValueTypes,
6
+ serializeNoteProperties,
7
+ setNotePropertyValues,
8
+ validateNotePropertiesPatchValues
9
+ } from "./properties.js";
4
10
  import { buildNoteSearchProjection } from "./search.js";
5
11
  class InvalidNoteAuthoringInputError extends Error {
6
12
  constructor(message) {
@@ -11,6 +17,7 @@ class InvalidNoteAuthoringInputError extends Error {
11
17
  const PLACEHOLDER_PREFIX = "{%";
12
18
  const PLACEHOLDER_SUFFIX = "%}";
13
19
  const serializeNote = (note) => ({
20
+ ...note.properties ? { properties: note.properties } : {},
14
21
  id: String(note.id),
15
22
  title: note.title,
16
23
  layout: note.layout,
@@ -49,6 +56,7 @@ const createNoteAuthoringService = (deps) => {
49
56
  if (!title) {
50
57
  throw new InvalidNoteAuthoringInputError("A note title is required.");
51
58
  }
59
+ const properties = input.properties ? await deps.validateProperties(input.properties) : void 0;
52
60
  const replacedTitle = await replacePlaceholders(title);
53
61
  const replacedMarkdown = await replacePlaceholders(input.markdown ?? "");
54
62
  const content = await deps.parseMarkdownToContentJson(replacedMarkdown);
@@ -57,6 +65,7 @@ const createNoteAuthoringService = (deps) => {
57
65
  title: replacedTitle,
58
66
  content,
59
67
  tagIds,
68
+ ...properties ? { properties } : {},
60
69
  ...input.layout ? { layout: input.layout } : {}
61
70
  });
62
71
  return serializeNote(note);
@@ -64,6 +73,7 @@ const createNoteAuthoringService = (deps) => {
64
73
  };
65
74
  };
66
75
  const defaultNoteAuthoringService = createNoteAuthoringService({
76
+ validateProperties: async (patch) => validateNotePropertiesPatchValues(await resolveNotePropertiesPatchValueTypes(patch)),
67
77
  createNote: async (input) => {
68
78
  return models.$transaction(async (tx) => {
69
79
  const note = await tx.note.create({
@@ -78,8 +88,14 @@ const defaultNoteAuthoringService = createNoteAuthoringService({
78
88
  ...input.tagIds ? { tags: { connect: input.tagIds.map((id) => ({ id: Number(id) })) } } : {}
79
89
  }
80
90
  });
91
+ if (input.properties) await setNotePropertyValues(tx, note.id, input.properties);
81
92
  await replaceNoteReferences(tx, note.id, input.content);
82
- return note;
93
+ const properties = await tx.noteProperty.findMany({
94
+ where: { noteId: note.id },
95
+ include: { definition: true, option: true },
96
+ orderBy: { definition: { key: "asc" } }
97
+ });
98
+ return { ...note, properties: serializeNoteProperties(properties) };
83
99
  });
84
100
  },
85
101
  findPlaceholders: async (templates) => {
@@ -167,6 +167,7 @@ const applyMarkdownPlan = async (deps, input) => {
167
167
  }
168
168
  return {
169
169
  status: "applied",
170
+ warnings: input.plan.warnings,
170
171
  note: {
171
172
  id: String(updateResult.note.id),
172
173
  updatedAt: updateResult.note.updatedAt.toISOString()
@@ -0,0 +1,85 @@
1
+ const boundary = (text, index) => index > 0 && /[\uDC00-\uDFFF]/.test(text[index] ?? "") && /[\uD800-\uDBFF]/.test(text[index - 1]) ? index - 1 : index;
2
+ const headingsIn = (markdown) => {
3
+ const headings = [];
4
+ let fence = null;
5
+ for (const line of markdown.matchAll(/^.*(?:\n|$)/gm)) {
6
+ if (!line[0]) continue;
7
+ const fenceMatch = /^ {0,3}(`{3,}|~{3,})/.exec(line[0]);
8
+ if (fenceMatch) {
9
+ const marker = fenceMatch[1];
10
+ if (!fence) fence = { character: marker[0], length: marker.length };
11
+ else if (marker[0] === fence.character && marker.length >= fence.length && line[0].slice(fenceMatch[0].length).trim() === "")
12
+ fence = null;
13
+ continue;
14
+ }
15
+ if (fence) continue;
16
+ const heading = /^ {0,3}(#{1,6})[ \t]+(.+?)[ \t]*(?:\r?\n)?$/.exec(line[0]);
17
+ if (heading)
18
+ headings.push({
19
+ heading: heading[2].replace(/[ \t]+#+[ \t]*$/, "").trim(),
20
+ level: heading[1].length,
21
+ start: line.index,
22
+ end: markdown.length
23
+ });
24
+ }
25
+ return headings.map((heading, index) => ({
26
+ ...heading,
27
+ end: headings.slice(index + 1).find((next) => next.level <= heading.level)?.start ?? markdown.length
28
+ }));
29
+ };
30
+ const readMarkdownRange = (markdown, updatedAt, input) => {
31
+ const maxLength = input.maxLength ?? 1e3;
32
+ const offset = input.offset ?? 0;
33
+ if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(maxLength) || maxLength < 0) {
34
+ throw new Error("offset and maxLength must be nonnegative integers.");
35
+ }
36
+ if (input.heading !== void 0 && input.offset !== void 0)
37
+ throw new Error("Use either heading or offset, not both.");
38
+ const empty = { markdown: "", contentRange: null, candidates: [] };
39
+ if (input.expectedUpdatedAt !== void 0) {
40
+ const expected = /^\d+$/.test(input.expectedUpdatedAt) ? Number(input.expectedUpdatedAt) : Date.parse(input.expectedUpdatedAt);
41
+ if (expected !== updatedAt.getTime())
42
+ return {
43
+ ...empty,
44
+ status: "failed",
45
+ reason: "NOTE_VERSION_CONFLICT",
46
+ message: "The note changed. Start a new read using its current updatedAt."
47
+ };
48
+ }
49
+ let start = boundary(markdown, Math.min(offset, markdown.length));
50
+ let sectionEnd = markdown.length;
51
+ if (input.heading !== void 0) {
52
+ const candidates = headingsIn(markdown).filter((heading) => heading.heading === input.heading);
53
+ if (candidates.length !== 1)
54
+ return {
55
+ ...empty,
56
+ status: candidates.length ? "needs_disambiguation" : "failed",
57
+ reason: candidates.length ? "HEADING_AMBIGUOUS" : "HEADING_NOT_FOUND",
58
+ message: candidates.length ? "Use a candidate start as offset to select a heading." : "Exact heading text was not found.",
59
+ candidates
60
+ };
61
+ start = candidates[0].start;
62
+ sectionEnd = candidates[0].end;
63
+ }
64
+ let end = maxLength === 0 ? sectionEnd : boundary(markdown, Math.min(start + maxLength, sectionEnd));
65
+ if (end === start && start < sectionEnd) end = Math.min(start + 2, sectionEnd);
66
+ const hasMore = end < sectionEnd;
67
+ return {
68
+ status: "read",
69
+ reason: null,
70
+ message: null,
71
+ candidates: [],
72
+ markdown: markdown.slice(start, end),
73
+ contentRange: {
74
+ start,
75
+ end,
76
+ totalLength: markdown.length,
77
+ sectionEnd,
78
+ hasMore,
79
+ nextOffset: hasMore ? end : null
80
+ }
81
+ };
82
+ };
83
+ export {
84
+ readMarkdownRange
85
+ };