ocean-brain 0.4.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp.js +660 -31
- package/package.json +2 -1
- package/server/client/dist/assets/{Calendar-DI0PhQTq.js → Calendar-BytW4PeT.js} +1 -1
- package/server/client/dist/assets/Callout-9sJgGNRb.js +1 -0
- package/server/client/dist/assets/Graph-o5u-NzrA.js +1 -0
- package/server/client/dist/assets/{Image-DlJ4MDo3.js → Image-DxWhlIDG.js} +1 -1
- package/server/client/dist/assets/{Image.es-COwqkQuh.js → Image.es-B0LL3pb7.js} +1 -1
- package/server/client/dist/assets/Note-CYoYzZNu.js +21 -0
- package/server/client/dist/assets/{Plus.es-KeV96R_e.js → Plus.es-DpxzgRtY.js} +1 -1
- package/server/client/dist/assets/Reminders-DAepTZg1.js +1 -0
- package/server/client/dist/assets/Search-6KH7An44.js +1 -0
- package/server/client/dist/assets/{SurfaceCard-CHjcCuqZ.js → SurfaceCard-EMs50xSF.js} +1 -1
- package/server/client/dist/assets/Tag-C89j8x56.js +1 -0
- package/server/client/dist/assets/TagNotes-BTbu-f6v.js +1 -0
- package/server/client/dist/assets/{Trash.es-BqRTZPrI.js → Trash.es-C5x35NKF.js} +1 -1
- package/server/client/dist/assets/ViewNotes-DPEuki3y.js +1 -0
- package/server/client/dist/assets/Views-BGkuNhVd.js +1 -0
- package/server/client/dist/assets/{image.api-CMdQbCyN.js → image.api-DRhRQ0Gf.js} +1 -1
- package/server/client/dist/assets/{index-D2gRB1ig.css → index-Cd81UxEh.css} +1 -1
- package/server/client/dist/assets/index-DYlYUrjH.js +327 -0
- package/server/client/dist/assets/index-DswSFFMb.js +1 -0
- package/server/client/dist/assets/manage-image-C6sHhWnI.js +1 -0
- package/server/client/dist/assets/manage-image-detail-CJkfybR9.js +1 -0
- package/server/client/dist/assets/{mcp-DqGLZv90.js → mcp-piouFDNm.js} +1 -1
- package/server/client/dist/assets/note-core-Z6kqulGB.js +180 -0
- package/server/client/dist/assets/note-vendor-i1M2FSI2.js +54 -0
- package/server/client/dist/assets/{placeholder-BXLtvdtN.js → placeholder-DGz3CHej.js} +4 -4
- package/server/client/dist/assets/properties-CM4khczg.js +1 -0
- package/server/client/dist/assets/trash-C2lItz-b.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-v1EXqM90.js → useReminderMutate-DHjDDHKg.js} +1 -1
- package/server/client/dist/assets/view-dashboard-C17WsdL7.js +99 -0
- package/server/client/dist/index.html +4 -4
- package/server/dist/features/note/graphql/note.field.resolver.js +16 -1
- package/server/dist/features/note/graphql/note.field.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js +192 -36
- package/server/dist/features/note/graphql/note.mutation.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.query.resolver.js +88 -76
- package/server/dist/features/note/graphql/note.query.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.resolver.js +3 -2
- package/server/dist/features/note/graphql/note.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.type-defs.js +104 -1
- package/server/dist/features/note/graphql/note.type-defs.js.map +1 -1
- package/server/dist/features/note/http/mcp.js +394 -1
- package/server/dist/features/note/http/mcp.js.map +1 -1
- package/server/dist/features/note/services/cleanup.js +4 -3
- package/server/dist/features/note/services/cleanup.js.map +1 -1
- package/server/dist/features/note/services/content-blocks.js +113 -0
- package/server/dist/features/note/services/content-blocks.js.map +1 -0
- package/server/dist/features/note/services/markdown-intent-write.js +478 -0
- package/server/dist/features/note/services/markdown-intent-write.js.map +1 -0
- package/server/dist/features/note/services/markdown-patch.js +601 -0
- package/server/dist/features/note/services/markdown-patch.js.map +1 -0
- package/server/dist/features/note/services/properties.js +874 -0
- package/server/dist/features/note/services/properties.js.map +1 -0
- package/server/dist/features/note/services/search.js +10 -29
- package/server/dist/features/note/services/search.js.map +1 -1
- package/server/dist/features/note/services/snapshot.js +260 -18
- package/server/dist/features/note/services/snapshot.js.map +1 -1
- package/server/dist/features/note/services/trash.js +70 -2
- package/server/dist/features/note/services/trash.js.map +1 -1
- package/server/dist/features/note/services/write-conflict.js +87 -0
- package/server/dist/features/note/services/write-conflict.js.map +1 -0
- package/server/dist/features/note/services/write.js +121 -0
- package/server/dist/features/note/services/write.js.map +1 -0
- package/server/dist/features/view/graphql/view.query.resolver.js +31 -1
- package/server/dist/features/view/graphql/view.query.resolver.js.map +1 -1
- package/server/dist/features/view/graphql/view.type-defs.js +57 -1
- package/server/dist/features/view/graphql/view.type-defs.js.map +1 -1
- package/server/dist/features/view/services/workspace.js +367 -43
- package/server/dist/features/view/services/workspace.js.map +1 -1
- package/server/dist/modules/blocknote-tree.js +41 -0
- package/server/dist/modules/blocknote-tree.js.map +1 -0
- package/server/dist/modules/blocknote.js +47 -2
- package/server/dist/modules/blocknote.js.map +1 -1
- package/server/dist/modules/error-handler.js +7 -4
- package/server/dist/modules/error-handler.js.map +1 -1
- package/server/dist/modules/recovery-retention.js +2 -2
- package/server/dist/modules/recovery-retention.js.map +1 -1
- package/server/dist/routes/mcp.js +21 -1
- package/server/dist/routes/mcp.js.map +1 -1
- package/server/prisma/migrations/20260527073000_0017_note_properties/migration.sql +105 -0
- package/server/prisma/migrations/20260531073000_0018_view_query/migration.sql +3 -0
- package/server/prisma/schema.prisma +226 -130
- package/server/client/dist/assets/Callout-BAqbXI0L.js +0 -1
- package/server/client/dist/assets/Graph-Dhuog4tc.js +0 -1
- package/server/client/dist/assets/Note-ChUWU3Su.js +0 -21
- package/server/client/dist/assets/Reminders-D7MCTNxb.js +0 -1
- package/server/client/dist/assets/Search-B-fwTr5y.js +0 -1
- package/server/client/dist/assets/Tag-BbV2z-Db.js +0 -1
- package/server/client/dist/assets/TagNotes-2HKF09Jm.js +0 -1
- package/server/client/dist/assets/ViewNotes-TfM3Uzbp.js +0 -1
- package/server/client/dist/assets/Views-CUwtnfa8.js +0 -1
- package/server/client/dist/assets/index-B4l2UvuC.js +0 -232
- package/server/client/dist/assets/index-zhxZjg7V.js +0 -1
- package/server/client/dist/assets/manage-image-DdXb5Bkj.js +0 -1
- package/server/client/dist/assets/manage-image-detail-CrrXCQIX.js +0 -1
- package/server/client/dist/assets/note-core-GgFcksAF.js +0 -178
- package/server/client/dist/assets/note-vendor-B0t5F04o.js +0 -54
- package/server/client/dist/assets/trash-3eUAjFRh.js +0 -1
- package/server/client/dist/assets/view-dashboard-T6gmohr-.js +0 -90
- package/server/dist/features/note/graphql/note.graphql.shared.js +0 -23
- package/server/dist/features/note/graphql/note.graphql.shared.js.map +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { parseBlockNoteContent, walkBlockNoteTree } from "../../../modules/blocknote-tree.js";
|
|
2
|
+
const parseNoteContent = (content) => {
|
|
3
|
+
return parseBlockNoteContent(content);
|
|
4
|
+
};
|
|
5
|
+
const normalizeReferenceId = (id) => {
|
|
6
|
+
if (typeof id === "number") {
|
|
7
|
+
return Number.isFinite(id) ? String(id) : null;
|
|
8
|
+
}
|
|
9
|
+
if (typeof id !== "string") {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const normalizedId = id.trim();
|
|
13
|
+
return normalizedId.length > 0 ? normalizedId : null;
|
|
14
|
+
};
|
|
15
|
+
const extractBlocksByType = (type, dataArray) => {
|
|
16
|
+
const result = [];
|
|
17
|
+
walkBlockNoteTree(dataArray, (node) => {
|
|
18
|
+
if (node.type === type) {
|
|
19
|
+
result.push(node);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
const extractReferenceBlocks = (dataArray) => {
|
|
25
|
+
return extractBlocksByType("reference", dataArray).filter((block) => {
|
|
26
|
+
return normalizeReferenceId(block.props?.id) !== null;
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const extractReferenceBlocksFromContent = (content) => {
|
|
30
|
+
const parsed = parseNoteContent(content);
|
|
31
|
+
return parsed ? extractReferenceBlocks(parsed) : [];
|
|
32
|
+
};
|
|
33
|
+
const contentReferencesNote = (content, noteId) => {
|
|
34
|
+
const targetId = normalizeReferenceId(noteId);
|
|
35
|
+
if (!targetId) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return extractReferenceBlocksFromContent(content).some(
|
|
39
|
+
(block) => normalizeReferenceId(block.props?.id) === targetId
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
const syncReferenceTitlesInContent = (content, titlesById) => {
|
|
43
|
+
const parsed = parseNoteContent(content);
|
|
44
|
+
if (!parsed) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
let changed = false;
|
|
48
|
+
for (const block of extractReferenceBlocks(parsed)) {
|
|
49
|
+
const props = block.props;
|
|
50
|
+
const id = normalizeReferenceId(props?.id);
|
|
51
|
+
if (!props || id === null) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const title = titlesById.get(id);
|
|
55
|
+
if (title !== void 0 && props.title !== title) {
|
|
56
|
+
block.props = {
|
|
57
|
+
...props,
|
|
58
|
+
title
|
|
59
|
+
};
|
|
60
|
+
changed = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return changed ? JSON.stringify(parsed) : content;
|
|
64
|
+
};
|
|
65
|
+
const buildNoteGraph = (notes) => {
|
|
66
|
+
const existingNoteIds = new Set(notes.map((note) => String(note.id)));
|
|
67
|
+
const links = [];
|
|
68
|
+
const connectionCount = {};
|
|
69
|
+
const linkSet = /* @__PURE__ */ new Set();
|
|
70
|
+
for (const note of notes) {
|
|
71
|
+
const sourceId = String(note.id);
|
|
72
|
+
for (const block of extractReferenceBlocksFromContent(note.content)) {
|
|
73
|
+
const targetId = normalizeReferenceId(block.props?.id);
|
|
74
|
+
if (!targetId || sourceId === targetId || !existingNoteIds.has(targetId)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const linkKey = `${sourceId}-${targetId}`;
|
|
78
|
+
const reverseLinkKey = `${targetId}-${sourceId}`;
|
|
79
|
+
if (linkSet.has(linkKey) || linkSet.has(reverseLinkKey)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
linkSet.add(linkKey);
|
|
83
|
+
links.push({
|
|
84
|
+
source: sourceId,
|
|
85
|
+
target: targetId
|
|
86
|
+
});
|
|
87
|
+
connectionCount[sourceId] = (connectionCount[sourceId] || 0) + 1;
|
|
88
|
+
connectionCount[targetId] = (connectionCount[targetId] || 0) + 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
nodes: notes.map((note) => {
|
|
93
|
+
const id = String(note.id);
|
|
94
|
+
return {
|
|
95
|
+
id,
|
|
96
|
+
title: note.title || "Untitled",
|
|
97
|
+
connections: connectionCount[id] || 0
|
|
98
|
+
};
|
|
99
|
+
}),
|
|
100
|
+
links
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
export {
|
|
104
|
+
buildNoteGraph,
|
|
105
|
+
contentReferencesNote,
|
|
106
|
+
extractBlocksByType,
|
|
107
|
+
extractReferenceBlocks,
|
|
108
|
+
extractReferenceBlocksFromContent,
|
|
109
|
+
normalizeReferenceId,
|
|
110
|
+
parseNoteContent,
|
|
111
|
+
syncReferenceTitlesInContent
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=content-blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/note/services/content-blocks.ts"],"sourcesContent":["import { parseBlockNoteContent, walkBlockNoteTree } from '~/modules/blocknote-tree.js';\n\ninterface BlockNote<T = unknown> {\n id?: string;\n type?: string;\n props?: T;\n content?: unknown;\n children?: unknown;\n}\n\ninterface ReferenceProps {\n id?: string | number;\n title?: string;\n}\n\ninterface NoteGraphInput {\n id: string | number;\n title: string;\n content: string;\n}\n\nexport interface NoteGraphResult {\n nodes: Array<{ id: string; title: string; connections: number }>;\n links: Array<{ source: string; target: string }>;\n}\n\nexport const parseNoteContent = (content: string): unknown[] | null => {\n return parseBlockNoteContent(content);\n};\n\nexport const normalizeReferenceId = (id: unknown) => {\n if (typeof id === 'number') {\n return Number.isFinite(id) ? String(id) : null;\n }\n\n if (typeof id !== 'string') {\n return null;\n }\n\n const normalizedId = id.trim();\n\n return normalizedId.length > 0 ? normalizedId : null;\n};\n\nexport const extractBlocksByType = <T>(type: string, dataArray: unknown): BlockNote<T>[] => {\n const result: BlockNote<T>[] = [];\n\n walkBlockNoteTree(dataArray, (node) => {\n if (node.type === type) {\n result.push(node as BlockNote<T>);\n }\n });\n\n return result;\n};\n\nexport const extractReferenceBlocks = (dataArray: unknown) => {\n return extractBlocksByType<ReferenceProps>('reference', dataArray).filter((block) => {\n return normalizeReferenceId(block.props?.id) !== null;\n });\n};\n\nexport const extractReferenceBlocksFromContent = (content: string) => {\n const parsed = parseNoteContent(content);\n return parsed ? extractReferenceBlocks(parsed) : [];\n};\n\nexport const contentReferencesNote = (content: string, noteId: string | number) => {\n const targetId = normalizeReferenceId(noteId);\n\n if (!targetId) {\n return false;\n }\n\n return extractReferenceBlocksFromContent(content).some(\n (block) => normalizeReferenceId(block.props?.id) === targetId,\n );\n};\n\nexport const syncReferenceTitlesInContent = (content: string, titlesById: Map<string, string>) => {\n const parsed = parseNoteContent(content);\n\n if (!parsed) {\n return null;\n }\n\n let changed = false;\n\n for (const block of extractReferenceBlocks(parsed)) {\n const props = block.props;\n const id = normalizeReferenceId(props?.id);\n\n if (!props || id === null) {\n continue;\n }\n\n const title = titlesById.get(id);\n\n if (title !== undefined && props.title !== title) {\n block.props = {\n ...props,\n title,\n };\n changed = true;\n }\n }\n\n return changed ? JSON.stringify(parsed) : content;\n};\n\nexport const buildNoteGraph = (notes: NoteGraphInput[]): NoteGraphResult => {\n const existingNoteIds = new Set(notes.map((note) => String(note.id)));\n const links: NoteGraphResult['links'] = [];\n const connectionCount: Record<string, number> = {};\n const linkSet = new Set<string>();\n\n for (const note of notes) {\n const sourceId = String(note.id);\n\n for (const block of extractReferenceBlocksFromContent(note.content)) {\n const targetId = normalizeReferenceId(block.props?.id);\n\n if (!targetId || sourceId === targetId || !existingNoteIds.has(targetId)) {\n continue;\n }\n\n const linkKey = `${sourceId}-${targetId}`;\n const reverseLinkKey = `${targetId}-${sourceId}`;\n\n if (linkSet.has(linkKey) || linkSet.has(reverseLinkKey)) {\n continue;\n }\n\n linkSet.add(linkKey);\n links.push({\n source: sourceId,\n target: targetId,\n });\n connectionCount[sourceId] = (connectionCount[sourceId] || 0) + 1;\n connectionCount[targetId] = (connectionCount[targetId] || 0) + 1;\n }\n }\n\n return {\n nodes: notes.map((note) => {\n const id = String(note.id);\n\n return {\n id,\n title: note.title || 'Untitled',\n connections: connectionCount[id] || 0,\n };\n }),\n links,\n };\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import models from "../../../models.js";
|
|
2
|
+
import {
|
|
3
|
+
blocksToMarkdown,
|
|
4
|
+
countReferenceInlinesFromContentJson,
|
|
5
|
+
extractTagIdsFromContentJson,
|
|
6
|
+
hasUnsupportedMarkdownBlocks,
|
|
7
|
+
markdownToBlocksJson
|
|
8
|
+
} from "../../../modules/blocknote.js";
|
|
9
|
+
import {
|
|
10
|
+
buildMarkdownAppendPlan,
|
|
11
|
+
buildMarkdownPatchPlan,
|
|
12
|
+
buildMarkdownReplacePlan,
|
|
13
|
+
calculateMarkdownSha256
|
|
14
|
+
} from "./markdown-patch.js";
|
|
15
|
+
import {
|
|
16
|
+
InvalidNotePropertyInputError,
|
|
17
|
+
resolveNotePropertiesPatchValueTypes,
|
|
18
|
+
updateNotePropertiesWithVersionGuardAndSnapshot,
|
|
19
|
+
validateNotePropertiesPatchValues
|
|
20
|
+
} from "./properties.js";
|
|
21
|
+
import { MCP_SNAPSHOT_META } from "./snapshot.js";
|
|
22
|
+
import { updateNoteWithVersionGuardAndSnapshot } from "./write.js";
|
|
23
|
+
import {
|
|
24
|
+
isInvalidNoteVersionError,
|
|
25
|
+
isMissingNoteVersionError,
|
|
26
|
+
isNoteVersionConflictError,
|
|
27
|
+
parseNoteVersion
|
|
28
|
+
} from "./write-conflict.js";
|
|
29
|
+
const serializeNoteSnapshot = async (note, renderMarkdown) => {
|
|
30
|
+
return {
|
|
31
|
+
id: String(note.id),
|
|
32
|
+
title: note.title,
|
|
33
|
+
updatedAt: note.updatedAt.toISOString(),
|
|
34
|
+
markdown: await renderMarkdown(note.content)
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const isRecord = (value) => {
|
|
38
|
+
return typeof value === "object" && value !== null;
|
|
39
|
+
};
|
|
40
|
+
const serializeSnapshot = (snapshot) => {
|
|
41
|
+
const meta = isRecord(snapshot) && isRecord(snapshot.meta) ? snapshot.meta : {};
|
|
42
|
+
const label = typeof meta.label === "string" ? meta.label : "MCP";
|
|
43
|
+
return {
|
|
44
|
+
id: isRecord(snapshot) && snapshot.id !== void 0 ? String(snapshot.id) : "",
|
|
45
|
+
label,
|
|
46
|
+
createdAt: isRecord(snapshot) && typeof snapshot.createdAt === "string" ? snapshot.createdAt : ""
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const toDryRun = (plan) => ({
|
|
50
|
+
status: "dry_run",
|
|
51
|
+
note: plan.note,
|
|
52
|
+
...plan.match ? { match: plan.match } : {},
|
|
53
|
+
...plan.placement ? { placement: plan.placement } : {},
|
|
54
|
+
proposed: plan.proposed,
|
|
55
|
+
warnings: plan.warnings
|
|
56
|
+
});
|
|
57
|
+
const toTagIds = (tagIds) => {
|
|
58
|
+
return tagIds.map((tagId) => Number(tagId)).filter((tagId) => Number.isSafeInteger(tagId) && tagId > 0);
|
|
59
|
+
};
|
|
60
|
+
const baselineMismatchFailure = () => ({
|
|
61
|
+
status: "failed",
|
|
62
|
+
reason: "BASELINE_MISMATCH",
|
|
63
|
+
message: "The markdown write baseline does not match the current note."
|
|
64
|
+
});
|
|
65
|
+
const missingBaselineFailure = () => ({
|
|
66
|
+
status: "failed",
|
|
67
|
+
reason: "MISSING_BASELINE",
|
|
68
|
+
message: "expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes."
|
|
69
|
+
});
|
|
70
|
+
const unsupportedMarkdownStructureFailure = () => ({
|
|
71
|
+
status: "failed",
|
|
72
|
+
reason: "UNSUPPORTED_MARKDOWN_STRUCTURE",
|
|
73
|
+
message: "This note contains BlockNote content that cannot be safely represented as Markdown."
|
|
74
|
+
});
|
|
75
|
+
const referenceStructureFailure = () => ({
|
|
76
|
+
status: "failed",
|
|
77
|
+
reason: "REFERENCE_STRUCTURE_DECREASED",
|
|
78
|
+
message: "The markdown write would reduce structured note reference links."
|
|
79
|
+
});
|
|
80
|
+
const invalidPropertyInputFailure = (error) => ({
|
|
81
|
+
status: "failed",
|
|
82
|
+
reason: "INVALID_PROPERTY_INPUT",
|
|
83
|
+
message: error.message
|
|
84
|
+
});
|
|
85
|
+
const mapGuardedWriteError = (error) => {
|
|
86
|
+
if (isNoteVersionConflictError(error) || isInvalidNoteVersionError(error)) {
|
|
87
|
+
return baselineMismatchFailure();
|
|
88
|
+
}
|
|
89
|
+
if (isMissingNoteVersionError(error)) {
|
|
90
|
+
return missingBaselineFailure();
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
const resolveMetadataPropertyPatch = async (deps, patch) => {
|
|
95
|
+
if (patch === void 0) {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
if (!deps.resolvePropertyPatch || !deps.validatePropertyPatch) {
|
|
99
|
+
throw new Error("Metadata property patch dependencies are not configured.");
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const resolvedPatch = await deps.resolvePropertyPatch(patch);
|
|
103
|
+
return deps.validatePropertyPatch(resolvedPatch);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error instanceof InvalidNotePropertyInputError) {
|
|
106
|
+
return invalidPropertyInputFailure(error);
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const noteVersionMatches = (expectedUpdatedAt, currentUpdatedAt) => {
|
|
112
|
+
try {
|
|
113
|
+
const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);
|
|
114
|
+
return expectedTimestamp !== null && expectedTimestamp === currentUpdatedAt.getTime();
|
|
115
|
+
} catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const getUnsupportedSourceFailure = (deps, note) => {
|
|
120
|
+
if (deps.hasUnsupportedMarkdownBlocks?.(note.content)) {
|
|
121
|
+
return unsupportedMarkdownStructureFailure();
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
};
|
|
125
|
+
const applyMarkdownPlan = async (deps, input) => {
|
|
126
|
+
const content = await deps.parseMarkdownToContentJson(input.plan.afterMarkdown);
|
|
127
|
+
const beforeReferenceCount = deps.countReferenceInlines?.(input.beforeContentJson) ?? 0;
|
|
128
|
+
const afterReferenceCount = deps.countReferenceInlines?.(content) ?? 0;
|
|
129
|
+
if (input.policy?.preserveReferences !== false && afterReferenceCount < beforeReferenceCount) {
|
|
130
|
+
return referenceStructureFailure();
|
|
131
|
+
}
|
|
132
|
+
const tagIds = toTagIds(deps.extractTagIds(content));
|
|
133
|
+
let updateResult;
|
|
134
|
+
try {
|
|
135
|
+
updateResult = await deps.updateNote({
|
|
136
|
+
id: input.noteId,
|
|
137
|
+
data: {
|
|
138
|
+
content,
|
|
139
|
+
tagIds
|
|
140
|
+
},
|
|
141
|
+
expectedUpdatedAt: input.expectedUpdatedAt ?? input.noteUpdatedAt,
|
|
142
|
+
snapshotMeta: MCP_SNAPSHOT_META,
|
|
143
|
+
...input.force ? { force: true } : {}
|
|
144
|
+
});
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const mappedError = mapGuardedWriteError(error);
|
|
147
|
+
if (mappedError) {
|
|
148
|
+
return mappedError;
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
if (!updateResult) {
|
|
153
|
+
return {
|
|
154
|
+
status: "failed",
|
|
155
|
+
reason: "TARGET_NOT_FOUND",
|
|
156
|
+
message: "The requested note was not found."
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
status: "applied",
|
|
161
|
+
note: {
|
|
162
|
+
id: String(updateResult.note.id),
|
|
163
|
+
updatedAt: updateResult.note.updatedAt.toISOString()
|
|
164
|
+
},
|
|
165
|
+
change: {
|
|
166
|
+
summary: input.summary,
|
|
167
|
+
changedLineCount: input.plan.proposed.changedLineCount,
|
|
168
|
+
changedCharCount: input.plan.proposed.changedCharCount
|
|
169
|
+
},
|
|
170
|
+
snapshot: serializeSnapshot(updateResult.snapshot)
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
const mapPlanResult = async (deps, input) => {
|
|
174
|
+
if (input.plan.status !== "dry_run") {
|
|
175
|
+
return input.plan;
|
|
176
|
+
}
|
|
177
|
+
if (input.dryRun ?? true) {
|
|
178
|
+
return toDryRun(input.plan);
|
|
179
|
+
}
|
|
180
|
+
return applyMarkdownPlan(deps, {
|
|
181
|
+
noteId: input.noteId,
|
|
182
|
+
noteUpdatedAt: input.noteUpdatedAt,
|
|
183
|
+
beforeContentJson: input.beforeContentJson,
|
|
184
|
+
plan: input.plan,
|
|
185
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
186
|
+
policy: input.policy,
|
|
187
|
+
summary: input.summary,
|
|
188
|
+
...input.force ? { force: true } : {}
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
const createMarkdownIntentWriteService = (deps) => ({
|
|
192
|
+
patchNoteMarkdown: async (input) => {
|
|
193
|
+
const note = await deps.findNoteById(input.id);
|
|
194
|
+
if (!note) {
|
|
195
|
+
return {
|
|
196
|
+
status: "failed",
|
|
197
|
+
reason: "TARGET_NOT_FOUND",
|
|
198
|
+
message: "The requested note was not found."
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const unsupportedFailure = getUnsupportedSourceFailure(deps, note);
|
|
202
|
+
if (unsupportedFailure) {
|
|
203
|
+
return unsupportedFailure;
|
|
204
|
+
}
|
|
205
|
+
const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);
|
|
206
|
+
const plan = buildMarkdownPatchPlan({
|
|
207
|
+
note: noteSnapshot,
|
|
208
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
209
|
+
baseMarkdownSha256: input.baseMarkdownSha256,
|
|
210
|
+
intent: input.intent,
|
|
211
|
+
selector: input.selector,
|
|
212
|
+
operation: input.operation,
|
|
213
|
+
policy: input.policy
|
|
214
|
+
});
|
|
215
|
+
return mapPlanResult(deps, {
|
|
216
|
+
noteId: input.id,
|
|
217
|
+
noteUpdatedAt: noteSnapshot.updatedAt,
|
|
218
|
+
beforeContentJson: note.content,
|
|
219
|
+
plan,
|
|
220
|
+
dryRun: input.dryRun,
|
|
221
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
222
|
+
policy: input.policy,
|
|
223
|
+
summary: input.intent
|
|
224
|
+
});
|
|
225
|
+
},
|
|
226
|
+
appendNoteMarkdown: async (input) => {
|
|
227
|
+
const note = await deps.findNoteById(input.id);
|
|
228
|
+
if (!note) {
|
|
229
|
+
return {
|
|
230
|
+
status: "failed",
|
|
231
|
+
reason: "TARGET_NOT_FOUND",
|
|
232
|
+
message: "The requested note was not found."
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const unsupportedFailure = getUnsupportedSourceFailure(deps, note);
|
|
236
|
+
if (unsupportedFailure) {
|
|
237
|
+
return unsupportedFailure;
|
|
238
|
+
}
|
|
239
|
+
const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);
|
|
240
|
+
const plan = buildMarkdownAppendPlan({
|
|
241
|
+
note: noteSnapshot,
|
|
242
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
243
|
+
baseMarkdownSha256: input.baseMarkdownSha256,
|
|
244
|
+
intent: input.intent,
|
|
245
|
+
insertion: input.insertion,
|
|
246
|
+
placement: input.placement,
|
|
247
|
+
separator: input.separator,
|
|
248
|
+
policy: input.policy
|
|
249
|
+
});
|
|
250
|
+
return mapPlanResult(deps, {
|
|
251
|
+
noteId: input.id,
|
|
252
|
+
noteUpdatedAt: noteSnapshot.updatedAt,
|
|
253
|
+
beforeContentJson: note.content,
|
|
254
|
+
plan,
|
|
255
|
+
dryRun: input.dryRun,
|
|
256
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
257
|
+
policy: input.policy,
|
|
258
|
+
summary: input.intent
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
replaceNoteMarkdown: async (input) => {
|
|
262
|
+
const note = await deps.findNoteById(input.id);
|
|
263
|
+
if (!note) {
|
|
264
|
+
return {
|
|
265
|
+
status: "failed",
|
|
266
|
+
reason: "TARGET_NOT_FOUND",
|
|
267
|
+
message: "The requested note was not found."
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const unsupportedFailure = getUnsupportedSourceFailure(deps, note);
|
|
271
|
+
if (unsupportedFailure) {
|
|
272
|
+
return unsupportedFailure;
|
|
273
|
+
}
|
|
274
|
+
const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);
|
|
275
|
+
const plan = buildMarkdownReplacePlan({
|
|
276
|
+
note: noteSnapshot,
|
|
277
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
278
|
+
baseMarkdownSha256: input.baseMarkdownSha256,
|
|
279
|
+
intent: input.intent,
|
|
280
|
+
replacement: input.replacement,
|
|
281
|
+
policy: input.policy
|
|
282
|
+
});
|
|
283
|
+
return mapPlanResult(deps, {
|
|
284
|
+
noteId: input.id,
|
|
285
|
+
noteUpdatedAt: noteSnapshot.updatedAt,
|
|
286
|
+
beforeContentJson: note.content,
|
|
287
|
+
plan,
|
|
288
|
+
dryRun: input.dryRun,
|
|
289
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
290
|
+
policy: input.policy,
|
|
291
|
+
summary: input.intent
|
|
292
|
+
});
|
|
293
|
+
},
|
|
294
|
+
updateNoteMetadata: async (input) => {
|
|
295
|
+
const note = await deps.findNoteById(input.id);
|
|
296
|
+
if (!note) {
|
|
297
|
+
return {
|
|
298
|
+
status: "failed",
|
|
299
|
+
reason: "TARGET_NOT_FOUND",
|
|
300
|
+
message: "The requested note was not found."
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (!noteVersionMatches(input.expectedUpdatedAt, note.updatedAt)) {
|
|
304
|
+
return {
|
|
305
|
+
status: "failed",
|
|
306
|
+
reason: input.expectedUpdatedAt ? "BASELINE_MISMATCH" : "MISSING_BASELINE",
|
|
307
|
+
message: "The metadata update baseline does not match the current note."
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
const nextTitle = input.title?.trim();
|
|
311
|
+
const resolvedPropertyPatch = await resolveMetadataPropertyPatch(deps, input.properties);
|
|
312
|
+
if (resolvedPropertyPatch && "status" in resolvedPropertyPatch) {
|
|
313
|
+
return resolvedPropertyPatch;
|
|
314
|
+
}
|
|
315
|
+
if (input.title !== void 0 && !nextTitle) {
|
|
316
|
+
return {
|
|
317
|
+
status: "failed",
|
|
318
|
+
reason: "EMPTY_REPLACEMENT",
|
|
319
|
+
message: "Note title must not be empty."
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (nextTitle === note.title && (input.layout === void 0 || input.layout === note.layout) && resolvedPropertyPatch === void 0) {
|
|
323
|
+
return {
|
|
324
|
+
status: "failed",
|
|
325
|
+
reason: "NOOP",
|
|
326
|
+
message: "The metadata update would not change the note."
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
status: "dry_run",
|
|
331
|
+
note: {
|
|
332
|
+
id: String(note.id),
|
|
333
|
+
title: note.title,
|
|
334
|
+
updatedAt: note.updatedAt.toISOString()
|
|
335
|
+
},
|
|
336
|
+
proposed: {
|
|
337
|
+
...nextTitle !== void 0 ? { title: nextTitle } : {},
|
|
338
|
+
...input.layout !== void 0 ? { layout: input.layout } : {},
|
|
339
|
+
...resolvedPropertyPatch ? { properties: resolvedPropertyPatch } : {}
|
|
340
|
+
},
|
|
341
|
+
warnings: []
|
|
342
|
+
};
|
|
343
|
+
},
|
|
344
|
+
applyNoteMetadata: async (input) => {
|
|
345
|
+
const preview = await createMarkdownIntentWriteService(deps).updateNoteMetadata(input);
|
|
346
|
+
if (preview.status !== "dry_run") {
|
|
347
|
+
return preview;
|
|
348
|
+
}
|
|
349
|
+
if (input.properties !== void 0) {
|
|
350
|
+
if (!deps.updateProperties) {
|
|
351
|
+
throw new Error("Metadata property write dependency is not configured.");
|
|
352
|
+
}
|
|
353
|
+
let updateResult2;
|
|
354
|
+
try {
|
|
355
|
+
updateResult2 = await deps.updateProperties({
|
|
356
|
+
id: input.id,
|
|
357
|
+
patch: preview.proposed.properties ?? { set: [], deleteKeys: [] },
|
|
358
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
359
|
+
noteData: {
|
|
360
|
+
...preview.proposed.title !== void 0 ? { title: preview.proposed.title } : {},
|
|
361
|
+
...preview.proposed.layout !== void 0 ? { layout: preview.proposed.layout } : {}
|
|
362
|
+
},
|
|
363
|
+
snapshotMeta: MCP_SNAPSHOT_META
|
|
364
|
+
});
|
|
365
|
+
} catch (error) {
|
|
366
|
+
const mappedError = mapGuardedWriteError(error);
|
|
367
|
+
if (mappedError) {
|
|
368
|
+
return mappedError;
|
|
369
|
+
}
|
|
370
|
+
if (error instanceof InvalidNotePropertyInputError) {
|
|
371
|
+
return invalidPropertyInputFailure(error);
|
|
372
|
+
}
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
if (!updateResult2) {
|
|
376
|
+
return {
|
|
377
|
+
status: "failed",
|
|
378
|
+
reason: "TARGET_NOT_FOUND",
|
|
379
|
+
message: "The requested note was not found."
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
status: "applied",
|
|
384
|
+
note: {
|
|
385
|
+
id: String(updateResult2.note.id),
|
|
386
|
+
title: updateResult2.note.title,
|
|
387
|
+
layout: updateResult2.note.layout,
|
|
388
|
+
updatedAt: updateResult2.note.updatedAt.toISOString()
|
|
389
|
+
},
|
|
390
|
+
snapshot: serializeSnapshot(updateResult2.snapshot)
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
let updateResult;
|
|
394
|
+
try {
|
|
395
|
+
updateResult = await deps.updateNote({
|
|
396
|
+
id: input.id,
|
|
397
|
+
data: {
|
|
398
|
+
...preview.proposed.title !== void 0 ? { title: preview.proposed.title } : {},
|
|
399
|
+
...preview.proposed.layout !== void 0 ? { layout: preview.proposed.layout } : {}
|
|
400
|
+
},
|
|
401
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
402
|
+
snapshotMeta: MCP_SNAPSHOT_META
|
|
403
|
+
});
|
|
404
|
+
} catch (error) {
|
|
405
|
+
const mappedError = mapGuardedWriteError(error);
|
|
406
|
+
if (mappedError) {
|
|
407
|
+
return mappedError;
|
|
408
|
+
}
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
411
|
+
if (!updateResult) {
|
|
412
|
+
return {
|
|
413
|
+
status: "failed",
|
|
414
|
+
reason: "TARGET_NOT_FOUND",
|
|
415
|
+
message: "The requested note was not found."
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
status: "applied",
|
|
420
|
+
note: {
|
|
421
|
+
id: String(updateResult.note.id),
|
|
422
|
+
title: updateResult.note.title,
|
|
423
|
+
layout: updateResult.note.layout,
|
|
424
|
+
updatedAt: updateResult.note.updatedAt.toISOString()
|
|
425
|
+
},
|
|
426
|
+
snapshot: serializeSnapshot(updateResult.snapshot)
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
const defaultMarkdownIntentWriteService = createMarkdownIntentWriteService({
|
|
431
|
+
findNoteById: (id) => models.note.findUnique({
|
|
432
|
+
where: { id },
|
|
433
|
+
select: {
|
|
434
|
+
id: true,
|
|
435
|
+
title: true,
|
|
436
|
+
content: true,
|
|
437
|
+
layout: true,
|
|
438
|
+
updatedAt: true
|
|
439
|
+
}
|
|
440
|
+
}),
|
|
441
|
+
renderMarkdown: blocksToMarkdown,
|
|
442
|
+
parseMarkdownToContentJson: markdownToBlocksJson,
|
|
443
|
+
extractTagIds: extractTagIdsFromContentJson,
|
|
444
|
+
countReferenceInlines: countReferenceInlinesFromContentJson,
|
|
445
|
+
hasUnsupportedMarkdownBlocks,
|
|
446
|
+
updateNote: updateNoteWithVersionGuardAndSnapshot,
|
|
447
|
+
resolvePropertyPatch: resolveNotePropertiesPatchValueTypes,
|
|
448
|
+
validatePropertyPatch: validateNotePropertiesPatchValues,
|
|
449
|
+
updateProperties: updateNotePropertiesWithVersionGuardAndSnapshot
|
|
450
|
+
});
|
|
451
|
+
const patchNoteMarkdown = async (input) => {
|
|
452
|
+
return defaultMarkdownIntentWriteService.patchNoteMarkdown(input);
|
|
453
|
+
};
|
|
454
|
+
const appendNoteMarkdown = async (input) => {
|
|
455
|
+
return defaultMarkdownIntentWriteService.appendNoteMarkdown(input);
|
|
456
|
+
};
|
|
457
|
+
const replaceNoteMarkdown = async (input) => {
|
|
458
|
+
return defaultMarkdownIntentWriteService.replaceNoteMarkdown(input);
|
|
459
|
+
};
|
|
460
|
+
const updateNoteMetadata = async (input) => {
|
|
461
|
+
if (input.dryRun ?? true) {
|
|
462
|
+
return defaultMarkdownIntentWriteService.updateNoteMetadata(input);
|
|
463
|
+
}
|
|
464
|
+
return defaultMarkdownIntentWriteService.applyNoteMetadata(input);
|
|
465
|
+
};
|
|
466
|
+
const calculateNoteMarkdownSha256 = (markdown) => {
|
|
467
|
+
return calculateMarkdownSha256(markdown);
|
|
468
|
+
};
|
|
469
|
+
export {
|
|
470
|
+
appendNoteMarkdown,
|
|
471
|
+
calculateNoteMarkdownSha256,
|
|
472
|
+
createMarkdownIntentWriteService,
|
|
473
|
+
defaultMarkdownIntentWriteService,
|
|
474
|
+
patchNoteMarkdown,
|
|
475
|
+
replaceNoteMarkdown,
|
|
476
|
+
updateNoteMetadata
|
|
477
|
+
};
|
|
478
|
+
//# sourceMappingURL=markdown-intent-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/note/services/markdown-intent-write.ts"],"sourcesContent":["import models, { type NoteLayout } from '~/models.js';\nimport {\n blocksToMarkdown,\n countReferenceInlinesFromContentJson,\n extractTagIdsFromContentJson,\n hasUnsupportedMarkdownBlocks,\n markdownToBlocksJson,\n} from '~/modules/blocknote.js';\nimport type {\n MarkdownAppendPlanResult,\n MarkdownChangeDryRun,\n MarkdownChangeFailure,\n MarkdownChangePlan,\n MarkdownPatchOperation,\n MarkdownPatchPlanResult,\n MarkdownPatchSelector,\n MarkdownReplacePlanResult,\n} from './markdown-patch.js';\nimport {\n buildMarkdownAppendPlan,\n buildMarkdownPatchPlan,\n buildMarkdownReplacePlan,\n calculateMarkdownSha256,\n} from './markdown-patch.js';\nimport {\n InvalidNotePropertyInputError,\n type NotePropertiesByKeyPatchInput,\n type NotePropertiesPatchInput,\n resolveNotePropertiesPatchValueTypes,\n updateNotePropertiesWithVersionGuardAndSnapshot,\n validateNotePropertiesPatchValues,\n} from './properties.js';\nimport { MCP_SNAPSHOT_META } from './snapshot.js';\nimport { type GuardedNoteWriteResult, updateNoteWithVersionGuardAndSnapshot } from './write.js';\nimport {\n isInvalidNoteVersionError,\n isMissingNoteVersionError,\n isNoteVersionConflictError,\n parseNoteVersion,\n} from './write-conflict.js';\n\ninterface MarkdownIntentNoteRecord {\n id: number;\n title: string;\n content: string;\n layout: NoteLayout;\n updatedAt: Date;\n}\n\ninterface MarkdownIntentWriteDeps {\n findNoteById: (id: number) => Promise<MarkdownIntentNoteRecord | null>;\n renderMarkdown: (contentJson: string) => Promise<string>;\n parseMarkdownToContentJson: (markdown: string) => Promise<string>;\n extractTagIds: (contentJson: string) => string[];\n countReferenceInlines?: (contentJson: string) => number;\n hasUnsupportedMarkdownBlocks?: (contentJson: string) => boolean;\n updateNote: (input: {\n id: number;\n data: {\n title?: string;\n content?: string;\n layout?: NoteLayout;\n tagIds?: number[];\n };\n expectedUpdatedAt: string;\n snapshotMeta?: string;\n force?: boolean;\n }) => Promise<GuardedNoteWriteResult | null>;\n resolvePropertyPatch?: (patch: NotePropertiesByKeyPatchInput) => Promise<NotePropertiesPatchInput>;\n validatePropertyPatch?: (patch: NotePropertiesPatchInput) => Promise<NotePropertiesPatchInput>;\n updateProperties?: (input: {\n id: number;\n patch: NotePropertiesPatchInput;\n expectedUpdatedAt: string;\n noteData?: {\n title?: string;\n layout?: NoteLayout;\n };\n snapshotMeta?: string;\n }) => Promise<{\n note: {\n id: number;\n title: string;\n layout: NoteLayout;\n updatedAt: Date;\n };\n snapshot: unknown;\n } | null>;\n}\n\nexport interface MarkdownWritePolicy {\n allowNoop?: boolean;\n maxChangedChars?: number;\n maxChangedLines?: number;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface PatchNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n selector: MarkdownPatchSelector;\n operation: MarkdownPatchOperation;\n policy?: MarkdownWritePolicy;\n dryRun?: boolean;\n}\n\nexport interface AppendNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n insertion: string;\n placement?:\n | {\n type: 'end';\n }\n | {\n type: 'after_heading';\n heading: string;\n level?: number;\n };\n separator?: '\\n\\n' | '\\n';\n policy?: MarkdownWritePolicy;\n dryRun?: boolean;\n}\n\nexport interface ReplaceNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownWritePolicy;\n dryRun?: boolean;\n}\n\nexport interface UpdateNoteMetadataInput {\n id: number;\n expectedUpdatedAt: string;\n title?: string;\n layout?: NoteLayout;\n properties?: NotePropertiesByKeyPatchInput;\n}\n\nexport interface AppliedMarkdownWriteResult {\n status: 'applied';\n note: {\n id: string;\n updatedAt: string;\n };\n change: {\n summary: string;\n changedLineCount: number;\n changedCharCount: number;\n };\n snapshot: {\n id: string;\n label: string;\n createdAt: string;\n };\n}\n\nexport interface MetadataUpdatePreview {\n status: 'dry_run';\n note: {\n id: string;\n title: string;\n updatedAt: string;\n };\n proposed: {\n title?: string;\n layout?: NoteLayout;\n properties?: NotePropertiesPatchInput;\n };\n warnings: string[];\n}\n\nexport interface AppliedMetadataUpdateResult {\n status: 'applied';\n note: {\n id: string;\n title: string;\n layout: NoteLayout;\n updatedAt: string;\n };\n snapshot: {\n id: string;\n label: string;\n createdAt: string;\n };\n}\n\nexport type MarkdownIntentWriteResult =\n | MarkdownChangeDryRun\n | AppliedMarkdownWriteResult\n | MarkdownChangeFailure\n | Extract<MarkdownPatchPlanResult, { status: 'needs_disambiguation' }>;\n\nexport type MetadataUpdateResult = MetadataUpdatePreview | AppliedMetadataUpdateResult | MarkdownChangeFailure;\n\nconst serializeNoteSnapshot = async (\n note: MarkdownIntentNoteRecord,\n renderMarkdown: (contentJson: string) => Promise<string>,\n) => {\n return {\n id: String(note.id),\n title: note.title,\n updatedAt: note.updatedAt.toISOString(),\n markdown: await renderMarkdown(note.content),\n };\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst serializeSnapshot = (snapshot: unknown) => {\n const meta = isRecord(snapshot) && isRecord(snapshot.meta) ? snapshot.meta : {};\n const label = typeof meta.label === 'string' ? meta.label : 'MCP';\n\n return {\n id: isRecord(snapshot) && snapshot.id !== undefined ? String(snapshot.id) : '',\n label,\n createdAt: isRecord(snapshot) && typeof snapshot.createdAt === 'string' ? snapshot.createdAt : '',\n };\n};\n\nconst toDryRun = (plan: MarkdownChangePlan): MarkdownChangeDryRun => ({\n status: 'dry_run',\n note: plan.note,\n ...(plan.match ? { match: plan.match } : {}),\n ...(plan.placement ? { placement: plan.placement } : {}),\n proposed: plan.proposed,\n warnings: plan.warnings,\n});\n\nconst toTagIds = (tagIds: string[]) => {\n return tagIds.map((tagId) => Number(tagId)).filter((tagId) => Number.isSafeInteger(tagId) && tagId > 0);\n};\n\nconst baselineMismatchFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message: 'The markdown write baseline does not match the current note.',\n});\n\nconst missingBaselineFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n});\n\nconst unsupportedMarkdownStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'UNSUPPORTED_MARKDOWN_STRUCTURE',\n message: 'This note contains BlockNote content that cannot be safely represented as Markdown.',\n});\n\nconst referenceStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'REFERENCE_STRUCTURE_DECREASED',\n message: 'The markdown write would reduce structured note reference links.',\n});\n\nconst invalidPropertyInputFailure = (error: InvalidNotePropertyInputError): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'INVALID_PROPERTY_INPUT',\n message: error.message,\n});\n\nconst mapGuardedWriteError = (error: unknown): MarkdownChangeFailure | null => {\n if (isNoteVersionConflictError(error) || isInvalidNoteVersionError(error)) {\n return baselineMismatchFailure();\n }\n\n if (isMissingNoteVersionError(error)) {\n return missingBaselineFailure();\n }\n\n return null;\n};\n\nconst resolveMetadataPropertyPatch = async (\n deps: MarkdownIntentWriteDeps,\n patch: NotePropertiesByKeyPatchInput | undefined,\n): Promise<NotePropertiesPatchInput | MarkdownChangeFailure | undefined> => {\n if (patch === undefined) {\n return undefined;\n }\n\n if (!deps.resolvePropertyPatch || !deps.validatePropertyPatch) {\n throw new Error('Metadata property patch dependencies are not configured.');\n }\n\n try {\n const resolvedPatch = await deps.resolvePropertyPatch(patch);\n return deps.validatePropertyPatch(resolvedPatch);\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n return invalidPropertyInputFailure(error);\n }\n\n throw error;\n }\n};\n\nconst noteVersionMatches = (expectedUpdatedAt: string | undefined, currentUpdatedAt: Date) => {\n try {\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n return expectedTimestamp !== null && expectedTimestamp === currentUpdatedAt.getTime();\n } catch {\n return false;\n }\n};\n\nconst getUnsupportedSourceFailure = (deps: MarkdownIntentWriteDeps, note: MarkdownIntentNoteRecord) => {\n if (deps.hasUnsupportedMarkdownBlocks?.(note.content)) {\n return unsupportedMarkdownStructureFailure();\n }\n\n return null;\n};\n\nconst applyMarkdownPlan = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: Extract<MarkdownPatchPlanResult, { status: 'dry_run' }>;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<AppliedMarkdownWriteResult | MarkdownChangeFailure> => {\n const content = await deps.parseMarkdownToContentJson(input.plan.afterMarkdown);\n const beforeReferenceCount = deps.countReferenceInlines?.(input.beforeContentJson) ?? 0;\n const afterReferenceCount = deps.countReferenceInlines?.(content) ?? 0;\n\n if (input.policy?.preserveReferences !== false && afterReferenceCount < beforeReferenceCount) {\n return referenceStructureFailure();\n }\n\n const tagIds = toTagIds(deps.extractTagIds(content));\n let updateResult: GuardedNoteWriteResult | null;\n\n try {\n updateResult = await deps.updateNote({\n id: input.noteId,\n data: {\n content,\n tagIds,\n },\n expectedUpdatedAt: input.expectedUpdatedAt ?? input.noteUpdatedAt,\n snapshotMeta: MCP_SNAPSHOT_META,\n ...(input.force ? { force: true } : {}),\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n change: {\n summary: input.summary,\n changedLineCount: input.plan.proposed.changedLineCount,\n changedCharCount: input.plan.proposed.changedCharCount,\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n};\n\nconst mapPlanResult = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: MarkdownPatchPlanResult | MarkdownAppendPlanResult | MarkdownReplacePlanResult;\n dryRun?: boolean;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<MarkdownIntentWriteResult> => {\n if (input.plan.status !== 'dry_run') {\n return input.plan;\n }\n\n if (input.dryRun ?? true) {\n return toDryRun(input.plan);\n }\n\n return applyMarkdownPlan(deps, {\n noteId: input.noteId,\n noteUpdatedAt: input.noteUpdatedAt,\n beforeContentJson: input.beforeContentJson,\n plan: input.plan,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.summary,\n ...(input.force ? { force: true } : {}),\n });\n};\n\nexport const createMarkdownIntentWriteService = (deps: MarkdownIntentWriteDeps) => ({\n patchNoteMarkdown: async (input: PatchNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownPatchPlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n selector: input.selector,\n operation: input.operation,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n dryRun: input.dryRun,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n appendNoteMarkdown: async (input: AppendNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownAppendPlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n insertion: input.insertion,\n placement: input.placement,\n separator: input.separator,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n dryRun: input.dryRun,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n replaceNoteMarkdown: async (input: ReplaceNoteMarkdownInput): Promise<MarkdownIntentWriteResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n const unsupportedFailure = getUnsupportedSourceFailure(deps, note);\n\n if (unsupportedFailure) {\n return unsupportedFailure;\n }\n\n const noteSnapshot = await serializeNoteSnapshot(note, deps.renderMarkdown);\n const plan = buildMarkdownReplacePlan({\n note: noteSnapshot,\n expectedUpdatedAt: input.expectedUpdatedAt,\n baseMarkdownSha256: input.baseMarkdownSha256,\n intent: input.intent,\n replacement: input.replacement,\n policy: input.policy,\n });\n\n return mapPlanResult(deps, {\n noteId: input.id,\n noteUpdatedAt: noteSnapshot.updatedAt,\n beforeContentJson: note.content,\n plan,\n dryRun: input.dryRun,\n expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n updateNoteMetadata: async (input: UpdateNoteMetadataInput): Promise<MetadataUpdateResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n if (!noteVersionMatches(input.expectedUpdatedAt, note.updatedAt)) {\n return {\n status: 'failed',\n reason: input.expectedUpdatedAt ? 'BASELINE_MISMATCH' : 'MISSING_BASELINE',\n message: 'The metadata update baseline does not match the current note.',\n };\n }\n\n const nextTitle = input.title?.trim();\n const resolvedPropertyPatch = await resolveMetadataPropertyPatch(deps, input.properties);\n\n if (resolvedPropertyPatch && 'status' in resolvedPropertyPatch) {\n return resolvedPropertyPatch;\n }\n\n if (input.title !== undefined && !nextTitle) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Note title must not be empty.',\n };\n }\n\n if (\n nextTitle === note.title &&\n (input.layout === undefined || input.layout === note.layout) &&\n resolvedPropertyPatch === undefined\n ) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The metadata update would not change the note.',\n };\n }\n\n return {\n status: 'dry_run',\n note: {\n id: String(note.id),\n title: note.title,\n updatedAt: note.updatedAt.toISOString(),\n },\n proposed: {\n ...(nextTitle !== undefined ? { title: nextTitle } : {}),\n ...(input.layout !== undefined ? { layout: input.layout } : {}),\n ...(resolvedPropertyPatch ? { properties: resolvedPropertyPatch } : {}),\n },\n warnings: [],\n };\n },\n\n applyNoteMetadata: async (input: UpdateNoteMetadataInput): Promise<MetadataUpdateResult> => {\n const preview = await createMarkdownIntentWriteService(deps).updateNoteMetadata(input);\n\n if (preview.status !== 'dry_run') {\n return preview;\n }\n\n if (input.properties !== undefined) {\n if (!deps.updateProperties) {\n throw new Error('Metadata property write dependency is not configured.');\n }\n\n let updateResult: Awaited<ReturnType<NonNullable<MarkdownIntentWriteDeps['updateProperties']>>>;\n\n try {\n updateResult = await deps.updateProperties({\n id: input.id,\n patch: preview.proposed.properties ?? { set: [], deleteKeys: [] },\n expectedUpdatedAt: input.expectedUpdatedAt,\n noteData: {\n ...(preview.proposed.title !== undefined ? { title: preview.proposed.title } : {}),\n ...(preview.proposed.layout !== undefined ? { layout: preview.proposed.layout } : {}),\n },\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n if (error instanceof InvalidNotePropertyInputError) {\n return invalidPropertyInputFailure(error);\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n title: updateResult.note.title,\n layout: updateResult.note.layout,\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n }\n\n let updateResult: GuardedNoteWriteResult | null;\n\n try {\n updateResult = await deps.updateNote({\n id: input.id,\n data: {\n ...(preview.proposed.title !== undefined ? { title: preview.proposed.title } : {}),\n ...(preview.proposed.layout !== undefined ? { layout: preview.proposed.layout } : {}),\n },\n expectedUpdatedAt: input.expectedUpdatedAt,\n snapshotMeta: MCP_SNAPSHOT_META,\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n title: updateResult.note.title,\n layout: updateResult.note.layout,\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n },\n});\n\nexport const defaultMarkdownIntentWriteService = createMarkdownIntentWriteService({\n findNoteById: (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n id: true,\n title: true,\n content: true,\n layout: true,\n updatedAt: true,\n },\n }),\n renderMarkdown: blocksToMarkdown,\n parseMarkdownToContentJson: markdownToBlocksJson,\n extractTagIds: extractTagIdsFromContentJson,\n countReferenceInlines: countReferenceInlinesFromContentJson,\n hasUnsupportedMarkdownBlocks,\n updateNote: updateNoteWithVersionGuardAndSnapshot,\n resolvePropertyPatch: resolveNotePropertiesPatchValueTypes,\n validatePropertyPatch: validateNotePropertiesPatchValues,\n updateProperties: updateNotePropertiesWithVersionGuardAndSnapshot,\n});\n\nexport const patchNoteMarkdown = async (input: PatchNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.patchNoteMarkdown(input);\n};\n\nexport const appendNoteMarkdown = async (input: AppendNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.appendNoteMarkdown(input);\n};\n\nexport const replaceNoteMarkdown = async (input: ReplaceNoteMarkdownInput) => {\n return defaultMarkdownIntentWriteService.replaceNoteMarkdown(input);\n};\n\nexport const updateNoteMetadata = async (input: UpdateNoteMetadataInput & { dryRun?: boolean }) => {\n if (input.dryRun ?? true) {\n return defaultMarkdownIntentWriteService.updateNoteMetadata(input);\n }\n\n return defaultMarkdownIntentWriteService.applyNoteMetadata(input);\n};\n\nexport const calculateNoteMarkdownSha256 = (markdown: string) => {\n return calculateMarkdownSha256(markdown);\n};\n"],"mappings":"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","names":["updateResult"]}
|