ocean-brain 0.6.2 → 0.7.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 +79 -24
- package/package.json +2 -1
- package/server/client/dist/assets/{Calendar-DbPut8EU.js → Calendar-B2_OJcHD.js} +1 -1
- package/server/client/dist/assets/{Callout-DfX-gIUB.js → Callout-BvXAlA5k.js} +1 -1
- package/server/client/dist/assets/{Graph-yHE9m5fy.js → Graph-DO35UCPM.js} +1 -1
- package/server/client/dist/assets/{Image.es-CtMc7BW4.js → Image.es-DLqLiGdW.js} +1 -1
- package/server/client/dist/assets/Note-AaTlS352.js +22 -0
- package/server/client/dist/assets/{Plus.es-BGVIIQtM.js → Plus.es-I7xuoJ9B.js} +1 -1
- package/server/client/dist/assets/{Reminders-CvEJf0eQ.js → Reminders-B0LJeSlu.js} +1 -1
- package/server/client/dist/assets/{Search-CKZhztuV.js → Search-xbDSlNOT.js} +1 -1
- package/server/client/dist/assets/{SurfaceCard-Bnfo5EOl.js → SurfaceCard-Brd1cMxi.js} +1 -1
- package/server/client/dist/assets/{Tag-C4oAwjs0.js → Tag-2PlhkjwY.js} +1 -1
- package/server/client/dist/assets/{TagNotes-Rl1ZSJP6.js → TagNotes-1yUBLqhX.js} +1 -1
- package/server/client/dist/assets/{Trash.es-BcwUz2yM.js → Trash.es-B-7U25B6.js} +1 -1
- package/server/client/dist/assets/{ViewNotes-B9Yc0A87.js → ViewNotes-D4X-MCTk.js} +1 -1
- package/server/client/dist/assets/{ViewSectionTableRenderer-C5yJBO7C.js → ViewSectionTableRenderer-FuqBpQts.js} +1 -1
- package/server/client/dist/assets/{Views-Bqq0pUEE.js → Views-CBEimWY8.js} +1 -1
- package/server/client/dist/assets/{image.api-CuGTgzyQ.js → image.api-BtadKO98.js} +1 -1
- package/server/client/dist/assets/{index-CDjYEIVf.css → index-BFkZTZ6v.css} +1 -1
- package/server/client/dist/assets/{index-B43ulIPo.js → index-BgnmVjeF.js} +45 -11
- package/server/client/dist/assets/{index-BQDR6hIA.js → index-sGhvnErO.js} +1 -1
- package/server/client/dist/assets/{manage-image-Bs4Kmevx.js → manage-image-B4UZ-ZQx.js} +1 -1
- package/server/client/dist/assets/{manage-image-detail-WARala-v.js → manage-image-detail-ejAh3nCL.js} +1 -1
- package/server/client/dist/assets/{mcp-C0Z3vKBa.js → mcp-aIwuouT9.js} +1 -1
- package/server/client/dist/assets/{placeholder-D080guzn.js → placeholder-B_9Hnm7h.js} +4 -4
- package/server/client/dist/assets/{properties-DbnmfbT5.js → properties-D8oLyTCD.js} +1 -1
- package/server/client/dist/assets/{trash-D5ZiZfTk.js → trash-w7Hhxckg.js} +1 -1
- package/server/client/dist/assets/{useReminderMutate-h7d_S_X2.js → useReminderMutate--RlAj_Gg.js} +1 -1
- package/server/client/dist/index.html +2 -2
- package/server/dist/features/note/graphql/note.query.resolver.js +88 -47
- package/server/dist/features/note/graphql/note.query.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.type-defs.js +32 -0
- package/server/dist/features/note/graphql/note.type-defs.js.map +1 -1
- package/server/dist/features/note/http/mcp.js +32 -0
- package/server/dist/features/note/http/mcp.js.map +1 -1
- package/server/dist/features/note/services/markdown-intent-write.js +3 -3
- package/server/dist/features/note/services/markdown-intent-write.js.map +1 -1
- package/server/dist/features/note/services/markdown-patch.js +34 -4
- package/server/dist/features/note/services/markdown-patch.js.map +1 -1
- package/server/dist/features/note/services/snapshot.js +159 -0
- package/server/dist/features/note/services/snapshot.js.map +1 -1
- package/server/dist/modules/blocknote.js +274 -24
- package/server/dist/modules/blocknote.js.map +1 -1
- package/server/dist/routes/mcp.js +5 -0
- package/server/dist/routes/mcp.js.map +1 -1
- package/server/client/dist/assets/Note-BECn-AoI.js +0 -21
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FILE_HEADERS_ONLY, formatPatch, structuredPatch } from "diff";
|
|
1
2
|
import { ensureTagByName } from "../../../features/tag/services/organization.js";
|
|
2
3
|
import models from "../../../models.js";
|
|
3
4
|
import { blocksToMarkdown, extractTagIdsFromContentJson } from "../../../modules/blocknote.js";
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
SNAPSHOT_MAX_PER_NOTE,
|
|
9
10
|
SNAPSHOT_RETENTION_DAYS
|
|
10
11
|
} from "../../../modules/recovery-retention.js";
|
|
12
|
+
import { calculateMarkdownSha256 } from "./markdown-patch.js";
|
|
11
13
|
import { buildNoteSearchProjection, extractVisibleSearchTextFromContent } from "./search.js";
|
|
12
14
|
const SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH = 240;
|
|
13
15
|
const parseMeta = (value) => {
|
|
@@ -31,6 +33,13 @@ const readSnapshotContent = (payload) => {
|
|
|
31
33
|
return "";
|
|
32
34
|
}
|
|
33
35
|
};
|
|
36
|
+
const readSnapshotTitle = (payload, fallback) => {
|
|
37
|
+
try {
|
|
38
|
+
return parsePayload(payload).title;
|
|
39
|
+
} catch {
|
|
40
|
+
return fallback;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
34
43
|
const buildSnapshotContentPreview = (content) => {
|
|
35
44
|
const preview = extractVisibleSearchTextFromContent(content);
|
|
36
45
|
if (preview.length <= SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH) {
|
|
@@ -159,6 +168,65 @@ const renderNoteSnapshotContentAsMarkdown = async (payload) => {
|
|
|
159
168
|
return "";
|
|
160
169
|
}
|
|
161
170
|
};
|
|
171
|
+
const renderNoteRecordContentAsMarkdown = async (note) => {
|
|
172
|
+
try {
|
|
173
|
+
return blocksToMarkdown(note.content);
|
|
174
|
+
} catch {
|
|
175
|
+
return "";
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const createSnapshotMarkdownDiff = (beforeMarkdown, afterMarkdown, contextLines = 3) => {
|
|
179
|
+
const patch = structuredPatch("before.md", "after.md", beforeMarkdown, afterMarkdown, void 0, void 0, {
|
|
180
|
+
context: contextLines === null ? Number.MAX_SAFE_INTEGER : contextLines
|
|
181
|
+
});
|
|
182
|
+
const changedLines = patch.hunks.flatMap(
|
|
183
|
+
(hunk) => hunk.lines.filter((line) => line.startsWith("+") && !line.startsWith("+++") || line.startsWith("-"))
|
|
184
|
+
);
|
|
185
|
+
return {
|
|
186
|
+
markdown: formatPatch(patch, FILE_HEADERS_ONLY).trimEnd(),
|
|
187
|
+
changedLineCount: changedLines.length,
|
|
188
|
+
changedCharCount: changedLines.reduce((total, line) => total + line.slice(1).length, 0)
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
const snapshotToDiffEndpoint = (snapshot) => ({
|
|
192
|
+
kind: "snapshot",
|
|
193
|
+
id: String(snapshot.id),
|
|
194
|
+
title: readSnapshotTitle(snapshot.payload, snapshot.title),
|
|
195
|
+
createdAt: snapshot.createdAt.toISOString(),
|
|
196
|
+
meta: parseMeta(snapshot.meta)
|
|
197
|
+
});
|
|
198
|
+
const noteToDiffEndpoint = (note) => ({
|
|
199
|
+
kind: "current_note",
|
|
200
|
+
id: String(note.id),
|
|
201
|
+
title: note.title,
|
|
202
|
+
...note.updatedAt ? { updatedAt: note.updatedAt.toISOString() } : {}
|
|
203
|
+
});
|
|
204
|
+
const buildSnapshotDiff = async ({
|
|
205
|
+
beforeSnapshot,
|
|
206
|
+
afterSnapshot,
|
|
207
|
+
afterNote,
|
|
208
|
+
contextLines
|
|
209
|
+
}) => {
|
|
210
|
+
if (!afterSnapshot && !afterNote) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const beforeMarkdown = await renderNoteSnapshotContentAsMarkdown(beforeSnapshot.payload);
|
|
214
|
+
const afterMarkdown = afterSnapshot ? await renderNoteSnapshotContentAsMarkdown(afterSnapshot.payload) : afterNote ? await renderNoteRecordContentAsMarkdown(afterNote) : "";
|
|
215
|
+
const diff = createSnapshotMarkdownDiff(beforeMarkdown, afterMarkdown, contextLines ?? 3);
|
|
216
|
+
return {
|
|
217
|
+
noteId: String(beforeSnapshot.noteId),
|
|
218
|
+
mode: afterSnapshot ? "snapshot_to_snapshot" : "snapshot_to_current",
|
|
219
|
+
before: snapshotToDiffEndpoint(beforeSnapshot),
|
|
220
|
+
after: afterSnapshot ? snapshotToDiffEndpoint(afterSnapshot) : noteToDiffEndpoint(afterNote),
|
|
221
|
+
diff: {
|
|
222
|
+
markdown: diff.markdown,
|
|
223
|
+
changedLineCount: diff.changedLineCount,
|
|
224
|
+
changedCharCount: diff.changedCharCount,
|
|
225
|
+
beforeMarkdownSha256: calculateMarkdownSha256(beforeMarkdown),
|
|
226
|
+
afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown)
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
};
|
|
162
230
|
const serializeSnapshotWithContent = async (snapshot) => {
|
|
163
231
|
const contentAsMarkdown = await renderNoteSnapshotContentAsMarkdown(snapshot.payload);
|
|
164
232
|
return {
|
|
@@ -249,6 +317,63 @@ const createNoteSnapshotService = (deps) => ({
|
|
|
249
317
|
}
|
|
250
318
|
return serializeSnapshotWithContent(snapshot);
|
|
251
319
|
},
|
|
320
|
+
diffSnapshot: async (snapshotId, options = {}) => {
|
|
321
|
+
await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);
|
|
322
|
+
const snapshot = await deps.findSnapshotById(snapshotId);
|
|
323
|
+
if (!snapshot) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
if (options.compareToSnapshotId !== void 0) {
|
|
327
|
+
const comparedSnapshot = await deps.findSnapshotById(options.compareToSnapshotId);
|
|
328
|
+
if (!comparedSnapshot || comparedSnapshot.noteId !== snapshot.noteId) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
return buildSnapshotDiff({
|
|
332
|
+
beforeSnapshot: snapshot,
|
|
333
|
+
afterSnapshot: comparedSnapshot,
|
|
334
|
+
contextLines: options.contextLines
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
if (options.target === "previous") {
|
|
338
|
+
const previousSnapshot = deps.findPreviousSnapshot ? await deps.findPreviousSnapshot(snapshot) : null;
|
|
339
|
+
if (!previousSnapshot) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
return buildSnapshotDiff({
|
|
343
|
+
beforeSnapshot: previousSnapshot,
|
|
344
|
+
afterSnapshot: snapshot,
|
|
345
|
+
contextLines: options.contextLines
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
if (options.target === "current") {
|
|
349
|
+
const note2 = await deps.findNoteById(snapshot.noteId);
|
|
350
|
+
if (!note2) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
return buildSnapshotDiff({
|
|
354
|
+
beforeSnapshot: snapshot,
|
|
355
|
+
afterNote: note2,
|
|
356
|
+
contextLines: options.contextLines
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const nextSnapshot = deps.findNextSnapshot ? await deps.findNextSnapshot(snapshot) : null;
|
|
360
|
+
if (nextSnapshot) {
|
|
361
|
+
return buildSnapshotDiff({
|
|
362
|
+
beforeSnapshot: snapshot,
|
|
363
|
+
afterSnapshot: nextSnapshot,
|
|
364
|
+
contextLines: options.contextLines
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const note = await deps.findNoteById(snapshot.noteId);
|
|
368
|
+
if (!note) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
return buildSnapshotDiff({
|
|
372
|
+
beforeSnapshot: snapshot,
|
|
373
|
+
afterNote: note,
|
|
374
|
+
contextLines: options.contextLines
|
|
375
|
+
});
|
|
376
|
+
},
|
|
252
377
|
restoreSnapshot: async (snapshotId, options) => {
|
|
253
378
|
await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);
|
|
254
379
|
const snapshot = await deps.findSnapshotById(snapshotId);
|
|
@@ -371,6 +496,36 @@ const defaultNoteSnapshotService = createNoteSnapshotService({
|
|
|
371
496
|
findSnapshotById: async (id) => {
|
|
372
497
|
return models.noteSnapshot.findUnique({ where: { id } });
|
|
373
498
|
},
|
|
499
|
+
findNextSnapshot: async (snapshot) => {
|
|
500
|
+
return models.noteSnapshot.findFirst({
|
|
501
|
+
where: {
|
|
502
|
+
noteId: snapshot.noteId,
|
|
503
|
+
OR: [
|
|
504
|
+
{ createdAt: { gt: snapshot.createdAt } },
|
|
505
|
+
{
|
|
506
|
+
createdAt: snapshot.createdAt,
|
|
507
|
+
id: { gt: snapshot.id }
|
|
508
|
+
}
|
|
509
|
+
]
|
|
510
|
+
},
|
|
511
|
+
orderBy: [{ createdAt: "asc" }, { id: "asc" }]
|
|
512
|
+
});
|
|
513
|
+
},
|
|
514
|
+
findPreviousSnapshot: async (snapshot) => {
|
|
515
|
+
return models.noteSnapshot.findFirst({
|
|
516
|
+
where: {
|
|
517
|
+
noteId: snapshot.noteId,
|
|
518
|
+
OR: [
|
|
519
|
+
{ createdAt: { lt: snapshot.createdAt } },
|
|
520
|
+
{
|
|
521
|
+
createdAt: snapshot.createdAt,
|
|
522
|
+
id: { lt: snapshot.id }
|
|
523
|
+
}
|
|
524
|
+
]
|
|
525
|
+
},
|
|
526
|
+
orderBy: [{ createdAt: "desc" }, { id: "desc" }]
|
|
527
|
+
});
|
|
528
|
+
},
|
|
374
529
|
updateNote: async (id, input) => {
|
|
375
530
|
const { tagIds, properties, ...noteInput } = input;
|
|
376
531
|
return models.$transaction(async (tx) => {
|
|
@@ -445,6 +600,9 @@ const listNoteSnapshots = async (noteId, limit) => {
|
|
|
445
600
|
const getNoteSnapshot = async (snapshotId) => {
|
|
446
601
|
return defaultNoteSnapshotService.getSnapshot(snapshotId);
|
|
447
602
|
};
|
|
603
|
+
const diffNoteSnapshot = async (snapshotId, options) => {
|
|
604
|
+
return defaultNoteSnapshotService.diffSnapshot(snapshotId, options);
|
|
605
|
+
};
|
|
448
606
|
const restoreNoteSnapshot = async (snapshotId, options) => {
|
|
449
607
|
return defaultNoteSnapshotService.restoreSnapshot(snapshotId, options);
|
|
450
608
|
};
|
|
@@ -457,6 +615,7 @@ export {
|
|
|
457
615
|
createNoteSnapshotService,
|
|
458
616
|
createSnapshotMetaFromUserAgent,
|
|
459
617
|
defaultNoteSnapshotService,
|
|
618
|
+
diffNoteSnapshot,
|
|
460
619
|
getNoteSnapshot,
|
|
461
620
|
listNoteSnapshots,
|
|
462
621
|
purgeExpiredNoteSnapshots,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/snapshot.ts"],"sourcesContent":["import { ensureTagByName } from '~/features/tag/services/organization.js';\nimport models, { type NoteLayout, type PropertyValueType } from '~/models.js';\nimport { blocksToMarkdown, extractTagIdsFromContentJson } from '~/modules/blocknote.js';\nimport { type BlockNoteTreeNode, parseBlockNoteContent, walkBlockNoteTree } from '~/modules/blocknote-tree.js';\nimport {\n createRetentionCutoff,\n RECOVERY_CLEANUP_BATCH_LIMIT,\n SNAPSHOT_MAX_PER_NOTE,\n SNAPSHOT_RETENTION_DAYS,\n} from '~/modules/recovery-retention.js';\nimport { buildNoteSearchProjection, extractVisibleSearchTextFromContent } from './search.js';\n\nconst SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH = 240;\n\ninterface NoteRecord {\n id: number;\n title: string;\n content: string;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n updatedAt?: Date;\n properties?: SnapshotNoteProperty[];\n}\n\ninterface SnapshotNoteProperty {\n key: string;\n name: string;\n valueType: PropertyValueType;\n textValue?: string | null;\n textValueNormalized?: string | null;\n numberValue?: number | null;\n dateValue?: Date | string | null;\n boolValue?: boolean | null;\n optionValue?: string | null;\n optionLabel?: string | null;\n optionColor?: string | null;\n}\n\ninterface NoteSnapshotRecord {\n id: number;\n noteId: number;\n title: string;\n payload: string;\n editSessionId: string | null;\n meta: string | null;\n createdAt: Date;\n}\n\ninterface NoteSnapshotDeps {\n findNoteById: (id: number) => Promise<NoteRecord | null>;\n findSnapshotByEditSessionId: (noteId: number, editSessionId: string) => Promise<NoteSnapshotRecord | null>;\n findLatestSnapshot: (noteId: number) => Promise<NoteSnapshotRecord | null>;\n createSnapshot: (input: {\n noteId: number;\n title: string;\n payload: string;\n editSessionId?: string;\n meta?: string;\n }) => Promise<NoteSnapshotRecord>;\n listSnapshots: (noteId: number, limit: number) => Promise<NoteSnapshotRecord[]>;\n findSnapshotById: (id: number) => Promise<NoteSnapshotRecord | null>;\n purgeExpiredSnapshots: (before: Date, limit: number) => Promise<number>;\n trimOverflowSnapshots: (noteId: number, keep: number, limit: number) => Promise<number>;\n resolveRestoredTags?: (content: string) => Promise<ResolvedRestoredSnapshotTags | null>;\n updateNote: (\n id: number,\n input: {\n title: string;\n content: string;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n tagIds?: number[];\n properties?: SnapshotNoteProperty[];\n },\n ) => Promise<NoteRecord>;\n}\n\nexport interface NoteSnapshotMeta {\n entrypoint?: 'web' | 'mobile' | 'mcp';\n label?: string;\n}\n\ninterface NoteSnapshotPayload {\n title: string;\n content: string;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n properties?: SnapshotNoteProperty[];\n}\n\ninterface TagRecord {\n id: number;\n name: string;\n}\n\ninterface ResolvedRestoredSnapshotTags {\n content: string;\n tagIds: number[];\n}\n\ninterface ResolveRestoredSnapshotTagsDeps {\n ensureTagByName: (name: string) => Promise<TagRecord>;\n findTagsByIds: (ids: number[]) => Promise<TagRecord[]>;\n}\n\nexport interface NoteSnapshotSummary {\n id: string;\n title: string;\n createdAt: string;\n meta: NoteSnapshotMeta;\n contentPreview: string;\n contentAsMarkdown?: string;\n payload?: string;\n}\n\nconst parseMeta = (value?: string | null): NoteSnapshotMeta => {\n if (!value) {\n return {};\n }\n\n try {\n const parsed = JSON.parse(value) as NoteSnapshotMeta;\n return {\n ...(parsed.entrypoint ? { entrypoint: parsed.entrypoint } : {}),\n ...(parsed.label ? { label: parsed.label } : {}),\n };\n } catch {\n return {};\n }\n};\n\nconst readSnapshotContent = (payload: string) => {\n try {\n return parsePayload(payload).content;\n } catch {\n return '';\n }\n};\n\nconst buildSnapshotContentPreview = (content: string) => {\n const preview = extractVisibleSearchTextFromContent(content);\n\n if (preview.length <= SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH) {\n return preview;\n }\n\n return `${preview.slice(0, SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH).trimEnd()}...`;\n};\n\nconst serializeSnapshot = (snapshot: NoteSnapshotRecord): NoteSnapshotSummary => ({\n id: String(snapshot.id),\n title: snapshot.title,\n contentPreview: buildSnapshotContentPreview(readSnapshotContent(snapshot.payload)),\n createdAt: snapshot.createdAt.toISOString(),\n meta: parseMeta(snapshot.meta),\n payload: snapshot.payload,\n});\n\nconst serializePayload = (note: NoteRecord): string => {\n const payload: NoteSnapshotPayload = {\n title: note.title,\n content: note.content,\n pinned: note.pinned,\n order: note.order,\n layout: note.layout,\n ...(note.properties\n ? {\n properties: note.properties.map((property) => ({\n ...property,\n ...(property.dateValue instanceof Date ? { dateValue: property.dateValue.toISOString() } : {}),\n })),\n }\n : {}),\n };\n\n return JSON.stringify(payload);\n};\n\nconst hasSameSnapshotPayload = (snapshot: NoteSnapshotRecord | null, payload: string) => {\n return snapshot?.payload === payload;\n};\n\nconst parsePayload = (payload: string): NoteSnapshotPayload => {\n const parsed = JSON.parse(payload) as Partial<NoteSnapshotPayload>;\n\n if (\n typeof parsed.title !== 'string' ||\n typeof parsed.content !== 'string' ||\n typeof parsed.pinned !== 'boolean' ||\n typeof parsed.order !== 'number' ||\n (parsed.layout !== 'narrow' && parsed.layout !== 'wide' && parsed.layout !== 'full')\n ) {\n throw new Error('INVALID_NOTE_SNAPSHOT_PAYLOAD');\n }\n\n return parsed as NoteSnapshotPayload;\n};\n\nconst extractRestoredTagIds = (content: string): number[] | null => {\n try {\n return extractTagIdsFromContentJson(content)\n .map(normalizeRestoredTagId)\n .filter((id): id is number => id !== null);\n } catch {\n return null;\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst normalizeRestoredTagId = (value: unknown) => {\n if (value === undefined || value === null) {\n return null;\n }\n\n const normalized = typeof value === 'string' ? value.trim() : value;\n\n if (normalized === '') {\n return null;\n }\n\n const numericId = Number(normalized);\n return Number.isSafeInteger(numericId) && numericId > 0 ? numericId : null;\n};\n\nconst normalizeRestoredTagName = (value: unknown) => {\n if (typeof value !== 'string') {\n return null;\n }\n\n const normalized = value.trim();\n return normalized ? normalized : null;\n};\n\nexport const resolveRestoredSnapshotTags = async (\n content: string,\n deps: ResolveRestoredSnapshotTagsDeps,\n): Promise<ResolvedRestoredSnapshotTags | null> => {\n const parsed = parseBlockNoteContent(content);\n\n if (!parsed) {\n return null;\n }\n\n const tagRefs: Array<{\n id: number | null;\n name: string | null;\n node: BlockNoteTreeNode;\n props: Record<string, unknown>;\n }> = [];\n\n walkBlockNoteTree(parsed, (node) => {\n if (node.type !== 'tag' || !isRecord(node.props)) {\n return;\n }\n\n tagRefs.push({\n id: normalizeRestoredTagId(node.props.id),\n name: normalizeRestoredTagName(node.props.tag),\n node,\n props: node.props,\n });\n });\n\n if (tagRefs.length === 0) {\n return { content, tagIds: [] };\n }\n\n const candidateIds = Array.from(new Set(tagRefs.map((tag) => tag.id).filter((id): id is number => id !== null)));\n const existingTags = candidateIds.length > 0 ? await deps.findTagsByIds(candidateIds) : [];\n const existingTagIds = new Set(existingTags.map((tag) => tag.id));\n const tagIds = new Set<number>();\n let contentChanged = false;\n\n for (const tagRef of tagRefs) {\n if (tagRef.id !== null && existingTagIds.has(tagRef.id)) {\n tagIds.add(tagRef.id);\n continue;\n }\n\n if (!tagRef.name) {\n continue;\n }\n\n try {\n const resolvedTag = await deps.ensureTagByName(tagRef.name);\n tagRef.node.props = {\n ...tagRef.props,\n id: String(resolvedTag.id),\n tag: resolvedTag.name,\n };\n tagIds.add(resolvedTag.id);\n contentChanged = true;\n } catch {\n continue;\n }\n }\n\n return {\n content: contentChanged ? JSON.stringify(parsed) : content,\n tagIds: [...tagIds],\n };\n};\n\nexport const renderNoteSnapshotContentAsMarkdown = async (payload: string) => {\n try {\n const snapshotPayload = parsePayload(payload);\n return blocksToMarkdown(snapshotPayload.content);\n } catch {\n return '';\n }\n};\n\nconst serializeSnapshotWithContent = async (snapshot: NoteSnapshotRecord): Promise<NoteSnapshotSummary> => {\n const contentAsMarkdown = await renderNoteSnapshotContentAsMarkdown(snapshot.payload);\n\n return {\n ...serializeSnapshot(snapshot),\n contentAsMarkdown,\n };\n};\n\nexport const createSnapshotMetaFromUserAgent = (userAgent?: string | null): string | undefined => {\n const normalized = userAgent?.toLowerCase() ?? '';\n const isMobile = /iphone|ipad|android|mobile/.test(normalized);\n\n return JSON.stringify({\n entrypoint: isMobile ? 'mobile' : 'web',\n label: isMobile ? 'Mobile browser' : 'Web browser',\n } satisfies NoteSnapshotMeta);\n};\n\nexport const MCP_SNAPSHOT_META = JSON.stringify({\n entrypoint: 'mcp',\n label: 'MCP',\n} satisfies NoteSnapshotMeta);\n\nconst defaultPurgeExpiredSnapshots = async (before: Date, limit: number) => {\n const expiredSnapshots = await models.noteSnapshot.findMany({\n where: { createdAt: { lt: before } },\n orderBy: { createdAt: 'asc' },\n take: limit,\n select: { id: true },\n });\n\n if (expiredSnapshots.length === 0) {\n return 0;\n }\n\n const deleted = await models.noteSnapshot.deleteMany({\n where: { id: { in: expiredSnapshots.map((snapshot) => snapshot.id) } },\n });\n\n return deleted.count;\n};\n\nconst defaultTrimOverflowSnapshots = async (noteId: number, keep: number, limit: number) => {\n const overflowSnapshots = await models.noteSnapshot.findMany({\n where: { noteId },\n orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],\n skip: keep,\n take: limit,\n select: { id: true },\n });\n\n if (overflowSnapshots.length === 0) {\n return 0;\n }\n\n const deleted = await models.noteSnapshot.deleteMany({\n where: { id: { in: overflowSnapshots.map((snapshot) => snapshot.id) } },\n });\n\n return deleted.count;\n};\n\nexport const createNoteSnapshotService = (deps: NoteSnapshotDeps) => ({\n captureBaseline: async (input: {\n noteId: number;\n editSessionId?: string;\n meta?: string;\n baseline?: NoteRecord;\n force?: boolean;\n }) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const existing =\n input.editSessionId && !input.force\n ? await deps.findSnapshotByEditSessionId(input.noteId, input.editSessionId)\n : null;\n\n if (existing) {\n return serializeSnapshot(existing);\n }\n\n const note = input.baseline ?? (await deps.findNoteById(input.noteId));\n\n if (!note) {\n return null;\n }\n\n const payload = serializePayload(note);\n const latestSnapshot = await deps.findLatestSnapshot(note.id);\n\n if (latestSnapshot && hasSameSnapshotPayload(latestSnapshot, payload)) {\n return serializeSnapshot(latestSnapshot);\n }\n\n const snapshot = await deps.createSnapshot({\n noteId: note.id,\n title: note.title,\n payload,\n ...(input.editSessionId && !input.force ? { editSessionId: input.editSessionId } : {}),\n ...(input.meta ? { meta: input.meta } : {}),\n });\n\n await deps.trimOverflowSnapshots(note.id, SNAPSHOT_MAX_PER_NOTE, RECOVERY_CLEANUP_BATCH_LIMIT);\n\n return serializeSnapshot(snapshot);\n },\n\n listSnapshots: async (noteId: number, limit = SNAPSHOT_MAX_PER_NOTE) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n await deps.trimOverflowSnapshots(noteId, SNAPSHOT_MAX_PER_NOTE, RECOVERY_CLEANUP_BATCH_LIMIT);\n const snapshots = await deps.listSnapshots(noteId, limit);\n return snapshots.map(serializeSnapshot);\n },\n\n getSnapshot: async (snapshotId: number) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n const snapshot = await deps.findSnapshotById(snapshotId);\n\n if (!snapshot) {\n return null;\n }\n\n return serializeSnapshotWithContent(snapshot);\n },\n\n restoreSnapshot: async (snapshotId: number, options?: { meta?: string }) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const snapshot = await deps.findSnapshotById(snapshotId);\n\n if (!snapshot) {\n return null;\n }\n\n const note = await deps.findNoteById(snapshot.noteId);\n\n if (!note) {\n return null;\n }\n\n const payload = parsePayload(snapshot.payload);\n const resolvedTags = deps.resolveRestoredTags\n ? await deps.resolveRestoredTags(payload.content)\n : extractRestoredTagIds(payload.content);\n const restoredPayload =\n resolvedTags && !Array.isArray(resolvedTags)\n ? {\n ...payload,\n content: resolvedTags.content,\n }\n : payload;\n const restoredTagIds = Array.isArray(resolvedTags) ? resolvedTags : resolvedTags?.tagIds;\n\n const currentPayload = serializePayload(note);\n const latestSnapshot = await deps.findLatestSnapshot(note.id);\n\n if (!hasSameSnapshotPayload(latestSnapshot, currentPayload)) {\n await deps.createSnapshot({\n noteId: note.id,\n title: note.title,\n payload: currentPayload,\n ...(options?.meta ? { meta: options.meta } : {}),\n });\n }\n\n const restoredNote = await deps.updateNote(snapshot.noteId, {\n ...restoredPayload,\n ...(restoredTagIds !== undefined ? { tagIds: restoredTagIds } : {}),\n });\n\n await deps.trimOverflowSnapshots(note.id, SNAPSHOT_MAX_PER_NOTE, RECOVERY_CLEANUP_BATCH_LIMIT);\n\n return restoredNote;\n },\n});\n\nexport const defaultNoteSnapshotService = createNoteSnapshotService({\n findNoteById: async (id) => {\n const note = await models.note.findUnique({\n where: { id },\n include: {\n properties: {\n include: { definition: true, option: true },\n },\n },\n });\n\n if (!note) {\n return null;\n }\n\n return {\n ...note,\n properties: note.properties.map((property) => ({\n key: property.definition.key,\n name: property.definition.name,\n valueType: property.definition.valueType,\n textValue: property.textValue,\n textValueNormalized: property.textValueNormalized,\n numberValue: property.numberValue,\n dateValue: property.dateValue,\n boolValue: property.boolValue,\n optionValue: property.option?.value ?? null,\n optionLabel: property.option?.label ?? null,\n optionColor: property.option?.color ?? null,\n })),\n };\n },\n findSnapshotByEditSessionId: async (noteId, editSessionId) => {\n return models.noteSnapshot.findFirst({\n where: {\n noteId,\n editSessionId,\n },\n });\n },\n findLatestSnapshot: async (noteId) => {\n return models.noteSnapshot.findFirst({\n where: { noteId },\n orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],\n });\n },\n createSnapshot: async (input) => {\n return models.noteSnapshot.create({\n data: {\n noteId: input.noteId,\n title: input.title,\n payload: input.payload,\n ...(input.editSessionId ? { editSessionId: input.editSessionId } : {}),\n ...(input.meta ? { meta: input.meta } : {}),\n },\n });\n },\n purgeExpiredSnapshots: defaultPurgeExpiredSnapshots,\n trimOverflowSnapshots: defaultTrimOverflowSnapshots,\n resolveRestoredTags: async (content) => {\n return resolveRestoredSnapshotTags(content, {\n ensureTagByName: async (name) => {\n const result = await ensureTagByName(name);\n return {\n id: Number(result.tag.id),\n name: result.tag.name,\n };\n },\n findTagsByIds: async (ids) => {\n return models.tag.findMany({\n where: { id: { in: ids } },\n select: {\n id: true,\n name: true,\n },\n });\n },\n });\n },\n listSnapshots: async (noteId, limit) => {\n return models.noteSnapshot.findMany({\n where: { noteId },\n orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],\n take: limit,\n });\n },\n findSnapshotById: async (id) => {\n return models.noteSnapshot.findUnique({ where: { id } });\n },\n updateNote: async (id, input) => {\n const { tagIds, properties, ...noteInput } = input;\n\n return models.$transaction(async (tx) => {\n const note = await tx.note.update({\n where: { id },\n data: {\n ...noteInput,\n ...buildNoteSearchProjection({\n title: noteInput.title,\n content: noteInput.content,\n }),\n ...(tagIds !== undefined ? { tags: { set: tagIds.map((tagId) => ({ id: tagId })) } } : {}),\n },\n });\n\n if (properties !== undefined) {\n await tx.noteProperty.deleteMany({ where: { noteId: id } });\n\n for (const property of properties) {\n const definition = await tx.propertyDefinition.upsert({\n where: { key: property.key },\n create: {\n key: property.key,\n name: property.name,\n valueType: property.valueType,\n },\n update: {\n name: property.name,\n valueType: property.valueType,\n },\n });\n\n const option =\n property.valueType === 'select' && property.optionValue\n ? await tx.propertyOption.upsert({\n where: {\n propertyDefinitionId_value: {\n propertyDefinitionId: definition.id,\n value: property.optionValue,\n },\n },\n create: {\n propertyDefinitionId: definition.id,\n value: property.optionValue,\n label: property.optionLabel ?? property.optionValue,\n color: property.optionColor ?? null,\n },\n update: {\n label: property.optionLabel ?? property.optionValue,\n color: property.optionColor ?? null,\n },\n })\n : null;\n\n await tx.noteProperty.create({\n data: {\n noteId: id,\n propertyDefinitionId: definition.id,\n optionId: option?.id ?? null,\n textValue: property.textValue ?? null,\n textValueNormalized: property.textValueNormalized ?? null,\n numberValue: property.numberValue ?? null,\n dateValue: property.dateValue ? new Date(property.dateValue) : null,\n boolValue: property.boolValue ?? null,\n },\n });\n }\n }\n\n return note;\n });\n },\n});\n\nexport const captureNoteBaseline = async (input: {\n noteId: number;\n editSessionId?: string;\n meta?: string;\n baseline?: NoteRecord;\n force?: boolean;\n}) => {\n return defaultNoteSnapshotService.captureBaseline(input);\n};\n\nexport const listNoteSnapshots = async (noteId: number, limit?: number) => {\n return defaultNoteSnapshotService.listSnapshots(noteId, limit);\n};\n\nexport const getNoteSnapshot = async (snapshotId: number) => {\n return defaultNoteSnapshotService.getSnapshot(snapshotId);\n};\n\nexport const restoreNoteSnapshot = async (snapshotId: number, options?: { meta?: string }) => {\n return defaultNoteSnapshotService.restoreSnapshot(snapshotId, options);\n};\n\nexport const purgeExpiredNoteSnapshots = async () => {\n return defaultPurgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/note/services/snapshot.ts"],"sourcesContent":["import { FILE_HEADERS_ONLY, formatPatch, structuredPatch } from 'diff';\nimport { ensureTagByName } from '~/features/tag/services/organization.js';\nimport models, { type NoteLayout, type PropertyValueType } from '~/models.js';\nimport { blocksToMarkdown, extractTagIdsFromContentJson } from '~/modules/blocknote.js';\nimport { type BlockNoteTreeNode, parseBlockNoteContent, walkBlockNoteTree } from '~/modules/blocknote-tree.js';\nimport {\n createRetentionCutoff,\n RECOVERY_CLEANUP_BATCH_LIMIT,\n SNAPSHOT_MAX_PER_NOTE,\n SNAPSHOT_RETENTION_DAYS,\n} from '~/modules/recovery-retention.js';\nimport { calculateMarkdownSha256 } from './markdown-patch.js';\nimport { buildNoteSearchProjection, extractVisibleSearchTextFromContent } from './search.js';\n\nconst SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH = 240;\n\ninterface NoteRecord {\n id: number;\n title: string;\n content: string;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n updatedAt?: Date;\n properties?: SnapshotNoteProperty[];\n}\n\ninterface SnapshotNoteProperty {\n key: string;\n name: string;\n valueType: PropertyValueType;\n textValue?: string | null;\n textValueNormalized?: string | null;\n numberValue?: number | null;\n dateValue?: Date | string | null;\n boolValue?: boolean | null;\n optionValue?: string | null;\n optionLabel?: string | null;\n optionColor?: string | null;\n}\n\ninterface NoteSnapshotRecord {\n id: number;\n noteId: number;\n title: string;\n payload: string;\n editSessionId: string | null;\n meta: string | null;\n createdAt: Date;\n}\n\ninterface NoteSnapshotDeps {\n findNoteById: (id: number) => Promise<NoteRecord | null>;\n findSnapshotByEditSessionId: (noteId: number, editSessionId: string) => Promise<NoteSnapshotRecord | null>;\n findLatestSnapshot: (noteId: number) => Promise<NoteSnapshotRecord | null>;\n createSnapshot: (input: {\n noteId: number;\n title: string;\n payload: string;\n editSessionId?: string;\n meta?: string;\n }) => Promise<NoteSnapshotRecord>;\n listSnapshots: (noteId: number, limit: number) => Promise<NoteSnapshotRecord[]>;\n findSnapshotById: (id: number) => Promise<NoteSnapshotRecord | null>;\n findNextSnapshot?: (snapshot: NoteSnapshotRecord) => Promise<NoteSnapshotRecord | null>;\n findPreviousSnapshot?: (snapshot: NoteSnapshotRecord) => Promise<NoteSnapshotRecord | null>;\n purgeExpiredSnapshots: (before: Date, limit: number) => Promise<number>;\n trimOverflowSnapshots: (noteId: number, keep: number, limit: number) => Promise<number>;\n resolveRestoredTags?: (content: string) => Promise<ResolvedRestoredSnapshotTags | null>;\n updateNote: (\n id: number,\n input: {\n title: string;\n content: string;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n tagIds?: number[];\n properties?: SnapshotNoteProperty[];\n },\n ) => Promise<NoteRecord>;\n}\n\nexport interface NoteSnapshotMeta {\n entrypoint?: 'web' | 'mobile' | 'mcp';\n label?: string;\n}\n\ninterface NoteSnapshotPayload {\n title: string;\n content: string;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n properties?: SnapshotNoteProperty[];\n}\n\ninterface TagRecord {\n id: number;\n name: string;\n}\n\ninterface ResolvedRestoredSnapshotTags {\n content: string;\n tagIds: number[];\n}\n\ninterface ResolveRestoredSnapshotTagsDeps {\n ensureTagByName: (name: string) => Promise<TagRecord>;\n findTagsByIds: (ids: number[]) => Promise<TagRecord[]>;\n}\n\nexport interface NoteSnapshotSummary {\n id: string;\n title: string;\n createdAt: string;\n meta: NoteSnapshotMeta;\n contentPreview: string;\n contentAsMarkdown?: string;\n payload?: string;\n}\n\nexport type NoteSnapshotDiffTarget = 'next' | 'previous' | 'current';\n\nexport interface NoteSnapshotDiffEndpoint {\n kind: 'snapshot' | 'current_note';\n id: string;\n title: string;\n createdAt?: string;\n updatedAt?: string;\n meta?: NoteSnapshotMeta;\n}\n\nexport interface NoteSnapshotDiffResult {\n noteId: string;\n mode: 'snapshot_to_snapshot' | 'snapshot_to_current';\n before: NoteSnapshotDiffEndpoint;\n after: NoteSnapshotDiffEndpoint;\n diff: {\n markdown: string;\n changedLineCount: number;\n changedCharCount: number;\n beforeMarkdownSha256: string;\n afterMarkdownSha256: string;\n };\n}\n\nconst parseMeta = (value?: string | null): NoteSnapshotMeta => {\n if (!value) {\n return {};\n }\n\n try {\n const parsed = JSON.parse(value) as NoteSnapshotMeta;\n return {\n ...(parsed.entrypoint ? { entrypoint: parsed.entrypoint } : {}),\n ...(parsed.label ? { label: parsed.label } : {}),\n };\n } catch {\n return {};\n }\n};\n\nconst readSnapshotContent = (payload: string) => {\n try {\n return parsePayload(payload).content;\n } catch {\n return '';\n }\n};\n\nconst readSnapshotTitle = (payload: string, fallback: string) => {\n try {\n return parsePayload(payload).title;\n } catch {\n return fallback;\n }\n};\n\nconst buildSnapshotContentPreview = (content: string) => {\n const preview = extractVisibleSearchTextFromContent(content);\n\n if (preview.length <= SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH) {\n return preview;\n }\n\n return `${preview.slice(0, SNAPSHOT_CONTENT_PREVIEW_MAX_LENGTH).trimEnd()}...`;\n};\n\nconst serializeSnapshot = (snapshot: NoteSnapshotRecord): NoteSnapshotSummary => ({\n id: String(snapshot.id),\n title: snapshot.title,\n contentPreview: buildSnapshotContentPreview(readSnapshotContent(snapshot.payload)),\n createdAt: snapshot.createdAt.toISOString(),\n meta: parseMeta(snapshot.meta),\n payload: snapshot.payload,\n});\n\nconst serializePayload = (note: NoteRecord): string => {\n const payload: NoteSnapshotPayload = {\n title: note.title,\n content: note.content,\n pinned: note.pinned,\n order: note.order,\n layout: note.layout,\n ...(note.properties\n ? {\n properties: note.properties.map((property) => ({\n ...property,\n ...(property.dateValue instanceof Date ? { dateValue: property.dateValue.toISOString() } : {}),\n })),\n }\n : {}),\n };\n\n return JSON.stringify(payload);\n};\n\nconst hasSameSnapshotPayload = (snapshot: NoteSnapshotRecord | null, payload: string) => {\n return snapshot?.payload === payload;\n};\n\nconst parsePayload = (payload: string): NoteSnapshotPayload => {\n const parsed = JSON.parse(payload) as Partial<NoteSnapshotPayload>;\n\n if (\n typeof parsed.title !== 'string' ||\n typeof parsed.content !== 'string' ||\n typeof parsed.pinned !== 'boolean' ||\n typeof parsed.order !== 'number' ||\n (parsed.layout !== 'narrow' && parsed.layout !== 'wide' && parsed.layout !== 'full')\n ) {\n throw new Error('INVALID_NOTE_SNAPSHOT_PAYLOAD');\n }\n\n return parsed as NoteSnapshotPayload;\n};\n\nconst extractRestoredTagIds = (content: string): number[] | null => {\n try {\n return extractTagIdsFromContentJson(content)\n .map(normalizeRestoredTagId)\n .filter((id): id is number => id !== null);\n } catch {\n return null;\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst normalizeRestoredTagId = (value: unknown) => {\n if (value === undefined || value === null) {\n return null;\n }\n\n const normalized = typeof value === 'string' ? value.trim() : value;\n\n if (normalized === '') {\n return null;\n }\n\n const numericId = Number(normalized);\n return Number.isSafeInteger(numericId) && numericId > 0 ? numericId : null;\n};\n\nconst normalizeRestoredTagName = (value: unknown) => {\n if (typeof value !== 'string') {\n return null;\n }\n\n const normalized = value.trim();\n return normalized ? normalized : null;\n};\n\nexport const resolveRestoredSnapshotTags = async (\n content: string,\n deps: ResolveRestoredSnapshotTagsDeps,\n): Promise<ResolvedRestoredSnapshotTags | null> => {\n const parsed = parseBlockNoteContent(content);\n\n if (!parsed) {\n return null;\n }\n\n const tagRefs: Array<{\n id: number | null;\n name: string | null;\n node: BlockNoteTreeNode;\n props: Record<string, unknown>;\n }> = [];\n\n walkBlockNoteTree(parsed, (node) => {\n if (node.type !== 'tag' || !isRecord(node.props)) {\n return;\n }\n\n tagRefs.push({\n id: normalizeRestoredTagId(node.props.id),\n name: normalizeRestoredTagName(node.props.tag),\n node,\n props: node.props,\n });\n });\n\n if (tagRefs.length === 0) {\n return { content, tagIds: [] };\n }\n\n const candidateIds = Array.from(new Set(tagRefs.map((tag) => tag.id).filter((id): id is number => id !== null)));\n const existingTags = candidateIds.length > 0 ? await deps.findTagsByIds(candidateIds) : [];\n const existingTagIds = new Set(existingTags.map((tag) => tag.id));\n const tagIds = new Set<number>();\n let contentChanged = false;\n\n for (const tagRef of tagRefs) {\n if (tagRef.id !== null && existingTagIds.has(tagRef.id)) {\n tagIds.add(tagRef.id);\n continue;\n }\n\n if (!tagRef.name) {\n continue;\n }\n\n try {\n const resolvedTag = await deps.ensureTagByName(tagRef.name);\n tagRef.node.props = {\n ...tagRef.props,\n id: String(resolvedTag.id),\n tag: resolvedTag.name,\n };\n tagIds.add(resolvedTag.id);\n contentChanged = true;\n } catch {\n continue;\n }\n }\n\n return {\n content: contentChanged ? JSON.stringify(parsed) : content,\n tagIds: [...tagIds],\n };\n};\n\nexport const renderNoteSnapshotContentAsMarkdown = async (payload: string) => {\n try {\n const snapshotPayload = parsePayload(payload);\n return blocksToMarkdown(snapshotPayload.content);\n } catch {\n return '';\n }\n};\n\nconst renderNoteRecordContentAsMarkdown = async (note: NoteRecord) => {\n try {\n return blocksToMarkdown(note.content);\n } catch {\n return '';\n }\n};\n\nconst createSnapshotMarkdownDiff = (beforeMarkdown: string, afterMarkdown: string, contextLines: number | null = 3) => {\n const patch = structuredPatch('before.md', 'after.md', beforeMarkdown, afterMarkdown, undefined, undefined, {\n context: contextLines === null ? Number.MAX_SAFE_INTEGER : contextLines,\n });\n const changedLines = patch.hunks.flatMap((hunk) =>\n hunk.lines.filter((line) => (line.startsWith('+') && !line.startsWith('+++')) || line.startsWith('-')),\n );\n\n return {\n markdown: formatPatch(patch, FILE_HEADERS_ONLY).trimEnd(),\n changedLineCount: changedLines.length,\n changedCharCount: changedLines.reduce((total, line) => total + line.slice(1).length, 0),\n };\n};\n\nconst snapshotToDiffEndpoint = (snapshot: NoteSnapshotRecord): NoteSnapshotDiffEndpoint => ({\n kind: 'snapshot',\n id: String(snapshot.id),\n title: readSnapshotTitle(snapshot.payload, snapshot.title),\n createdAt: snapshot.createdAt.toISOString(),\n meta: parseMeta(snapshot.meta),\n});\n\nconst noteToDiffEndpoint = (note: NoteRecord): NoteSnapshotDiffEndpoint => ({\n kind: 'current_note',\n id: String(note.id),\n title: note.title,\n ...(note.updatedAt ? { updatedAt: note.updatedAt.toISOString() } : {}),\n});\n\nconst buildSnapshotDiff = async ({\n beforeSnapshot,\n afterSnapshot,\n afterNote,\n contextLines,\n}: {\n beforeSnapshot: NoteSnapshotRecord;\n afterSnapshot?: NoteSnapshotRecord;\n afterNote?: NoteRecord;\n contextLines?: number | null;\n}): Promise<NoteSnapshotDiffResult | null> => {\n if (!afterSnapshot && !afterNote) {\n return null;\n }\n\n const beforeMarkdown = await renderNoteSnapshotContentAsMarkdown(beforeSnapshot.payload);\n const afterMarkdown = afterSnapshot\n ? await renderNoteSnapshotContentAsMarkdown(afterSnapshot.payload)\n : afterNote\n ? await renderNoteRecordContentAsMarkdown(afterNote)\n : '';\n const diff = createSnapshotMarkdownDiff(beforeMarkdown, afterMarkdown, contextLines ?? 3);\n\n return {\n noteId: String(beforeSnapshot.noteId),\n mode: afterSnapshot ? 'snapshot_to_snapshot' : 'snapshot_to_current',\n before: snapshotToDiffEndpoint(beforeSnapshot),\n after: afterSnapshot ? snapshotToDiffEndpoint(afterSnapshot) : noteToDiffEndpoint(afterNote as NoteRecord),\n diff: {\n markdown: diff.markdown,\n changedLineCount: diff.changedLineCount,\n changedCharCount: diff.changedCharCount,\n beforeMarkdownSha256: calculateMarkdownSha256(beforeMarkdown),\n afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown),\n },\n };\n};\n\nconst serializeSnapshotWithContent = async (snapshot: NoteSnapshotRecord): Promise<NoteSnapshotSummary> => {\n const contentAsMarkdown = await renderNoteSnapshotContentAsMarkdown(snapshot.payload);\n\n return {\n ...serializeSnapshot(snapshot),\n contentAsMarkdown,\n };\n};\n\nexport const createSnapshotMetaFromUserAgent = (userAgent?: string | null): string | undefined => {\n const normalized = userAgent?.toLowerCase() ?? '';\n const isMobile = /iphone|ipad|android|mobile/.test(normalized);\n\n return JSON.stringify({\n entrypoint: isMobile ? 'mobile' : 'web',\n label: isMobile ? 'Mobile browser' : 'Web browser',\n } satisfies NoteSnapshotMeta);\n};\n\nexport const MCP_SNAPSHOT_META = JSON.stringify({\n entrypoint: 'mcp',\n label: 'MCP',\n} satisfies NoteSnapshotMeta);\n\nconst defaultPurgeExpiredSnapshots = async (before: Date, limit: number) => {\n const expiredSnapshots = await models.noteSnapshot.findMany({\n where: { createdAt: { lt: before } },\n orderBy: { createdAt: 'asc' },\n take: limit,\n select: { id: true },\n });\n\n if (expiredSnapshots.length === 0) {\n return 0;\n }\n\n const deleted = await models.noteSnapshot.deleteMany({\n where: { id: { in: expiredSnapshots.map((snapshot) => snapshot.id) } },\n });\n\n return deleted.count;\n};\n\nconst defaultTrimOverflowSnapshots = async (noteId: number, keep: number, limit: number) => {\n const overflowSnapshots = await models.noteSnapshot.findMany({\n where: { noteId },\n orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],\n skip: keep,\n take: limit,\n select: { id: true },\n });\n\n if (overflowSnapshots.length === 0) {\n return 0;\n }\n\n const deleted = await models.noteSnapshot.deleteMany({\n where: { id: { in: overflowSnapshots.map((snapshot) => snapshot.id) } },\n });\n\n return deleted.count;\n};\n\nexport const createNoteSnapshotService = (deps: NoteSnapshotDeps) => ({\n captureBaseline: async (input: {\n noteId: number;\n editSessionId?: string;\n meta?: string;\n baseline?: NoteRecord;\n force?: boolean;\n }) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const existing =\n input.editSessionId && !input.force\n ? await deps.findSnapshotByEditSessionId(input.noteId, input.editSessionId)\n : null;\n\n if (existing) {\n return serializeSnapshot(existing);\n }\n\n const note = input.baseline ?? (await deps.findNoteById(input.noteId));\n\n if (!note) {\n return null;\n }\n\n const payload = serializePayload(note);\n const latestSnapshot = await deps.findLatestSnapshot(note.id);\n\n if (latestSnapshot && hasSameSnapshotPayload(latestSnapshot, payload)) {\n return serializeSnapshot(latestSnapshot);\n }\n\n const snapshot = await deps.createSnapshot({\n noteId: note.id,\n title: note.title,\n payload,\n ...(input.editSessionId && !input.force ? { editSessionId: input.editSessionId } : {}),\n ...(input.meta ? { meta: input.meta } : {}),\n });\n\n await deps.trimOverflowSnapshots(note.id, SNAPSHOT_MAX_PER_NOTE, RECOVERY_CLEANUP_BATCH_LIMIT);\n\n return serializeSnapshot(snapshot);\n },\n\n listSnapshots: async (noteId: number, limit = SNAPSHOT_MAX_PER_NOTE) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n await deps.trimOverflowSnapshots(noteId, SNAPSHOT_MAX_PER_NOTE, RECOVERY_CLEANUP_BATCH_LIMIT);\n const snapshots = await deps.listSnapshots(noteId, limit);\n return snapshots.map(serializeSnapshot);\n },\n\n getSnapshot: async (snapshotId: number) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n const snapshot = await deps.findSnapshotById(snapshotId);\n\n if (!snapshot) {\n return null;\n }\n\n return serializeSnapshotWithContent(snapshot);\n },\n\n diffSnapshot: async (\n snapshotId: number,\n options: {\n compareToSnapshotId?: number;\n target?: NoteSnapshotDiffTarget;\n contextLines?: number | null;\n } = {},\n ) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n const snapshot = await deps.findSnapshotById(snapshotId);\n\n if (!snapshot) {\n return null;\n }\n\n if (options.compareToSnapshotId !== undefined) {\n const comparedSnapshot = await deps.findSnapshotById(options.compareToSnapshotId);\n\n if (!comparedSnapshot || comparedSnapshot.noteId !== snapshot.noteId) {\n return null;\n }\n\n return buildSnapshotDiff({\n beforeSnapshot: snapshot,\n afterSnapshot: comparedSnapshot,\n contextLines: options.contextLines,\n });\n }\n\n if (options.target === 'previous') {\n const previousSnapshot = deps.findPreviousSnapshot ? await deps.findPreviousSnapshot(snapshot) : null;\n\n if (!previousSnapshot) {\n return null;\n }\n\n return buildSnapshotDiff({\n beforeSnapshot: previousSnapshot,\n afterSnapshot: snapshot,\n contextLines: options.contextLines,\n });\n }\n\n if (options.target === 'current') {\n const note = await deps.findNoteById(snapshot.noteId);\n\n if (!note) {\n return null;\n }\n\n return buildSnapshotDiff({\n beforeSnapshot: snapshot,\n afterNote: note,\n contextLines: options.contextLines,\n });\n }\n\n const nextSnapshot = deps.findNextSnapshot ? await deps.findNextSnapshot(snapshot) : null;\n\n if (nextSnapshot) {\n return buildSnapshotDiff({\n beforeSnapshot: snapshot,\n afterSnapshot: nextSnapshot,\n contextLines: options.contextLines,\n });\n }\n\n const note = await deps.findNoteById(snapshot.noteId);\n\n if (!note) {\n return null;\n }\n\n return buildSnapshotDiff({\n beforeSnapshot: snapshot,\n afterNote: note,\n contextLines: options.contextLines,\n });\n },\n\n restoreSnapshot: async (snapshotId: number, options?: { meta?: string }) => {\n await deps.purgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const snapshot = await deps.findSnapshotById(snapshotId);\n\n if (!snapshot) {\n return null;\n }\n\n const note = await deps.findNoteById(snapshot.noteId);\n\n if (!note) {\n return null;\n }\n\n const payload = parsePayload(snapshot.payload);\n const resolvedTags = deps.resolveRestoredTags\n ? await deps.resolveRestoredTags(payload.content)\n : extractRestoredTagIds(payload.content);\n const restoredPayload =\n resolvedTags && !Array.isArray(resolvedTags)\n ? {\n ...payload,\n content: resolvedTags.content,\n }\n : payload;\n const restoredTagIds = Array.isArray(resolvedTags) ? resolvedTags : resolvedTags?.tagIds;\n\n const currentPayload = serializePayload(note);\n const latestSnapshot = await deps.findLatestSnapshot(note.id);\n\n if (!hasSameSnapshotPayload(latestSnapshot, currentPayload)) {\n await deps.createSnapshot({\n noteId: note.id,\n title: note.title,\n payload: currentPayload,\n ...(options?.meta ? { meta: options.meta } : {}),\n });\n }\n\n const restoredNote = await deps.updateNote(snapshot.noteId, {\n ...restoredPayload,\n ...(restoredTagIds !== undefined ? { tagIds: restoredTagIds } : {}),\n });\n\n await deps.trimOverflowSnapshots(note.id, SNAPSHOT_MAX_PER_NOTE, RECOVERY_CLEANUP_BATCH_LIMIT);\n\n return restoredNote;\n },\n});\n\nexport const defaultNoteSnapshotService = createNoteSnapshotService({\n findNoteById: async (id) => {\n const note = await models.note.findUnique({\n where: { id },\n include: {\n properties: {\n include: { definition: true, option: true },\n },\n },\n });\n\n if (!note) {\n return null;\n }\n\n return {\n ...note,\n properties: note.properties.map((property) => ({\n key: property.definition.key,\n name: property.definition.name,\n valueType: property.definition.valueType,\n textValue: property.textValue,\n textValueNormalized: property.textValueNormalized,\n numberValue: property.numberValue,\n dateValue: property.dateValue,\n boolValue: property.boolValue,\n optionValue: property.option?.value ?? null,\n optionLabel: property.option?.label ?? null,\n optionColor: property.option?.color ?? null,\n })),\n };\n },\n findSnapshotByEditSessionId: async (noteId, editSessionId) => {\n return models.noteSnapshot.findFirst({\n where: {\n noteId,\n editSessionId,\n },\n });\n },\n findLatestSnapshot: async (noteId) => {\n return models.noteSnapshot.findFirst({\n where: { noteId },\n orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],\n });\n },\n createSnapshot: async (input) => {\n return models.noteSnapshot.create({\n data: {\n noteId: input.noteId,\n title: input.title,\n payload: input.payload,\n ...(input.editSessionId ? { editSessionId: input.editSessionId } : {}),\n ...(input.meta ? { meta: input.meta } : {}),\n },\n });\n },\n purgeExpiredSnapshots: defaultPurgeExpiredSnapshots,\n trimOverflowSnapshots: defaultTrimOverflowSnapshots,\n resolveRestoredTags: async (content) => {\n return resolveRestoredSnapshotTags(content, {\n ensureTagByName: async (name) => {\n const result = await ensureTagByName(name);\n return {\n id: Number(result.tag.id),\n name: result.tag.name,\n };\n },\n findTagsByIds: async (ids) => {\n return models.tag.findMany({\n where: { id: { in: ids } },\n select: {\n id: true,\n name: true,\n },\n });\n },\n });\n },\n listSnapshots: async (noteId, limit) => {\n return models.noteSnapshot.findMany({\n where: { noteId },\n orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],\n take: limit,\n });\n },\n findSnapshotById: async (id) => {\n return models.noteSnapshot.findUnique({ where: { id } });\n },\n findNextSnapshot: async (snapshot) => {\n return models.noteSnapshot.findFirst({\n where: {\n noteId: snapshot.noteId,\n OR: [\n { createdAt: { gt: snapshot.createdAt } },\n {\n createdAt: snapshot.createdAt,\n id: { gt: snapshot.id },\n },\n ],\n },\n orderBy: [{ createdAt: 'asc' }, { id: 'asc' }],\n });\n },\n findPreviousSnapshot: async (snapshot) => {\n return models.noteSnapshot.findFirst({\n where: {\n noteId: snapshot.noteId,\n OR: [\n { createdAt: { lt: snapshot.createdAt } },\n {\n createdAt: snapshot.createdAt,\n id: { lt: snapshot.id },\n },\n ],\n },\n orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],\n });\n },\n updateNote: async (id, input) => {\n const { tagIds, properties, ...noteInput } = input;\n\n return models.$transaction(async (tx) => {\n const note = await tx.note.update({\n where: { id },\n data: {\n ...noteInput,\n ...buildNoteSearchProjection({\n title: noteInput.title,\n content: noteInput.content,\n }),\n ...(tagIds !== undefined ? { tags: { set: tagIds.map((tagId) => ({ id: tagId })) } } : {}),\n },\n });\n\n if (properties !== undefined) {\n await tx.noteProperty.deleteMany({ where: { noteId: id } });\n\n for (const property of properties) {\n const definition = await tx.propertyDefinition.upsert({\n where: { key: property.key },\n create: {\n key: property.key,\n name: property.name,\n valueType: property.valueType,\n },\n update: {\n name: property.name,\n valueType: property.valueType,\n },\n });\n\n const option =\n property.valueType === 'select' && property.optionValue\n ? await tx.propertyOption.upsert({\n where: {\n propertyDefinitionId_value: {\n propertyDefinitionId: definition.id,\n value: property.optionValue,\n },\n },\n create: {\n propertyDefinitionId: definition.id,\n value: property.optionValue,\n label: property.optionLabel ?? property.optionValue,\n color: property.optionColor ?? null,\n },\n update: {\n label: property.optionLabel ?? property.optionValue,\n color: property.optionColor ?? null,\n },\n })\n : null;\n\n await tx.noteProperty.create({\n data: {\n noteId: id,\n propertyDefinitionId: definition.id,\n optionId: option?.id ?? null,\n textValue: property.textValue ?? null,\n textValueNormalized: property.textValueNormalized ?? null,\n numberValue: property.numberValue ?? null,\n dateValue: property.dateValue ? new Date(property.dateValue) : null,\n boolValue: property.boolValue ?? null,\n },\n });\n }\n }\n\n return note;\n });\n },\n});\n\nexport const captureNoteBaseline = async (input: {\n noteId: number;\n editSessionId?: string;\n meta?: string;\n baseline?: NoteRecord;\n force?: boolean;\n}) => {\n return defaultNoteSnapshotService.captureBaseline(input);\n};\n\nexport const listNoteSnapshots = async (noteId: number, limit?: number) => {\n return defaultNoteSnapshotService.listSnapshots(noteId, limit);\n};\n\nexport const getNoteSnapshot = async (snapshotId: number) => {\n return defaultNoteSnapshotService.getSnapshot(snapshotId);\n};\n\nexport const diffNoteSnapshot = async (\n snapshotId: number,\n options?: {\n compareToSnapshotId?: number;\n target?: NoteSnapshotDiffTarget;\n contextLines?: number | null;\n },\n) => {\n return defaultNoteSnapshotService.diffSnapshot(snapshotId, options);\n};\n\nexport const restoreNoteSnapshot = async (snapshotId: number, options?: { meta?: string }) => {\n return defaultNoteSnapshotService.restoreSnapshot(snapshotId, options);\n};\n\nexport const purgeExpiredNoteSnapshots = async () => {\n return defaultPurgeExpiredSnapshots(createRetentionCutoff(SNAPSHOT_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n};\n"],"mappings":"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","names":["note"]}
|