ocean-brain 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/server/client/dist/assets/{ArrowRight.es-C4zlc54Z.js → ArrowRight.es-CbA8ey6a.js} +1 -1
- package/server/client/dist/assets/{Calendar-COrN6Hjv.js → Calendar-CgWgr1BV.js} +1 -1
- package/server/client/dist/assets/{Callout-BK0K9E74.js → Callout-DKruIT0f.js} +1 -1
- package/server/client/dist/assets/{Copy.es-zm8f_XXX.js → Copy.es-DFbgpaNJ.js} +1 -1
- package/server/client/dist/assets/{Graph-BH1fzhue.js → Graph-kCltGRlY.js} +1 -1
- package/server/client/dist/assets/{Image-KXuHOG4b.js → Image-7W0DZwMo.js} +1 -1
- package/server/client/dist/assets/{Image.es-BKahRbIm.js → Image.es-5pXLx9TU.js} +1 -1
- package/server/client/dist/assets/Note-BW6Np_5g.js +20 -0
- package/server/client/dist/assets/{Plus.es-BqeC8T8h.js → Plus.es-B0sg64VJ.js} +1 -1
- package/server/client/dist/assets/{Reminders-BYG5rz1M.js → Reminders-C10fHQcl.js} +1 -1
- package/server/client/dist/assets/{Search-DBWsIMDP.js → Search-tf2nV4v0.js} +1 -1
- package/server/client/dist/assets/{SurfaceCard-CNSQfYfM.js → SurfaceCard-pydtvHfu.js} +1 -1
- package/server/client/dist/assets/{Tag-5aCfSO8Y.js → Tag-uCoYbISy.js} +1 -1
- package/server/client/dist/assets/{TagNotes-BPZjN-6l.js → TagNotes-CqOcQiRw.js} +1 -1
- package/server/client/dist/assets/{Trash.es-CRQkTnXZ.js → Trash.es-w32Goxfa.js} +1 -1
- package/server/client/dist/assets/{ViewNotes-DGLNF96x.js → ViewNotes-slPXXoOY.js} +1 -1
- package/server/client/dist/assets/{ViewSectionDialog-CzskgmMF.js → ViewSectionDialog-brTrsF6G.js} +1 -1
- package/server/client/dist/assets/{Views-DR5jT23G.js → Views-B_wsb-XJ.js} +1 -1
- package/server/client/dist/assets/{graph-vendor-UTvF2NS_.js → graph-vendor-BAF95C0g.js} +1 -1
- package/server/client/dist/assets/{image.api-8dlMwzMd.js → image.api-CYsjXiMr.js} +1 -1
- package/server/client/dist/assets/{index-VHp17GQX.js → index-BR05aSXr.js} +4 -4
- package/server/client/dist/assets/{index-DGIAh3ZY.js → index-D83QbOcw.js} +1 -1
- package/server/client/dist/assets/{index-BEUqDuAZ.css → index-DPI6YoDt.css} +1 -1
- package/server/client/dist/assets/{manage-image-RqtWKas2.js → manage-image-BYIhWu56.js} +1 -1
- package/server/client/dist/assets/{manage-image-detail-BTSIbLlQ.js → manage-image-detail-6KgmQvbN.js} +1 -1
- package/server/client/dist/assets/{mcp-EDurbggR.js → mcp-Dd98N9Hq.js} +2 -2
- package/server/client/dist/assets/{mcp-admin-adapter-DmGUo5G8.js → mcp-admin-adapter-DOgBsYpl.js} +1 -1
- package/server/client/dist/assets/note-core-CMGUaW1v.js +176 -0
- package/server/client/dist/assets/note-vendor-Hoz9Q4-4.js +54 -0
- package/server/client/dist/assets/{placeholder-DeKgSBnC.js → placeholder-siHGg9aK.js} +4 -4
- package/server/client/dist/assets/{properties-BjrZvOYn.js → properties-CyVRKgcT.js} +1 -1
- package/server/client/dist/assets/{tag.api-Dl8DQd0m.js → tag.api-CwmC8crB.js} +1 -1
- package/server/client/dist/assets/{trash-C5ntddLu.js → trash-wojQBK9Z.js} +1 -1
- package/server/client/dist/assets/{useReminderMutate-CoXXqJY2.js → useReminderMutate-DuVKxpBh.js} +1 -1
- package/server/client/dist/index.html +5 -5
- package/server/dist/app.js +0 -1
- package/server/dist/features/auth/http/api.js +0 -1
- package/server/dist/features/auth/http/login-page.js +272 -5
- package/server/dist/features/auth/http/pages.js +20 -3
- package/server/dist/features/auth/service.js +3 -2
- package/server/dist/features/cache/graphql/cache.field.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.mutation.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.query.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.resolver.js +0 -1
- package/server/dist/features/cache/graphql/cache.type-defs.js +0 -1
- package/server/dist/features/cache/graphql/index.js +0 -1
- package/server/dist/features/image/graphql/image.field.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.mutation.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.query.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.resolver.js +0 -1
- package/server/dist/features/image/graphql/image.type-defs.js +0 -1
- package/server/dist/features/image/graphql/index.js +0 -1
- package/server/dist/features/image/http/upload.js +0 -1
- package/server/dist/features/image/services/delete.js +0 -1
- package/server/dist/features/image/services/upload.js +0 -1
- package/server/dist/features/image/services/validation.js +0 -1
- package/server/dist/features/mcp-admin/http/handlers.js +0 -1
- package/server/dist/features/mcp-admin/service.js +0 -1
- package/server/dist/features/note/graphql/index.js +0 -1
- package/server/dist/features/note/graphql/note.field.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.query.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.resolver.js +0 -1
- package/server/dist/features/note/graphql/note.type-defs.js +0 -1
- package/server/dist/features/note/http/mcp.js +0 -1
- package/server/dist/features/note/services/authoring.js +0 -1
- package/server/dist/features/note/services/cleanup.js +0 -1
- package/server/dist/features/note/services/content-blocks.js +0 -1
- package/server/dist/features/note/services/markdown-intent-write.js +0 -1
- package/server/dist/features/note/services/markdown-patch.js +0 -1
- package/server/dist/features/note/services/properties.js +0 -1
- package/server/dist/features/note/services/search-backfill.js +0 -1
- package/server/dist/features/note/services/search.js +0 -1
- package/server/dist/features/note/services/snapshot.js +0 -1
- package/server/dist/features/note/services/tag-filter.js +0 -1
- package/server/dist/features/note/services/trash.js +0 -1
- package/server/dist/features/note/services/write-conflict.js +0 -1
- package/server/dist/features/note/services/write.js +0 -1
- package/server/dist/features/placeholder/graphql/index.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.resolver.js +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.type-defs.js +0 -1
- package/server/dist/features/reminder/graphql/index.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.field.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.query.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.resolver.js +0 -1
- package/server/dist/features/reminder/graphql/reminder.type-defs.js +0 -1
- package/server/dist/features/tag/graphql/index.js +0 -1
- package/server/dist/features/tag/graphql/tag.field.resolver.js +0 -1
- package/server/dist/features/tag/graphql/tag.mutation.resolver.js +0 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js +0 -1
- package/server/dist/features/tag/graphql/tag.resolver.js +0 -1
- package/server/dist/features/tag/graphql/tag.type-defs.js +0 -1
- package/server/dist/features/tag/http/mcp.js +0 -1
- package/server/dist/features/tag/services/organization.js +0 -1
- package/server/dist/features/view/graphql/index.js +0 -1
- package/server/dist/features/view/graphql/view.mutation.resolver.js +0 -1
- package/server/dist/features/view/graphql/view.query.resolver.js +0 -1
- package/server/dist/features/view/graphql/view.resolver.js +0 -1
- package/server/dist/features/view/graphql/view.type-defs.js +0 -1
- package/server/dist/features/view/services/workspace.js +0 -1
- package/server/dist/main.js +0 -1
- package/server/dist/models.js +0 -1
- package/server/dist/modules/app-version.js +0 -1
- package/server/dist/modules/auth-guard.js +0 -1
- package/server/dist/modules/auth-mode.js +0 -1
- package/server/dist/modules/auth-redirect.js +0 -1
- package/server/dist/modules/blocknote-markdown-editor.js +53 -0
- package/server/dist/modules/blocknote-tree.js +0 -1
- package/server/dist/modules/blocknote.js +10 -4
- package/server/dist/modules/data-maintenance.js +0 -1
- package/server/dist/modules/error-handler.js +0 -1
- package/server/dist/modules/graphql.js +0 -1
- package/server/dist/modules/logger.js +0 -1
- package/server/dist/modules/mcp-auth.js +0 -1
- package/server/dist/modules/mcp-token.js +0 -1
- package/server/dist/modules/prisma-runtime.js +0 -1
- package/server/dist/modules/rate-limit.js +0 -1
- package/server/dist/modules/recovery-retention.js +0 -1
- package/server/dist/modules/server-events-handler.js +0 -1
- package/server/dist/modules/server-events.js +0 -1
- package/server/dist/modules/session-store.js +0 -1
- package/server/dist/modules/use-async.js +0 -1
- package/server/dist/paths.js +0 -1
- package/server/dist/routes/api.js +0 -1
- package/server/dist/routes/auth-pages.js +0 -1
- package/server/dist/routes/client.js +0 -1
- package/server/dist/routes/graphql.js +0 -1
- package/server/dist/routes/index.js +0 -1
- package/server/dist/routes/mcp.js +0 -1
- package/server/dist/schema/index.js +0 -1
- package/server/dist/start.js +0 -1
- package/server/dist/types/index.js +0 -1
- package/server/dist/types/input.js +0 -1
- package/server/client/dist/assets/Note-C143cKKw.js +0 -12
- package/server/client/dist/assets/note-core-BklwyZHk.js +0 -180
- package/server/client/dist/assets/note-vendor-DkYKUmLo.js +0 -54
- package/server/client/dist/fonts/Pretendard-Bold.woff +0 -0
- package/server/client/dist/fonts/Pretendard-Regular.woff +0 -0
- package/server/dist/app.js.map +0 -1
- package/server/dist/features/auth/http/api.js.map +0 -1
- package/server/dist/features/auth/http/login-page.js.map +0 -1
- package/server/dist/features/auth/http/pages.js.map +0 -1
- package/server/dist/features/auth/service.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.field.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.mutation.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.query.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.resolver.js.map +0 -1
- package/server/dist/features/cache/graphql/cache.type-defs.js.map +0 -1
- package/server/dist/features/cache/graphql/index.js.map +0 -1
- package/server/dist/features/image/graphql/image.field.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.mutation.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.query.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.resolver.js.map +0 -1
- package/server/dist/features/image/graphql/image.type-defs.js.map +0 -1
- package/server/dist/features/image/graphql/index.js.map +0 -1
- package/server/dist/features/image/http/upload.js.map +0 -1
- package/server/dist/features/image/services/delete.js.map +0 -1
- package/server/dist/features/image/services/upload.js.map +0 -1
- package/server/dist/features/image/services/validation.js.map +0 -1
- package/server/dist/features/mcp-admin/http/handlers.js.map +0 -1
- package/server/dist/features/mcp-admin/service.js.map +0 -1
- package/server/dist/features/note/graphql/index.js.map +0 -1
- package/server/dist/features/note/graphql/note.field.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.query.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.resolver.js.map +0 -1
- package/server/dist/features/note/graphql/note.type-defs.js.map +0 -1
- package/server/dist/features/note/http/mcp.js.map +0 -1
- package/server/dist/features/note/services/authoring.js.map +0 -1
- package/server/dist/features/note/services/cleanup.js.map +0 -1
- package/server/dist/features/note/services/content-blocks.js.map +0 -1
- package/server/dist/features/note/services/markdown-intent-write.js.map +0 -1
- package/server/dist/features/note/services/markdown-patch.js.map +0 -1
- package/server/dist/features/note/services/properties.js.map +0 -1
- package/server/dist/features/note/services/search-backfill.js.map +0 -1
- package/server/dist/features/note/services/search.js.map +0 -1
- package/server/dist/features/note/services/snapshot.js.map +0 -1
- package/server/dist/features/note/services/tag-filter.js.map +0 -1
- package/server/dist/features/note/services/trash.js.map +0 -1
- package/server/dist/features/note/services/write-conflict.js.map +0 -1
- package/server/dist/features/note/services/write.js.map +0 -1
- package/server/dist/features/placeholder/graphql/index.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.field.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.mutation.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.query.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.resolver.js.map +0 -1
- package/server/dist/features/placeholder/graphql/placeholder.type-defs.js.map +0 -1
- package/server/dist/features/reminder/graphql/index.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.field.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.mutation.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.query.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.resolver.js.map +0 -1
- package/server/dist/features/reminder/graphql/reminder.type-defs.js.map +0 -1
- package/server/dist/features/tag/graphql/index.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.field.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.mutation.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.resolver.js.map +0 -1
- package/server/dist/features/tag/graphql/tag.type-defs.js.map +0 -1
- package/server/dist/features/tag/http/mcp.js.map +0 -1
- package/server/dist/features/tag/services/organization.js.map +0 -1
- package/server/dist/features/view/graphql/index.js.map +0 -1
- package/server/dist/features/view/graphql/view.mutation.resolver.js.map +0 -1
- package/server/dist/features/view/graphql/view.query.resolver.js.map +0 -1
- package/server/dist/features/view/graphql/view.resolver.js.map +0 -1
- package/server/dist/features/view/graphql/view.type-defs.js.map +0 -1
- package/server/dist/features/view/services/workspace.js.map +0 -1
- package/server/dist/main.js.map +0 -1
- package/server/dist/models.js.map +0 -1
- package/server/dist/modules/app-version.js.map +0 -1
- package/server/dist/modules/auth-guard.js.map +0 -1
- package/server/dist/modules/auth-mode.js.map +0 -1
- package/server/dist/modules/auth-redirect.js.map +0 -1
- package/server/dist/modules/blocknote-tree.js.map +0 -1
- package/server/dist/modules/blocknote.js.map +0 -1
- package/server/dist/modules/data-maintenance.js.map +0 -1
- package/server/dist/modules/error-handler.js.map +0 -1
- package/server/dist/modules/graphql.js.map +0 -1
- package/server/dist/modules/logger.js.map +0 -1
- package/server/dist/modules/mcp-auth.js.map +0 -1
- package/server/dist/modules/mcp-token.js.map +0 -1
- package/server/dist/modules/prisma-runtime.js.map +0 -1
- package/server/dist/modules/rate-limit.js.map +0 -1
- package/server/dist/modules/recovery-retention.js.map +0 -1
- package/server/dist/modules/server-events-handler.js.map +0 -1
- package/server/dist/modules/server-events.js.map +0 -1
- package/server/dist/modules/session-store.js.map +0 -1
- package/server/dist/modules/use-async.js.map +0 -1
- package/server/dist/paths.js.map +0 -1
- package/server/dist/routes/api.js.map +0 -1
- package/server/dist/routes/auth-pages.js.map +0 -1
- package/server/dist/routes/client.js.map +0 -1
- package/server/dist/routes/graphql.js.map +0 -1
- package/server/dist/routes/index.js.map +0 -1
- package/server/dist/routes/mcp.js.map +0 -1
- package/server/dist/schema/index.js.map +0 -1
- package/server/dist/start.js.map +0 -1
- package/server/dist/types/index.js.map +0 -1
- package/server/dist/types/input.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/markdown-intent-write.ts"],"sourcesContent":["import models, { type NoteLayout } from '~/models.js';\nimport {\n blocksToMarkdown,\n countReferenceInlinesFromContentJson,\n extractLiteralAngleBracketTextTokens,\n extractTagIdsFromContentJson,\n hasLiteralAngleBracketTextTokenLoss,\n hasNumericTildeRangeMarkerLoss,\n hasNumericTildeRangeMarkers,\n hasUnsupportedMarkdownBlocks,\n markdownToBlocksJson,\n} from '~/modules/blocknote.js';\nimport type {\n MarkdownAppendPlanResult,\n MarkdownChangeFailure,\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 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}\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}\n\nexport interface ReplaceNoteMarkdownInput {\n id: number;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownWritePolicy;\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 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 | AppliedMarkdownWriteResult\n | MarkdownChangeFailure\n | Extract<MarkdownPatchPlanResult, { status: 'needs_disambiguation' }>;\n\nexport type MetadataUpdateResult = 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 toTagIds = (tagIds: string[]) => {\n return tagIds.map((tagId) => Number(tagId)).filter((tagId) => Number.isSafeInteger(tagId) && tagId > 0);\n};\n\nconst baselineMismatchFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message:\n 'The markdown write baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n});\n\nconst missingBaselineFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n});\n\nconst unsupportedMarkdownStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'UNSUPPORTED_MARKDOWN_STRUCTURE',\n message: 'This note contains BlockNote content that cannot be safely represented as Markdown.',\n});\n\nconst referenceStructureFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'REFERENCE_STRUCTURE_DECREASED',\n message: 'The markdown write would reduce structured note reference links.',\n});\n\nconst markdownImportLossyFailure = (): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'MARKDOWN_IMPORT_LOSSY',\n message: 'The markdown write would lose literal text during Markdown import.',\n});\n\nconst invalidPropertyInputFailure = (error: InvalidNotePropertyInputError): MarkdownChangeFailure => ({\n status: 'failed',\n reason: 'INVALID_PROPERTY_INPUT',\n message: error.message,\n});\n\nconst mapGuardedWriteError = (error: unknown): MarkdownChangeFailure | null => {\n if (isNoteVersionConflictError(error) || isInvalidNoteVersionError(error)) {\n return baselineMismatchFailure();\n }\n\n if (isMissingNoteVersionError(error)) {\n return missingBaselineFailure();\n }\n\n return null;\n};\n\nconst resolveMetadataPropertyPatch = async (\n deps: MarkdownIntentWriteDeps,\n patch: NotePropertiesByKeyPatchInput | undefined,\n): Promise<NotePropertiesPatchInput | MarkdownChangeFailure | undefined> => {\n if (patch === undefined) {\n return undefined;\n }\n\n if (!deps.resolvePropertyPatch || !deps.validatePropertyPatch) {\n throw new Error('Metadata property patch dependencies are not configured.');\n }\n\n try {\n const resolvedPatch = await deps.resolvePropertyPatch(patch);\n return deps.validatePropertyPatch(resolvedPatch);\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n return invalidPropertyInputFailure(error);\n }\n\n throw error;\n }\n};\n\nconst noteVersionMatches = (expectedUpdatedAt: string | undefined, currentUpdatedAt: Date) => {\n try {\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n return expectedTimestamp !== null && expectedTimestamp === currentUpdatedAt.getTime();\n } catch {\n return false;\n }\n};\n\nconst getUnsupportedSourceFailure = (deps: MarkdownIntentWriteDeps, note: MarkdownIntentNoteRecord) => {\n if (deps.hasUnsupportedMarkdownBlocks?.(note.content)) {\n return unsupportedMarkdownStructureFailure();\n }\n\n return null;\n};\n\nconst applyMarkdownPlan = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: Extract<MarkdownPatchPlanResult, { status: 'planned' }>;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<AppliedMarkdownWriteResult | MarkdownChangeFailure> => {\n const content = await deps.parseMarkdownToContentJson(input.plan.afterMarkdown);\n const literalAngleBracketTokens = extractLiteralAngleBracketTextTokens(input.plan.afterMarkdown);\n const shouldCheckMarkdownImportLoss =\n literalAngleBracketTokens.length > 0 || hasNumericTildeRangeMarkers(input.plan.afterMarkdown);\n\n if (shouldCheckMarkdownImportLoss) {\n const importedMarkdown = await deps.renderMarkdown(content);\n\n if (\n hasLiteralAngleBracketTextTokenLoss(input.plan.afterMarkdown, importedMarkdown) ||\n hasNumericTildeRangeMarkerLoss(input.plan.afterMarkdown, importedMarkdown)\n ) {\n return markdownImportLossyFailure();\n }\n }\n\n const beforeReferenceCount = deps.countReferenceInlines?.(input.beforeContentJson) ?? 0;\n const afterReferenceCount = deps.countReferenceInlines?.(content) ?? 0;\n\n if (input.policy?.preserveReferences === true && afterReferenceCount < beforeReferenceCount) {\n return referenceStructureFailure();\n }\n\n const tagIds = toTagIds(deps.extractTagIds(content));\n let updateResult: GuardedNoteWriteResult | null;\n\n try {\n updateResult = await deps.updateNote({\n id: input.noteId,\n data: {\n content,\n tagIds,\n },\n expectedUpdatedAt: input.expectedUpdatedAt ?? input.noteUpdatedAt,\n snapshotMeta: MCP_SNAPSHOT_META,\n ...(input.force ? { force: true } : {}),\n });\n } catch (error) {\n const mappedError = mapGuardedWriteError(error);\n\n if (mappedError) {\n return mappedError;\n }\n\n throw error;\n }\n\n if (!updateResult) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n return {\n status: 'applied',\n note: {\n id: String(updateResult.note.id),\n updatedAt: updateResult.note.updatedAt.toISOString(),\n },\n change: {\n summary: input.summary,\n changedLineCount: input.plan.proposed.changedLineCount,\n changedCharCount: input.plan.proposed.changedCharCount,\n },\n snapshot: serializeSnapshot(updateResult.snapshot),\n };\n};\n\nconst mapPlanResult = async (\n deps: MarkdownIntentWriteDeps,\n input: {\n noteId: number;\n noteUpdatedAt: string;\n beforeContentJson: string;\n plan: MarkdownPatchPlanResult | MarkdownAppendPlanResult | MarkdownReplacePlanResult;\n expectedUpdatedAt?: string;\n policy?: MarkdownWritePolicy;\n summary: string;\n force?: boolean;\n },\n): Promise<MarkdownIntentWriteResult> => {\n if (input.plan.status !== 'planned') {\n return 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 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 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 expectedUpdatedAt: input.expectedUpdatedAt,\n policy: input.policy,\n summary: input.intent,\n });\n },\n\n updateNoteMetadata: async (input: UpdateNoteMetadataInput): Promise<MetadataUpdateResult> => {\n const note = await deps.findNoteById(input.id);\n\n if (!note) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'The requested note was not found.',\n };\n }\n\n if (!noteVersionMatches(input.expectedUpdatedAt, note.updatedAt)) {\n return {\n status: 'failed',\n reason: input.expectedUpdatedAt ? 'BASELINE_MISMATCH' : 'MISSING_BASELINE',\n message:\n 'The metadata update baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n };\n }\n\n const nextTitle = input.title?.trim();\n const resolvedPropertyPatch = await resolveMetadataPropertyPatch(deps, input.properties);\n\n if (resolvedPropertyPatch && 'status' in resolvedPropertyPatch) {\n return resolvedPropertyPatch;\n }\n\n if (input.title !== undefined && !nextTitle) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Note title must not be empty.',\n };\n }\n\n if (\n nextTitle === note.title &&\n (input.layout === undefined || input.layout === note.layout) &&\n resolvedPropertyPatch === undefined\n ) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The metadata update would not change the note.',\n };\n }\n\n const proposed = {\n ...(nextTitle !== undefined ? { title: nextTitle } : {}),\n ...(input.layout !== undefined ? { layout: input.layout } : {}),\n ...(resolvedPropertyPatch ? { properties: resolvedPropertyPatch } : {}),\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: proposed.properties ?? { set: [], deleteKeys: [] },\n expectedUpdatedAt: input.expectedUpdatedAt,\n noteData: {\n ...(proposed.title !== undefined ? { title: proposed.title } : {}),\n ...(proposed.layout !== undefined ? { layout: 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 ...(proposed.title !== undefined ? { title: proposed.title } : {}),\n ...(proposed.layout !== undefined ? { layout: 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) => {\n return defaultMarkdownIntentWriteService.updateNoteMetadata(input);\n};\n\nexport const calculateNoteMarkdownSha256 = (markdown: string) => {\n return calculateMarkdownSha256(markdown);\n};\n"],"mappings":"AAAA,OAAO,YAAiC;AACxC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AASP;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP;AAAA,EACI;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,yBAAyB;AAClC,SAAsC,6CAA6C;AACnF;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AA+IP,MAAM,wBAAwB,OAC1B,MACA,mBACC;AACD,SAAO;AAAA,IACH,IAAI,OAAO,KAAK,EAAE;AAAA,IAClB,OAAO,KAAK;AAAA,IACZ,WAAW,KAAK,UAAU,YAAY;AAAA,IACtC,UAAU,MAAM,eAAe,KAAK,OAAO;AAAA,EAC/C;AACJ;AAEA,MAAM,WAAW,CAAC,UAAqD;AACnE,SAAO,OAAO,UAAU,YAAY,UAAU;AAClD;AAEA,MAAM,oBAAoB,CAAC,aAAsB;AAC7C,QAAM,OAAO,SAAS,QAAQ,KAAK,SAAS,SAAS,IAAI,IAAI,SAAS,OAAO,CAAC;AAC9E,QAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAE5D,SAAO;AAAA,IACH,IAAI,SAAS,QAAQ,KAAK,SAAS,OAAO,SAAY,OAAO,SAAS,EAAE,IAAI;AAAA,IAC5E;AAAA,IACA,WAAW,SAAS,QAAQ,KAAK,OAAO,SAAS,cAAc,WAAW,SAAS,YAAY;AAAA,EACnG;AACJ;AAEA,MAAM,WAAW,CAAC,WAAqB;AACnC,SAAO,OAAO,IAAI,CAAC,UAAU,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,OAAO,cAAc,KAAK,KAAK,QAAQ,CAAC;AAC1G;AAEA,MAAM,0BAA0B,OAA8B;AAAA,EAC1D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SACI;AACR;AAEA,MAAM,yBAAyB,OAA8B;AAAA,EACzD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,sCAAsC,OAA8B;AAAA,EACtE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,4BAA4B,OAA8B;AAAA,EAC5D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,6BAA6B,OAA8B;AAAA,EAC7D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACb;AAEA,MAAM,8BAA8B,CAAC,WAAiE;AAAA,EAClG,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS,MAAM;AACnB;AAEA,MAAM,uBAAuB,CAAC,UAAiD;AAC3E,MAAI,2BAA2B,KAAK,KAAK,0BAA0B,KAAK,GAAG;AACvE,WAAO,wBAAwB;AAAA,EACnC;AAEA,MAAI,0BAA0B,KAAK,GAAG;AAClC,WAAO,uBAAuB;AAAA,EAClC;AAEA,SAAO;AACX;AAEA,MAAM,+BAA+B,OACjC,MACA,UACwE;AACxE,MAAI,UAAU,QAAW;AACrB,WAAO;AAAA,EACX;AAEA,MAAI,CAAC,KAAK,wBAAwB,CAAC,KAAK,uBAAuB;AAC3D,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC9E;AAEA,MAAI;AACA,UAAM,gBAAgB,MAAM,KAAK,qBAAqB,KAAK;AAC3D,WAAO,KAAK,sBAAsB,aAAa;AAAA,EACnD,SAAS,OAAO;AACZ,QAAI,iBAAiB,+BAA+B;AAChD,aAAO,4BAA4B,KAAK;AAAA,IAC5C;AAEA,UAAM;AAAA,EACV;AACJ;AAEA,MAAM,qBAAqB,CAAC,mBAAuC,qBAA2B;AAC1F,MAAI;AACA,UAAM,oBAAoB,iBAAiB,iBAAiB;AAE5D,WAAO,sBAAsB,QAAQ,sBAAsB,iBAAiB,QAAQ;AAAA,EACxF,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,MAAM,8BAA8B,CAAC,MAA+B,SAAmC;AACnG,MAAI,KAAK,+BAA+B,KAAK,OAAO,GAAG;AACnD,WAAO,oCAAoC;AAAA,EAC/C;AAEA,SAAO;AACX;AAEA,MAAM,oBAAoB,OACtB,MACA,UAU8D;AAC9D,QAAM,UAAU,MAAM,KAAK,2BAA2B,MAAM,KAAK,aAAa;AAC9E,QAAM,4BAA4B,qCAAqC,MAAM,KAAK,aAAa;AAC/F,QAAM,gCACF,0BAA0B,SAAS,KAAK,4BAA4B,MAAM,KAAK,aAAa;AAEhG,MAAI,+BAA+B;AAC/B,UAAM,mBAAmB,MAAM,KAAK,eAAe,OAAO;AAE1D,QACI,oCAAoC,MAAM,KAAK,eAAe,gBAAgB,KAC9E,+BAA+B,MAAM,KAAK,eAAe,gBAAgB,GAC3E;AACE,aAAO,2BAA2B;AAAA,IACtC;AAAA,EACJ;AAEA,QAAM,uBAAuB,KAAK,wBAAwB,MAAM,iBAAiB,KAAK;AACtF,QAAM,sBAAsB,KAAK,wBAAwB,OAAO,KAAK;AAErE,MAAI,MAAM,QAAQ,uBAAuB,QAAQ,sBAAsB,sBAAsB;AACzF,WAAO,0BAA0B;AAAA,EACrC;AAEA,QAAM,SAAS,SAAS,KAAK,cAAc,OAAO,CAAC;AACnD,MAAI;AAEJ,MAAI;AACA,mBAAe,MAAM,KAAK,WAAW;AAAA,MACjC,IAAI,MAAM;AAAA,MACV,MAAM;AAAA,QACF;AAAA,QACA;AAAA,MACJ;AAAA,MACA,mBAAmB,MAAM,qBAAqB,MAAM;AAAA,MACpD,cAAc;AAAA,MACd,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,IACzC,CAAC;AAAA,EACL,SAAS,OAAO;AACZ,UAAM,cAAc,qBAAqB,KAAK;AAE9C,QAAI,aAAa;AACb,aAAO;AAAA,IACX;AAEA,UAAM;AAAA,EACV;AAEA,MAAI,CAAC,cAAc;AACf,WAAO;AAAA,MACH,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SAAS;AAAA,IACb;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,QAAQ;AAAA,IACR,MAAM;AAAA,MACF,IAAI,OAAO,aAAa,KAAK,EAAE;AAAA,MAC/B,WAAW,aAAa,KAAK,UAAU,YAAY;AAAA,IACvD;AAAA,IACA,QAAQ;AAAA,MACJ,SAAS,MAAM;AAAA,MACf,kBAAkB,MAAM,KAAK,SAAS;AAAA,MACtC,kBAAkB,MAAM,KAAK,SAAS;AAAA,IAC1C;AAAA,IACA,UAAU,kBAAkB,aAAa,QAAQ;AAAA,EACrD;AACJ;AAEA,MAAM,gBAAgB,OAClB,MACA,UAUqC;AACrC,MAAI,MAAM,KAAK,WAAW,WAAW;AACjC,WAAO,MAAM;AAAA,EACjB;AAEA,SAAO,kBAAkB,MAAM;AAAA,IAC3B,QAAQ,MAAM;AAAA,IACd,eAAe,MAAM;AAAA,IACrB,mBAAmB,MAAM;AAAA,IACzB,MAAM,MAAM;AAAA,IACZ,mBAAmB,MAAM;AAAA,IACzB,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM;AAAA,IACf,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,EACzC,CAAC;AACL;AAEO,MAAM,mCAAmC,CAAC,UAAmC;AAAA,EAChF,mBAAmB,OAAO,UAAsE;AAC5F,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,qBAAqB,4BAA4B,MAAM,IAAI;AAEjE,QAAI,oBAAoB;AACpB,aAAO;AAAA,IACX;AAEA,UAAM,eAAe,MAAM,sBAAsB,MAAM,KAAK,cAAc;AAC1E,UAAM,OAAO,uBAAuB;AAAA,MAChC,MAAM;AAAA,MACN,mBAAmB,MAAM;AAAA,MACzB,oBAAoB,MAAM;AAAA,MAC1B,QAAQ,MAAM;AAAA,MACd,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MACjB,QAAQ,MAAM;AAAA,IAClB,CAAC;AAED,WAAO,cAAc,MAAM;AAAA,MACvB,QAAQ,MAAM;AAAA,MACd,eAAe,aAAa;AAAA,MAC5B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA,mBAAmB,MAAM;AAAA,MACzB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACnB,CAAC;AAAA,EACL;AAAA,EAEA,oBAAoB,OAAO,UAAuE;AAC9F,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,qBAAqB,4BAA4B,MAAM,IAAI;AAEjE,QAAI,oBAAoB;AACpB,aAAO;AAAA,IACX;AAEA,UAAM,eAAe,MAAM,sBAAsB,MAAM,KAAK,cAAc;AAC1E,UAAM,OAAO,wBAAwB;AAAA,MACjC,MAAM;AAAA,MACN,mBAAmB,MAAM;AAAA,MACzB,oBAAoB,MAAM;AAAA,MAC1B,QAAQ,MAAM;AAAA,MACd,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,QAAQ,MAAM;AAAA,IAClB,CAAC;AAED,WAAO,cAAc,MAAM;AAAA,MACvB,QAAQ,MAAM;AAAA,MACd,eAAe,aAAa;AAAA,MAC5B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA,mBAAmB,MAAM;AAAA,MACzB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACnB,CAAC;AAAA,EACL;AAAA,EAEA,qBAAqB,OAAO,UAAwE;AAChG,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,qBAAqB,4BAA4B,MAAM,IAAI;AAEjE,QAAI,oBAAoB;AACpB,aAAO;AAAA,IACX;AAEA,UAAM,eAAe,MAAM,sBAAsB,MAAM,KAAK,cAAc;AAC1E,UAAM,OAAO,yBAAyB;AAAA,MAClC,MAAM;AAAA,MACN,mBAAmB,MAAM;AAAA,MACzB,oBAAoB,MAAM;AAAA,MAC1B,QAAQ,MAAM;AAAA,MACd,aAAa,MAAM;AAAA,MACnB,QAAQ,MAAM;AAAA,IAClB,CAAC;AAED,WAAO,cAAc,MAAM;AAAA,MACvB,QAAQ,MAAM;AAAA,MACd,eAAe,aAAa;AAAA,MAC5B,mBAAmB,KAAK;AAAA,MACxB;AAAA,MACA,mBAAmB,MAAM;AAAA,MACzB,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,IACnB,CAAC;AAAA,EACL;AAAA,EAEA,oBAAoB,OAAO,UAAkE;AACzF,UAAM,OAAO,MAAM,KAAK,aAAa,MAAM,EAAE;AAE7C,QAAI,CAAC,MAAM;AACP,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,QAAI,CAAC,mBAAmB,MAAM,mBAAmB,KAAK,SAAS,GAAG;AAC9D,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ,MAAM,oBAAoB,sBAAsB;AAAA,QACxD,SACI;AAAA,MACR;AAAA,IACJ;AAEA,UAAM,YAAY,MAAM,OAAO,KAAK;AACpC,UAAM,wBAAwB,MAAM,6BAA6B,MAAM,MAAM,UAAU;AAEvF,QAAI,yBAAyB,YAAY,uBAAuB;AAC5D,aAAO;AAAA,IACX;AAEA,QAAI,MAAM,UAAU,UAAa,CAAC,WAAW;AACzC,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,QACI,cAAc,KAAK,UAClB,MAAM,WAAW,UAAa,MAAM,WAAW,KAAK,WACrD,0BAA0B,QAC5B;AACE,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,UAAM,WAAW;AAAA,MACb,GAAI,cAAc,SAAY,EAAE,OAAO,UAAU,IAAI,CAAC;AAAA,MACtD,GAAI,MAAM,WAAW,SAAY,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,MAC7D,GAAI,wBAAwB,EAAE,YAAY,sBAAsB,IAAI,CAAC;AAAA,IACzE;AAEA,QAAI,MAAM,eAAe,QAAW;AAChC,UAAI,CAAC,KAAK,kBAAkB;AACxB,cAAM,IAAI,MAAM,uDAAuD;AAAA,MAC3E;AAEA,UAAIA;AAEJ,UAAI;AACA,QAAAA,gBAAe,MAAM,KAAK,iBAAiB;AAAA,UACvC,IAAI,MAAM;AAAA,UACV,OAAO,SAAS,cAAc,EAAE,KAAK,CAAC,GAAG,YAAY,CAAC,EAAE;AAAA,UACxD,mBAAmB,MAAM;AAAA,UACzB,UAAU;AAAA,YACN,GAAI,SAAS,UAAU,SAAY,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;AAAA,YAChE,GAAI,SAAS,WAAW,SAAY,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;AAAA,UACvE;AAAA,UACA,cAAc;AAAA,QAClB,CAAC;AAAA,MACL,SAAS,OAAO;AACZ,cAAM,cAAc,qBAAqB,KAAK;AAE9C,YAAI,aAAa;AACb,iBAAO;AAAA,QACX;AAEA,YAAI,iBAAiB,+BAA+B;AAChD,iBAAO,4BAA4B,KAAK;AAAA,QAC5C;AAEA,cAAM;AAAA,MACV;AAEA,UAAI,CAACA,eAAc;AACf,eAAO;AAAA,UACH,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS;AAAA,QACb;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,MAAM;AAAA,UACF,IAAI,OAAOA,cAAa,KAAK,EAAE;AAAA,UAC/B,OAAOA,cAAa,KAAK;AAAA,UACzB,QAAQA,cAAa,KAAK;AAAA,UAC1B,WAAWA,cAAa,KAAK,UAAU,YAAY;AAAA,QACvD;AAAA,QACA,UAAU,kBAAkBA,cAAa,QAAQ;AAAA,MACrD;AAAA,IACJ;AAEA,QAAI;AAEJ,QAAI;AACA,qBAAe,MAAM,KAAK,WAAW;AAAA,QACjC,IAAI,MAAM;AAAA,QACV,MAAM;AAAA,UACF,GAAI,SAAS,UAAU,SAAY,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC;AAAA,UAChE,GAAI,SAAS,WAAW,SAAY,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;AAAA,QACvE;AAAA,QACA,mBAAmB,MAAM;AAAA,QACzB,cAAc;AAAA,MAClB,CAAC;AAAA,IACL,SAAS,OAAO;AACZ,YAAM,cAAc,qBAAqB,KAAK;AAE9C,UAAI,aAAa;AACb,eAAO;AAAA,MACX;AAEA,YAAM;AAAA,IACV;AAEA,QAAI,CAAC,cAAc;AACf,aAAO;AAAA,QACH,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACb;AAAA,IACJ;AAEA,WAAO;AAAA,MACH,QAAQ;AAAA,MACR,MAAM;AAAA,QACF,IAAI,OAAO,aAAa,KAAK,EAAE;AAAA,QAC/B,OAAO,aAAa,KAAK;AAAA,QACzB,QAAQ,aAAa,KAAK;AAAA,QAC1B,WAAW,aAAa,KAAK,UAAU,YAAY;AAAA,MACvD;AAAA,MACA,UAAU,kBAAkB,aAAa,QAAQ;AAAA,IACrD;AAAA,EACJ;AACJ;AAEO,MAAM,oCAAoC,iCAAiC;AAAA,EAC9E,cAAc,CAAC,OACX,OAAO,KAAK,WAAW;AAAA,IACnB,OAAO,EAAE,GAAG;AAAA,IACZ,QAAQ;AAAA,MACJ,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,WAAW;AAAA,IACf;AAAA,EACJ,CAAC;AAAA,EACL,gBAAgB;AAAA,EAChB,4BAA4B;AAAA,EAC5B,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB;AAAA,EACA,YAAY;AAAA,EACZ,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,kBAAkB;AACtB,CAAC;AAEM,MAAM,oBAAoB,OAAO,UAAkC;AACtE,SAAO,kCAAkC,kBAAkB,KAAK;AACpE;AAEO,MAAM,qBAAqB,OAAO,UAAmC;AACxE,SAAO,kCAAkC,mBAAmB,KAAK;AACrE;AAEO,MAAM,sBAAsB,OAAO,UAAoC;AAC1E,SAAO,kCAAkC,oBAAoB,KAAK;AACtE;AAEO,MAAM,qBAAqB,OAAO,UAAmC;AACxE,SAAO,kCAAkC,mBAAmB,KAAK;AACrE;AAEO,MAAM,8BAA8B,CAAC,aAAqB;AAC7D,SAAO,wBAAwB,QAAQ;AAC3C;","names":["updateResult"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/markdown-patch.ts"],"sourcesContent":["import crypto from 'crypto';\n\nconst HASH_ALGORITHM = 'sha256';\nconst EXCERPT_CONTEXT_CHARS = 80;\n\nexport interface MarkdownPatchNoteSnapshot {\n id: string;\n title: string;\n updatedAt: string;\n markdown: string;\n}\n\nexport interface ExactTextMarkdownPatchSelector {\n type: 'exact_text';\n text: string;\n before?: string;\n after?: string;\n}\n\nexport interface MatchCandidateMarkdownPatchSelector {\n type: 'match_candidate';\n text: string;\n matchIndex: number;\n lineStart: number;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport type MarkdownPatchSelector = ExactTextMarkdownPatchSelector | MatchCandidateMarkdownPatchSelector;\n\nexport interface ReplaceMarkdownPatchOperation {\n type: 'replace';\n replacement: string;\n}\n\nexport interface InsertBeforeMarkdownPatchOperation {\n type: 'insert_before';\n insertion: string;\n}\n\nexport interface InsertAfterMarkdownPatchOperation {\n type: 'insert_after';\n insertion: string;\n}\n\nexport type MarkdownPatchOperation =\n | ReplaceMarkdownPatchOperation\n | InsertBeforeMarkdownPatchOperation\n | InsertAfterMarkdownPatchOperation;\n\nexport interface MarkdownChangePolicy {\n allowNoop?: boolean;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface BuildMarkdownPatchPlanInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n selector: MarkdownPatchSelector;\n operation: MarkdownPatchOperation;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownAppendPlacementEnd {\n type: 'end';\n}\n\nexport interface MarkdownAppendPlacementAfterHeading {\n type: 'after_heading';\n heading: string;\n level?: number;\n}\n\nexport type MarkdownAppendPlacement = MarkdownAppendPlacementEnd | MarkdownAppendPlacementAfterHeading;\n\nexport interface BuildMarkdownAppendPlanInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n insertion: string;\n placement?: MarkdownAppendPlacement;\n separator?: '\\n\\n' | '\\n';\n policy?: MarkdownChangePolicy;\n}\n\nexport interface BuildMarkdownReplacePlanInput {\n note: MarkdownPatchNoteSnapshot;\n expectedUpdatedAt?: string;\n baseMarkdownSha256?: string;\n intent: string;\n replacement: string;\n policy?: MarkdownChangePolicy;\n}\n\nexport interface MarkdownPatchMatch {\n matchIndex: number;\n lineStart: number;\n lineEnd: number;\n text: string;\n beforeExcerpt?: string;\n afterExcerpt?: string;\n matchSha256: string;\n surroundingHash: string;\n positionHint?: 'first' | 'last';\n}\n\nexport interface MarkdownChangePlan {\n status: 'planned';\n note: {\n id: string;\n title: string;\n updatedAt: string;\n };\n match?: {\n count: 1;\n lineStart: number;\n lineEnd: number;\n selectorType: string;\n matchedTextSha256: string;\n surroundingHash: string;\n };\n placement?: MarkdownAppendPlacement;\n proposed: {\n changedLineCount: number;\n changedCharCount: number;\n beforeMarkdownSha256: string;\n afterMarkdownSha256: string;\n };\n warnings: string[];\n afterMarkdown: string;\n}\n\nexport interface MarkdownPatchNeedsDisambiguation {\n status: 'needs_disambiguation';\n reason: 'TARGET_AMBIGUOUS';\n matches: MarkdownPatchMatch[];\n}\n\nexport interface MarkdownChangeFailure {\n status: 'failed';\n reason:\n | 'ANCHOR_MISMATCH'\n | 'BASELINE_MISMATCH'\n | 'CANDIDATE_MISMATCH'\n | 'EMPTY_INSERTION'\n | 'EMPTY_REPLACEMENT'\n | 'EMPTY_TARGET'\n | 'HEADING_AMBIGUOUS'\n | 'HEADING_NOT_FOUND'\n | 'INVALID_HEADING_LEVEL'\n | 'INVALID_PROPERTY_INPUT'\n | 'MARKDOWN_IMPORT_LOSSY'\n | 'MISSING_BASELINE'\n | 'NOOP'\n | 'REFERENCE_STRUCTURE_DECREASED'\n | 'REFERENCE_TOKEN_DECREASED'\n | 'TAG_TOKEN_DECREASED'\n | 'TARGET_NOT_FOUND'\n | 'UNSUPPORTED_MARKDOWN_STRUCTURE';\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type MarkdownPatchPlanResult = MarkdownChangePlan | MarkdownPatchNeedsDisambiguation | MarkdownChangeFailure;\nexport type MarkdownAppendPlanResult = MarkdownChangePlan | MarkdownChangeFailure;\nexport type MarkdownReplacePlanResult = MarkdownChangePlan | MarkdownChangeFailure;\n\ninterface RawTextMatch {\n matchIndex: number;\n start: number;\n end: number;\n text: string;\n}\n\ninterface HeadingMatch {\n heading: string;\n level: number;\n lineIndex: number;\n start: number;\n nextSectionStart: number;\n}\n\nexport const calculateMarkdownSha256 = (value: string) => {\n return crypto.createHash(HASH_ALGORITHM).update(value, 'utf8').digest('hex');\n};\n\nconst countNewlines = (value: string) => {\n let count = 0;\n\n for (const character of value) {\n if (character === '\\n') {\n count += 1;\n }\n }\n\n return count;\n};\n\nconst getLineRange = (markdown: string, match: RawTextMatch) => {\n const lineStart = countNewlines(markdown.slice(0, match.start)) + 1;\n const lineEnd = lineStart + countNewlines(match.text);\n\n return {\n lineStart,\n lineEnd,\n };\n};\n\nconst buildExcerpt = (markdown: string, start: number, end: number) => {\n const excerpt = markdown.slice(start, end).trim();\n\n return excerpt.length > 0 ? excerpt : undefined;\n};\n\nconst toPatchMatch = (markdown: string, match: RawTextMatch, totalMatches: number): MarkdownPatchMatch => {\n const { lineStart, lineEnd } = getLineRange(markdown, match);\n const beforeStart = Math.max(0, match.start - EXCERPT_CONTEXT_CHARS);\n const afterEnd = Math.min(markdown.length, match.end + EXCERPT_CONTEXT_CHARS);\n const beforeText = markdown.slice(beforeStart, match.start);\n const afterText = markdown.slice(match.end, afterEnd);\n\n return {\n matchIndex: match.matchIndex,\n lineStart,\n lineEnd,\n text: match.text,\n beforeExcerpt: buildExcerpt(markdown, beforeStart, match.start),\n afterExcerpt: buildExcerpt(markdown, match.end, afterEnd),\n matchSha256: calculateMarkdownSha256(match.text),\n surroundingHash: calculateMarkdownSha256(JSON.stringify({ beforeText, targetText: match.text, afterText })),\n ...(match.matchIndex === 0 && totalMatches > 1 ? { positionHint: 'first' } : {}),\n ...(match.matchIndex === totalMatches - 1 && totalMatches > 1 ? { positionHint: 'last' } : {}),\n };\n};\n\nconst findExactTextMatches = (markdown: string, text: string) => {\n const matches: RawTextMatch[] = [];\n let searchStart = 0;\n\n while (searchStart <= markdown.length) {\n const start = markdown.indexOf(text, searchStart);\n\n if (start === -1) {\n break;\n }\n\n matches.push({\n matchIndex: matches.length,\n start,\n end: start + text.length,\n text,\n });\n searchStart = start + text.length;\n }\n\n return matches;\n};\n\nconst splitLines = (value: string) => {\n return value.split('\\n');\n};\n\nconst findSharedLineEdges = (beforeLines: string[], afterLines: string[]) => {\n let prefix = 0;\n const maxPrefix = Math.min(beforeLines.length, afterLines.length);\n\n while (prefix < maxPrefix && beforeLines[prefix] === afterLines[prefix]) {\n prefix += 1;\n }\n\n let suffix = 0;\n const maxSuffix = Math.min(beforeLines.length - prefix, afterLines.length - prefix);\n\n while (\n suffix < maxSuffix &&\n beforeLines[beforeLines.length - 1 - suffix] === afterLines[afterLines.length - 1 - suffix]\n ) {\n suffix += 1;\n }\n\n return {\n prefix,\n suffix,\n };\n};\n\nconst countChangedLines = (beforeMarkdown: string, afterMarkdown: string) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const removedLineCount = beforeLines.length - prefix - suffix;\n const addedLineCount = afterLines.length - prefix - suffix;\n\n return Math.max(removedLineCount, addedLineCount);\n};\n\nconst isMarkdownTokenWhitespace = (character: string) => /\\s/.test(character);\n\nexport const countExplicitTagTokens = (value: string) => {\n let count = 0;\n\n for (let index = 0; index < value.length; index += 1) {\n if (value[index] !== '[' || (value[index + 1] !== '@' && value[index + 1] !== '#')) {\n continue;\n }\n\n let cursor = index + 2;\n let isValid = cursor < value.length;\n\n while (cursor < value.length && value[cursor] !== ']') {\n if (value[cursor] === '[' || isMarkdownTokenWhitespace(value[cursor])) {\n isValid = false;\n break;\n }\n\n cursor += 1;\n }\n\n if (isValid && cursor < value.length && cursor > index + 2) {\n count += 1;\n index = cursor;\n continue;\n }\n\n if (!isValid) {\n index = Math.max(index, cursor - 1);\n }\n }\n\n return count;\n};\n\nexport const countMarkdownReferenceTokens = (value: string) => {\n let count = 0;\n let cursor = 0;\n\n while (cursor < value.length) {\n const start = value.indexOf('[[', cursor);\n\n if (start === -1) {\n break;\n }\n\n const end = value.indexOf(']]', start + 2);\n\n if (end === -1) {\n break;\n }\n\n if (end > start + 2) {\n count += 1;\n }\n\n cursor = end + 2;\n }\n\n return count;\n};\n\nconst parseVersionTimestamp = (value: string) => {\n const timestamp = /^\\d+$/.test(value) ? Number(value) : Date.parse(value);\n\n return Number.isFinite(timestamp) ? timestamp : null;\n};\n\nconst noteVersionsMatch = (expectedUpdatedAt: string, currentUpdatedAt: string) => {\n const expectedTimestamp = parseVersionTimestamp(expectedUpdatedAt);\n const currentTimestamp = parseVersionTimestamp(currentUpdatedAt);\n\n return expectedTimestamp !== null && currentTimestamp !== null && expectedTimestamp === currentTimestamp;\n};\n\nconst getPreservationFailure = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n\n if (policy?.preserveTags === true && afterTagCount < beforeTagCount) {\n return {\n status: 'failed',\n reason: 'TAG_TOKEN_DECREASED',\n message: 'The patch would reduce explicit tag tokens.',\n };\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n\n if (policy?.preserveReferences === true && afterReferenceCount < beforeReferenceCount) {\n return {\n status: 'failed',\n reason: 'REFERENCE_TOKEN_DECREASED',\n message: 'The patch would reduce note reference tokens.',\n };\n }\n\n return null;\n};\n\nconst collectWarnings = (\n beforeMarkdown: string,\n afterMarkdown: string,\n policy: MarkdownChangePolicy | undefined,\n): string[] => {\n const warnings: string[] = [];\n const beforeTagCount = countExplicitTagTokens(beforeMarkdown);\n const afterTagCount = countExplicitTagTokens(afterMarkdown);\n const shouldWarnTags = policy?.preserveTags === undefined || policy.preserveTags === 'warn';\n\n if (shouldWarnTags && afterTagCount < beforeTagCount) {\n warnings.push('TAG_TOKEN_COUNT_DECREASED');\n }\n\n const beforeReferenceCount = countMarkdownReferenceTokens(beforeMarkdown);\n const afterReferenceCount = countMarkdownReferenceTokens(afterMarkdown);\n const shouldWarnReferences = policy?.preserveReferences === undefined || policy.preserveReferences === 'warn';\n\n if (shouldWarnReferences && afterReferenceCount < beforeReferenceCount) {\n warnings.push('REFERENCE_TOKEN_COUNT_DECREASED');\n }\n\n return warnings;\n};\n\nconst validateBaseline = (\n note: MarkdownPatchNoteSnapshot,\n expectedUpdatedAt: string | undefined,\n baseMarkdownSha256: string | undefined,\n): MarkdownChangeFailure | null => {\n const beforeMarkdownSha256 = calculateMarkdownSha256(note.markdown);\n\n if (!expectedUpdatedAt && !baseMarkdownSha256) {\n return {\n status: 'failed',\n reason: 'MISSING_BASELINE',\n message: 'expectedUpdatedAt or baseMarkdownSha256 is required for markdown writes.',\n };\n }\n\n if (\n (expectedUpdatedAt && !noteVersionsMatch(expectedUpdatedAt, note.updatedAt)) ||\n (baseMarkdownSha256 && baseMarkdownSha256 !== beforeMarkdownSha256)\n ) {\n return {\n status: 'failed',\n reason: 'BASELINE_MISMATCH',\n message:\n 'The markdown write baseline does not match the current note. expectedUpdatedAt accepts an ISO datetime string or epoch milliseconds string.',\n };\n }\n\n return null;\n};\n\nconst createChangePlan = ({\n note,\n afterMarkdown,\n changedCharCount,\n policy,\n match,\n placement,\n}: {\n note: MarkdownPatchNoteSnapshot;\n afterMarkdown: string;\n changedCharCount: number;\n policy: MarkdownChangePolicy | undefined;\n match?: MarkdownChangePlan['match'];\n placement?: MarkdownAppendPlacement;\n}): MarkdownChangePlan | MarkdownChangeFailure => {\n if (!policy?.allowNoop && afterMarkdown === note.markdown) {\n return {\n status: 'failed',\n reason: 'NOOP',\n message: 'The markdown write would not change the note.',\n };\n }\n\n const preservationFailure = getPreservationFailure(note.markdown, afterMarkdown, policy);\n\n if (preservationFailure) {\n return preservationFailure;\n }\n\n return {\n status: 'planned',\n note: {\n id: note.id,\n title: note.title,\n updatedAt: note.updatedAt,\n },\n ...(match ? { match } : {}),\n ...(placement ? { placement } : {}),\n proposed: {\n changedLineCount: countChangedLines(note.markdown, afterMarkdown),\n changedCharCount,\n beforeMarkdownSha256: calculateMarkdownSha256(note.markdown),\n afterMarkdownSha256: calculateMarkdownSha256(afterMarkdown),\n },\n warnings: collectWarnings(note.markdown, afterMarkdown, policy),\n afterMarkdown,\n };\n};\n\nconst matchHasAnchors = (markdown: string, match: RawTextMatch, selector: ExactTextMarkdownPatchSelector) => {\n if (selector.before !== undefined && !markdown.slice(0, match.start).endsWith(selector.before)) {\n return false;\n }\n\n if (selector.after !== undefined && !markdown.slice(match.end).startsWith(selector.after)) {\n return false;\n }\n\n return true;\n};\n\nconst selectExactTextMatch = (\n markdown: string,\n selector: ExactTextMarkdownPatchSelector,\n):\n | { match: RawTextMatch; patchMatch: MarkdownPatchMatch }\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Exact text selector must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No exact text match was found in the current note.',\n };\n }\n\n const anchoredMatches = rawMatches.filter((match) => matchHasAnchors(markdown, match, selector));\n\n if (anchoredMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'ANCHOR_MISMATCH',\n message: 'Exact text matches were found, but before/after anchors did not match.',\n };\n }\n\n if (anchoredMatches.length > 1) {\n return {\n status: 'needs_disambiguation',\n reason: 'TARGET_AMBIGUOUS',\n matches: anchoredMatches.map((match) => toPatchMatch(markdown, match, rawMatches.length)),\n };\n }\n\n const match = anchoredMatches[0];\n\n return {\n match,\n patchMatch: toPatchMatch(markdown, match, rawMatches.length),\n };\n};\n\nconst selectMatchCandidate = (\n markdown: string,\n selector: MatchCandidateMarkdownPatchSelector,\n): { match: RawTextMatch; patchMatch: MarkdownPatchMatch } | MarkdownChangeFailure => {\n if (selector.text.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_TARGET',\n message: 'Match candidate selector text must not be empty.',\n };\n }\n\n const rawMatches = findExactTextMatches(markdown, selector.text);\n\n if (rawMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'TARGET_NOT_FOUND',\n message: 'No match candidate text was found in the current note.',\n };\n }\n\n const match = rawMatches.find((candidate) => candidate.matchIndex === selector.matchIndex);\n\n if (!match) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match index does not exist in the current note.',\n };\n }\n\n const patchMatch = toPatchMatch(markdown, match, rawMatches.length);\n const positionMatches = selector.positionHint === undefined || patchMatch.positionHint === selector.positionHint;\n\n if (\n patchMatch.lineStart !== selector.lineStart ||\n patchMatch.matchSha256 !== selector.matchSha256 ||\n patchMatch.surroundingHash !== selector.surroundingHash ||\n !positionMatches\n ) {\n return {\n status: 'failed',\n reason: 'CANDIDATE_MISMATCH',\n message: 'The selected match candidate no longer matches the current note context.',\n };\n }\n\n return {\n match,\n patchMatch,\n };\n};\n\nconst applyPatchOperation = (markdown: string, match: RawTextMatch, operation: MarkdownPatchOperation) => {\n if (operation.type === 'replace') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.replacement}${markdown.slice(match.end)}`,\n changedCharCount: match.text.length + operation.replacement.length,\n };\n }\n\n if (operation.type === 'insert_before') {\n return {\n markdown: `${markdown.slice(0, match.start)}${operation.insertion}${markdown.slice(match.start)}`,\n changedCharCount: operation.insertion.length,\n };\n }\n\n return {\n markdown: `${markdown.slice(0, match.end)}${operation.insertion}${markdown.slice(match.end)}`,\n changedCharCount: operation.insertion.length,\n };\n};\n\nexport const buildMarkdownPatchPlan = (input: BuildMarkdownPatchPlanInput): MarkdownPatchPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const selected =\n input.selector.type === 'exact_text'\n ? selectExactTextMatch(input.note.markdown, input.selector)\n : selectMatchCandidate(input.note.markdown, input.selector);\n\n if ('status' in selected) {\n return selected;\n }\n\n const patched = applyPatchOperation(input.note.markdown, selected.match, input.operation);\n const plan = createChangePlan({\n note: input.note,\n afterMarkdown: patched.markdown,\n changedCharCount: patched.changedCharCount,\n policy: input.policy,\n match: {\n count: 1,\n lineStart: selected.patchMatch.lineStart,\n lineEnd: selected.patchMatch.lineEnd,\n selectorType: input.selector.type,\n matchedTextSha256: selected.patchMatch.matchSha256,\n surroundingHash: selected.patchMatch.surroundingHash,\n },\n });\n\n return plan;\n};\n\nconst parseHeadingLine = (line: string) => {\n const match = /^(#{1,6})\\s+(.+?)\\s*#*\\s*$/.exec(line);\n\n if (!match) {\n return null;\n }\n\n return {\n level: match[1].length,\n heading: match[2].trim(),\n };\n};\n\nconst getLineStartOffsets = (lines: string[]) => {\n const offsets: number[] = [];\n let offset = 0;\n\n for (const line of lines) {\n offsets.push(offset);\n offset += line.length + 1;\n }\n\n return offsets;\n};\n\nconst findHeadingMatches = (markdown: string, placement: MarkdownAppendPlacementAfterHeading) => {\n const lines = splitLines(markdown);\n const offsets = getLineStartOffsets(lines);\n const headingLines = lines\n .map((line, index) => {\n const heading = parseHeadingLine(line);\n\n if (!heading) {\n return null;\n }\n\n return {\n ...heading,\n lineIndex: index,\n start: offsets[index],\n };\n })\n .filter((heading): heading is Omit<HeadingMatch, 'nextSectionStart'> => heading !== null);\n const matches = headingLines.filter((heading) => {\n return (\n heading.heading === placement.heading &&\n (placement.level === undefined || heading.level === placement.level)\n );\n });\n\n return matches.map((match) => {\n const nextHeading = headingLines.find(\n (heading) => heading.lineIndex > match.lineIndex && heading.level <= match.level,\n );\n\n return {\n ...match,\n nextSectionStart: nextHeading?.start ?? markdown.length,\n };\n });\n};\n\nconst appendWithSeparator = (prefix: string, insertion: string, separator: '\\n\\n' | '\\n') => {\n if (prefix.length === 0) {\n return insertion;\n }\n\n return `${prefix}${prefix.endsWith(separator) ? '' : separator}${insertion}`;\n};\n\nconst applyAppendPlacement = (\n markdown: string,\n insertion: string,\n placement: MarkdownAppendPlacement,\n separator: '\\n\\n' | '\\n',\n): { markdown: string; placement: MarkdownAppendPlacement } | MarkdownChangeFailure => {\n if (insertion.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_INSERTION',\n message: 'Append insertion must not be empty.',\n };\n }\n\n if (placement.type === 'end') {\n return {\n markdown: appendWithSeparator(markdown, insertion, separator),\n placement,\n };\n }\n\n if (\n placement.level !== undefined &&\n (!Number.isInteger(placement.level) || placement.level < 1 || placement.level > 6)\n ) {\n return {\n status: 'failed',\n reason: 'INVALID_HEADING_LEVEL',\n message: 'Heading level must be an integer from 1 to 6.',\n };\n }\n\n const headingMatches = findHeadingMatches(markdown, placement);\n\n if (headingMatches.length === 0) {\n return {\n status: 'failed',\n reason: 'HEADING_NOT_FOUND',\n message: 'The requested heading was not found.',\n };\n }\n\n if (headingMatches.length > 1) {\n return {\n status: 'failed',\n reason: 'HEADING_AMBIGUOUS',\n message: 'The requested heading appears more than once.',\n };\n }\n\n const [headingMatch] = headingMatches;\n const prefix = markdown.slice(0, headingMatch.nextSectionStart);\n const suffix = markdown.slice(headingMatch.nextSectionStart);\n const prefixWithInsertion = appendWithSeparator(prefix, insertion, separator);\n const nextMarkdown =\n suffix.length === 0\n ? prefixWithInsertion\n : `${prefixWithInsertion}${prefixWithInsertion.endsWith(separator) ? '' : separator}${suffix}`;\n\n return {\n markdown: nextMarkdown,\n placement,\n };\n};\n\nexport const buildMarkdownAppendPlan = (input: BuildMarkdownAppendPlanInput): MarkdownAppendPlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n const placement = input.placement ?? { type: 'end' as const };\n const separator = input.separator ?? '\\n\\n';\n const appended = applyAppendPlacement(input.note.markdown, input.insertion, placement, separator);\n\n if ('status' in appended) {\n return appended;\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: appended.markdown,\n changedCharCount: input.insertion.length,\n policy: input.policy,\n placement: appended.placement,\n });\n};\n\nexport const buildMarkdownReplacePlan = (input: BuildMarkdownReplacePlanInput): MarkdownReplacePlanResult => {\n const baselineFailure = validateBaseline(input.note, input.expectedUpdatedAt, input.baseMarkdownSha256);\n\n if (baselineFailure) {\n return baselineFailure;\n }\n\n if (input.replacement.length === 0) {\n return {\n status: 'failed',\n reason: 'EMPTY_REPLACEMENT',\n message: 'Replacement markdown must not be empty.',\n };\n }\n\n return createChangePlan({\n note: input.note,\n afterMarkdown: input.replacement,\n changedCharCount: input.note.markdown.length + input.replacement.length,\n policy: input.policy,\n });\n};\n"],"mappings":"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","names":["match"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/properties.ts"],"sourcesContent":["import models, { type Note, type NoteLayout, Prisma, type PropertyValueType } from '~/models.js';\nimport { buildNoteSearchProjection } from './search.js';\nimport { captureNoteBaseline } from './snapshot.js';\nimport { createNoteVersionConflictError, MissingNoteVersionError, parseNoteVersion } from './write-conflict.js';\n\nexport interface SerializedNotePropertyOption {\n id: string;\n label: string;\n value: string;\n color?: string | null;\n order: number;\n}\n\nexport interface SerializedNoteProperty {\n key: string;\n name: string;\n value: string;\n valueType: PropertyValueType;\n option?: SerializedNotePropertyOption | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface SerializedNotePropertyKey {\n key: string;\n name: string;\n valueType: PropertyValueType;\n noteCount: number;\n options: SerializedNotePropertyOption[];\n updatedAt: string;\n}\n\nexport interface SerializedNotePropertyKeysResult {\n totalCount: number;\n keys: SerializedNotePropertyKey[];\n}\n\nexport interface SerializedNotePropertyDeleteResult {\n key: string;\n name: string;\n valueType: PropertyValueType;\n affectedNoteCount: number;\n deleted: boolean;\n}\n\nexport interface NotePropertySetInput {\n key: string;\n name?: string | null;\n value: string;\n valueType: PropertyValueType;\n}\n\nexport interface NotePropertiesPatchInput {\n set?: NotePropertySetInput[] | null;\n deleteKeys?: string[] | null;\n}\n\nexport interface NotePropertySetByKeyInput {\n key: string;\n value: string;\n}\n\nexport interface NotePropertiesByKeyPatchInput {\n set?: NotePropertySetByKeyInput[] | null;\n deleteKeys?: string[] | null;\n}\n\nexport interface NotePropertyOptionInput {\n label: string;\n value?: string | null;\n color?: string | null;\n order?: number | null;\n}\n\nexport interface NotePropertyOptionUpdateInput extends NotePropertyOptionInput {\n id?: string | number | null;\n}\n\nexport interface NotePropertyDefinitionInput {\n key: string;\n name?: string | null;\n valueType: PropertyValueType;\n options?: NotePropertyOptionInput[] | null;\n}\n\nexport interface NotePropertyDefinitionUpdateInput {\n name?: string | null;\n options?: NotePropertyOptionUpdateInput[] | null;\n}\n\ntype NotePropertyWithDefinition = Prisma.NotePropertyGetPayload<{\n include: { definition: true; option: true };\n}>;\n\ntype PropertyDefinitionWithOptions = Prisma.PropertyDefinitionGetPayload<{\n include: { options: true; _count: { select: { properties: true } } };\n}>;\n\ntype PropertyPatchDefinitionLookupClient = Pick<typeof models, 'propertyDefinition'>;\ntype PropertyPatchValueValidationClient = Pick<typeof models, 'propertyDefinition' | 'propertyOption'>;\n\nconst PROPERTY_KEY_MAX_LENGTH = 80;\nconst PROPERTY_NAME_MAX_LENGTH = 120;\nconst PROPERTY_TEXT_VALUE_MAX_LENGTH = 2000;\nconst PROPERTY_OPTION_LABEL_MAX_LENGTH = 80;\nconst PROPERTY_OPTION_VALUE_MAX_LENGTH = 80;\nconst PROPERTY_OPTION_LIMIT = 50;\nconst PROPERTY_PATCH_SET_LIMIT = 50;\nconst PROPERTY_PATCH_DELETE_LIMIT = 50;\n\nexport class InvalidNotePropertyInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidNotePropertyInputError';\n }\n}\n\nexport class NotePropertyDeleteConfirmationRequiredError extends Error {\n code = 'NOTE_PROPERTY_DELETE_CONFIRMATION_REQUIRED' as const;\n affectedNoteCount: number;\n\n constructor(key: string, affectedNoteCount: number) {\n super(`Property ${key} is used by ${affectedNoteCount} note(s). Confirm deletion to remove those values.`);\n this.name = 'NotePropertyDeleteConfirmationRequiredError';\n this.affectedNoteCount = affectedNoteCount;\n }\n}\n\nconst isRecordNotFoundError = (error: unknown) => {\n return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025';\n};\n\nexport const normalizePropertyKey = (value: string) => {\n const normalized = value.trim().toLowerCase().replace(/\\s+/g, '-');\n\n if (!normalized) {\n throw new InvalidNotePropertyInputError('Property key is required.');\n }\n\n if (normalized.length > PROPERTY_KEY_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(`Property key must be ${PROPERTY_KEY_MAX_LENGTH} characters or fewer.`);\n }\n\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(normalized)) {\n throw new InvalidNotePropertyInputError('Property key must use letters, numbers, dashes, or underscores.');\n }\n\n return normalized;\n};\n\nconst normalizePropertyName = (value: string | null | undefined, key: string) => {\n const normalized = value?.trim() || key;\n\n if (normalized.length > PROPERTY_NAME_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property name must be ${PROPERTY_NAME_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n return normalized;\n};\n\nconst normalizeOptionValue = (value: string) => {\n const normalized = value.trim().toLowerCase().replace(/\\s+/g, '-');\n\n if (!normalized) {\n throw new InvalidNotePropertyInputError('Property option value is required.');\n }\n\n if (normalized.length > PROPERTY_OPTION_VALUE_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property option value must be ${PROPERTY_OPTION_VALUE_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(normalized)) {\n throw new InvalidNotePropertyInputError(\n 'Property option value must use letters, numbers, dashes, or underscores.',\n );\n }\n\n return normalized;\n};\n\nconst normalizeOptionLabel = (value: string) => {\n const normalized = value.trim();\n\n if (!normalized) {\n throw new InvalidNotePropertyInputError('Property option label is required.');\n }\n\n if (normalized.length > PROPERTY_OPTION_LABEL_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property option label must be ${PROPERTY_OPTION_LABEL_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n return normalized;\n};\n\nconst normalizePropertyOptions = (options: NotePropertyOptionInput[] | null | undefined) => {\n const normalizedOptions = options ?? [];\n\n if (normalizedOptions.length > PROPERTY_OPTION_LIMIT) {\n throw new InvalidNotePropertyInputError(`Select properties can have up to ${PROPERTY_OPTION_LIMIT} options.`);\n }\n\n const seenValues = new Set<string>();\n\n return normalizedOptions.map((option, index) => {\n const label = normalizeOptionLabel(option.label);\n const value = normalizeOptionValue(option.value ?? label);\n\n if (seenValues.has(value)) {\n throw new InvalidNotePropertyInputError('Property options contain duplicate values.');\n }\n\n seenValues.add(value);\n\n return {\n label,\n value,\n color: option.color?.trim() || null,\n order: Number.isFinite(Number(option.order)) ? Number(option.order) : index,\n };\n });\n};\n\nconst normalizePropertyOptionUpdates = (\n options: NotePropertyOptionUpdateInput[] | null | undefined,\n existingOptions: Array<{ id: number; value: string }>,\n) => {\n const normalizedOptions = options ?? [];\n\n if (normalizedOptions.length > PROPERTY_OPTION_LIMIT) {\n throw new InvalidNotePropertyInputError(`Select properties can have up to ${PROPERTY_OPTION_LIMIT} options.`);\n }\n\n const existingOptionById = new Map(existingOptions.map((option) => [String(option.id), option]));\n const seenValues = new Set<string>();\n\n return normalizedOptions.map((option, index) => {\n const label = normalizeOptionLabel(option.label);\n const existingOption =\n option.id === undefined || option.id === null ? null : existingOptionById.get(String(option.id));\n\n if (option.id !== undefined && option.id !== null && !existingOption) {\n throw new InvalidNotePropertyInputError('Property option does not belong to this property.');\n }\n\n const value = normalizeOptionValue(option.value ?? existingOption?.value ?? label);\n\n if (existingOption && value !== existingOption.value) {\n throw new InvalidNotePropertyInputError(\n `Property option ${existingOption.value} value cannot be changed. Create a new option instead.`,\n );\n }\n\n if (seenValues.has(value)) {\n throw new InvalidNotePropertyInputError('Property options contain duplicate values.');\n }\n\n seenValues.add(value);\n\n return {\n id: existingOption?.id,\n label,\n value,\n color: option.color?.trim() || null,\n order: Number.isFinite(Number(option.order)) ? Number(option.order) : index,\n };\n });\n};\n\nconst normalizeTextValue = (value: string) => {\n if (value.length > PROPERTY_TEXT_VALUE_MAX_LENGTH) {\n throw new InvalidNotePropertyInputError(\n `Property text value must be ${PROPERTY_TEXT_VALUE_MAX_LENGTH} characters or fewer.`,\n );\n }\n\n return value;\n};\n\nexport const normalizeUrlValue = (value: string) => {\n const trimmedValue = normalizeTextValue(value.trim());\n\n if (!trimmedValue) {\n throw new InvalidNotePropertyInputError('URL property value is required.');\n }\n\n let url: URL;\n\n try {\n url = new URL(trimmedValue);\n } catch {\n throw new InvalidNotePropertyInputError('URL property values must be valid http(s) URLs.');\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new InvalidNotePropertyInputError('URL property values must use http or https.');\n }\n\n return url.toString();\n};\n\nconst normalizeDateValue = (value: string) => {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n throw new InvalidNotePropertyInputError('Date property values must use YYYY-MM-DD.');\n }\n\n const date = new Date(`${value}T00:00:00.000Z`);\n\n if (Number.isNaN(date.getTime()) || date.toISOString().slice(0, 10) !== value) {\n throw new InvalidNotePropertyInputError('Date property value is invalid.');\n }\n\n return date;\n};\n\nconst normalizeBooleanValue = (value: string) => {\n if (value === 'true') {\n return true;\n }\n\n if (value === 'false') {\n return false;\n }\n\n throw new InvalidNotePropertyInputError('Boolean property values must be true or false.');\n};\n\nconst normalizeNumberValue = (value: string) => {\n const numberValue = Number(value);\n\n if (!Number.isFinite(numberValue)) {\n throw new InvalidNotePropertyInputError('Number property value must be finite.');\n }\n\n return numberValue;\n};\n\nconst serializeOption = (option: NonNullable<NotePropertyWithDefinition['option']>): SerializedNotePropertyOption => ({\n id: String(option.id),\n label: option.label,\n value: option.value,\n color: option.color,\n order: option.order,\n});\n\nconst serializeDefinitionOption = (\n option: PropertyDefinitionWithOptions['options'][number],\n): SerializedNotePropertyOption => ({\n id: String(option.id),\n label: option.label,\n value: option.value,\n color: option.color,\n order: option.order,\n});\n\nconst serializePropertyValue = (property: NotePropertyWithDefinition) => {\n switch (property.definition.valueType) {\n case 'number':\n return property.numberValue === null ? '' : String(property.numberValue);\n case 'date':\n return property.dateValue ? property.dateValue.toISOString().slice(0, 10) : '';\n case 'boolean':\n return property.boolValue === null ? '' : String(property.boolValue);\n case 'select':\n return property.option?.value ?? '';\n case 'url':\n case 'text':\n default:\n return property.textValue ?? '';\n }\n};\n\nexport const serializeNoteProperty = (property: NotePropertyWithDefinition): SerializedNoteProperty => ({\n key: property.definition.key,\n name: property.definition.name,\n value: serializePropertyValue(property),\n valueType: property.definition.valueType,\n option: property.option ? serializeOption(property.option) : null,\n createdAt: property.createdAt.toISOString(),\n updatedAt: property.updatedAt.toISOString(),\n});\n\nexport const serializeNoteProperties = (properties: NotePropertyWithDefinition[]) => {\n return properties.map(serializeNoteProperty).sort((left, right) => left.key.localeCompare(right.key));\n};\n\nconst serializePropertyDefinition = (definition: PropertyDefinitionWithOptions): SerializedNotePropertyKey => ({\n key: definition.key,\n name: definition.name,\n valueType: definition.valueType,\n noteCount: definition._count.properties,\n options: definition.options.sort((left, right) => left.order - right.order).map(serializeDefinitionOption),\n updatedAt: definition.updatedAt.toISOString(),\n});\n\nexport const assertPropertyOptionUpdateKeepsUsedValues = ({\n existingOptions,\n nextOptions,\n}: {\n existingOptions: Array<{ value: string; _count: { properties: number } }>;\n nextOptions: Array<{ value: string }>;\n}) => {\n const nextValues = new Set(nextOptions.map((option) => option.value));\n const removedUsedOption = existingOptions.find(\n (option) => option._count.properties > 0 && !nextValues.has(option.value),\n );\n\n if (removedUsedOption) {\n throw new InvalidNotePropertyInputError(\n `Property option ${removedUsedOption.value} is used by notes and cannot be removed.`,\n );\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n};\n\nconst parseViewQueryRecord = (value: string | null) => {\n if (!value) {\n return null;\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n return isRecord(parsed) ? parsed : null;\n } catch {\n return null;\n }\n};\n\nconst getNormalizedViewFilterKey = (filter: Record<string, unknown>) => {\n if (typeof filter.key !== 'string') {\n return null;\n }\n\n try {\n return normalizePropertyKey(filter.key);\n } catch {\n return null;\n }\n};\n\nexport const renamePropertyFiltersInViewQuery = ({\n query,\n key,\n name,\n}: {\n query: string | null;\n key: string;\n name: string;\n}) => {\n const parsed = parseViewQueryRecord(query);\n\n if (!parsed || !Array.isArray(parsed.propertyFilters)) {\n return null;\n }\n\n let changed = false;\n const propertyFilters = parsed.propertyFilters.map((filter) => {\n if (!isRecord(filter) || getNormalizedViewFilterKey(filter) !== key) {\n return filter;\n }\n\n changed = true;\n return {\n ...filter,\n key,\n name,\n };\n });\n\n if (!changed) {\n return null;\n }\n\n return JSON.stringify({\n ...parsed,\n propertyFilters,\n });\n};\n\nconst normalizeReferencedOptionValue = (value: unknown) => {\n return typeof value === 'string' ? normalizeOptionValue(value) : null;\n};\n\nexport const findReferencedRemovedPropertyOptionValue = ({\n query,\n key,\n removedValues,\n}: {\n query: string | null;\n key: string;\n removedValues: Set<string>;\n}) => {\n const parsed = parseViewQueryRecord(query);\n\n if (!parsed || !Array.isArray(parsed.propertyFilters)) {\n return null;\n }\n\n for (const filter of parsed.propertyFilters) {\n if (!isRecord(filter) || getNormalizedViewFilterKey(filter) !== key) {\n continue;\n }\n\n if (filter.valueType !== 'select' || filter.operator === 'exists' || filter.operator === 'notExists') {\n continue;\n }\n\n try {\n const optionValue = normalizeReferencedOptionValue(filter.value);\n\n if (optionValue && removedValues.has(optionValue)) {\n return optionValue;\n }\n } catch {\n continue;\n }\n }\n\n return null;\n};\n\nconst buildTypedValueData = async (\n input: NotePropertySetInput,\n definitionId: number,\n tx: Pick<typeof models, 'propertyOption'> = models,\n) => {\n const resetValues = {\n textValue: null,\n textValueNormalized: null,\n numberValue: null,\n dateValue: null,\n boolValue: null,\n optionId: null,\n };\n\n switch (input.valueType) {\n case 'number': {\n const numberValue = normalizeNumberValue(input.value);\n return { ...resetValues, numberValue };\n }\n case 'date': {\n const dateValue = normalizeDateValue(input.value);\n return { ...resetValues, dateValue };\n }\n case 'boolean': {\n const boolValue = normalizeBooleanValue(input.value);\n return { ...resetValues, boolValue };\n }\n case 'select': {\n const optionValue = normalizeOptionValue(input.value);\n const option = await tx.propertyOption.findUnique({\n where: {\n propertyDefinitionId_value: {\n propertyDefinitionId: definitionId,\n value: optionValue,\n },\n },\n });\n\n if (!option) {\n throw new InvalidNotePropertyInputError(`Property option ${optionValue} is not defined.`);\n }\n\n return { ...resetValues, optionId: option.id };\n }\n case 'url': {\n const urlValue = normalizeUrlValue(input.value);\n return { ...resetValues, textValue: urlValue, textValueNormalized: urlValue.toLowerCase() };\n }\n case 'text': {\n const textValue = normalizeTextValue(input.value);\n return { ...resetValues, textValue, textValueNormalized: textValue.toLowerCase() };\n }\n default:\n throw new InvalidNotePropertyInputError('Unsupported property value type.');\n }\n};\n\nconst normalizePatch = (patch: NotePropertiesPatchInput) => {\n const set = patch.set ?? [];\n const deleteKeys = patch.deleteKeys ?? [];\n\n if (set.length === 0 && deleteKeys.length === 0) {\n throw new InvalidNotePropertyInputError('At least one property change is required.');\n }\n\n if (set.length > PROPERTY_PATCH_SET_LIMIT || deleteKeys.length > PROPERTY_PATCH_DELETE_LIMIT) {\n throw new InvalidNotePropertyInputError('Too many property changes in one request.');\n }\n\n const normalizedSet = set.map((item) => {\n const key = normalizePropertyKey(item.key);\n return {\n ...item,\n key,\n name: normalizePropertyName(item.name, key),\n };\n });\n const normalizedDeleteKeys = deleteKeys.map(normalizePropertyKey);\n const seenSetKeys = new Set<string>();\n const seenDeleteKeys = new Set<string>();\n\n for (const item of normalizedSet) {\n if (seenSetKeys.has(item.key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate keys.');\n }\n\n seenSetKeys.add(item.key);\n }\n\n for (const key of normalizedDeleteKeys) {\n if (seenDeleteKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate delete keys.');\n }\n\n if (seenSetKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch cannot set and delete the same key.');\n }\n\n seenDeleteKeys.add(key);\n }\n\n return { set: normalizedSet, deleteKeys: normalizedDeleteKeys };\n};\n\nconst normalizePatchByKey = (patch: NotePropertiesByKeyPatchInput) => {\n const set = patch.set ?? [];\n const deleteKeys = patch.deleteKeys ?? [];\n\n if (set.length === 0 && deleteKeys.length === 0) {\n throw new InvalidNotePropertyInputError('At least one property change is required.');\n }\n\n if (set.length > PROPERTY_PATCH_SET_LIMIT || deleteKeys.length > PROPERTY_PATCH_DELETE_LIMIT) {\n throw new InvalidNotePropertyInputError('Too many property changes in one request.');\n }\n\n const normalizedSet = set.map((item) => ({\n key: normalizePropertyKey(item.key),\n value: item.value,\n }));\n const normalizedDeleteKeys = deleteKeys.map(normalizePropertyKey);\n const seenSetKeys = new Set<string>();\n const seenDeleteKeys = new Set<string>();\n\n for (const item of normalizedSet) {\n if (seenSetKeys.has(item.key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate keys.');\n }\n\n seenSetKeys.add(item.key);\n }\n\n for (const key of normalizedDeleteKeys) {\n if (seenDeleteKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch contains duplicate delete keys.');\n }\n\n if (seenSetKeys.has(key)) {\n throw new InvalidNotePropertyInputError('Property patch cannot set and delete the same key.');\n }\n\n seenDeleteKeys.add(key);\n }\n\n return { set: normalizedSet, deleteKeys: normalizedDeleteKeys };\n};\n\nexport const resolveNotePropertiesPatchValueTypes = async (\n patch: NotePropertiesByKeyPatchInput,\n tx: PropertyPatchDefinitionLookupClient = models,\n): Promise<NotePropertiesPatchInput> => {\n const normalizedPatch = normalizePatchByKey(patch);\n\n if (normalizedPatch.set.length === 0) {\n return normalizePatch({ deleteKeys: normalizedPatch.deleteKeys });\n }\n\n const definitions = await tx.propertyDefinition.findMany({\n where: { key: { in: normalizedPatch.set.map((item) => item.key) } },\n select: {\n key: true,\n name: true,\n valueType: true,\n },\n });\n const definitionByKey = new Map(definitions.map((definition) => [definition.key, definition]));\n\n return normalizePatch({\n set: normalizedPatch.set.map((item) => {\n const definition = definitionByKey.get(item.key);\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} is not defined. Create it in property settings first.`,\n );\n }\n\n return {\n key: item.key,\n name: definition.name,\n value: item.value,\n valueType: definition.valueType,\n };\n }),\n deleteKeys: normalizedPatch.deleteKeys,\n });\n};\n\nexport const validateNotePropertiesPatchValues = async (\n patch: NotePropertiesPatchInput,\n tx: PropertyPatchValueValidationClient = models,\n) => {\n const normalizedPatch = normalizePatch(patch);\n\n for (const item of normalizedPatch.set) {\n const definition = await tx.propertyDefinition.findUnique({ where: { key: item.key } });\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} is not defined. Create it in property settings first.`,\n );\n }\n\n if (definition.valueType !== item.valueType) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} already uses ${definition.valueType} values.`,\n );\n }\n\n await buildTypedValueData(item, definition.id, tx);\n }\n\n return normalizedPatch;\n};\n\nexport const listNoteProperties = async (noteId: number) => {\n const properties = await models.noteProperty.findMany({\n where: { noteId },\n include: { definition: true, option: true },\n orderBy: [{ definition: { key: 'asc' } }],\n });\n\n return serializeNoteProperties(properties);\n};\n\nexport const listNotePropertyKeys = async (input?: { query?: string; limit?: number; offset?: number }) => {\n const normalizedQuery = input?.query?.trim();\n const where = normalizedQuery\n ? {\n OR: [{ key: { contains: normalizedQuery } }, { name: { contains: normalizedQuery } }],\n }\n : undefined;\n const take = Math.max(1, Math.min(100, Number(input?.limit ?? 50)));\n const skip = Math.max(0, Number(input?.offset ?? 0));\n const [definitions, totalCount] = await Promise.all([\n models.propertyDefinition.findMany({\n where,\n orderBy: { key: 'asc' },\n take,\n skip,\n include: { options: { orderBy: { order: 'asc' } }, _count: { select: { properties: true } } },\n }),\n models.propertyDefinition.count({ where }),\n ]);\n\n return {\n totalCount,\n keys: definitions.map(serializePropertyDefinition),\n } satisfies SerializedNotePropertyKeysResult;\n};\n\nexport const createNotePropertyDefinition = async (input: NotePropertyDefinitionInput) => {\n const key = normalizePropertyKey(input.key);\n const name = normalizePropertyName(input.name, key);\n const options = normalizePropertyOptions(input.options);\n\n if (input.valueType !== 'select' && options.length > 0) {\n throw new InvalidNotePropertyInputError('Only select properties can have options.');\n }\n\n if (input.valueType === 'select' && options.length === 0) {\n throw new InvalidNotePropertyInputError('Select properties require at least one option.');\n }\n\n try {\n const definition = await models.propertyDefinition.create({\n data: {\n key,\n name,\n valueType: input.valueType,\n ...(input.valueType === 'select'\n ? {\n options: {\n create: options,\n },\n }\n : {}),\n },\n include: { options: { orderBy: { order: 'asc' } }, _count: { select: { properties: true } } },\n });\n\n return serializePropertyDefinition(definition);\n } catch (error) {\n if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {\n throw new InvalidNotePropertyInputError(`Property ${key} already exists.`);\n }\n\n throw error;\n }\n};\n\nexport const updateNotePropertyDefinition = async ({\n key,\n input,\n}: {\n key: string;\n input: NotePropertyDefinitionUpdateInput;\n}): Promise<SerializedNotePropertyKey | null> => {\n const normalizedKey = normalizePropertyKey(key);\n\n return models.$transaction(async (tx) => {\n const definition = await tx.propertyDefinition.findUnique({\n where: { key: normalizedKey },\n include: {\n options: {\n orderBy: { order: 'asc' },\n include: { _count: { select: { properties: true } } },\n },\n _count: { select: { properties: true } },\n },\n });\n\n if (!definition) {\n return null;\n }\n\n const nextName = input.name !== undefined ? normalizePropertyName(input.name, definition.key) : definition.name;\n const shouldUpdateOptions = input.options !== undefined;\n let nextOptions: ReturnType<typeof normalizePropertyOptionUpdates> | null = null;\n\n if (shouldUpdateOptions) {\n nextOptions = normalizePropertyOptionUpdates(input.options, definition.options);\n\n if (definition.valueType !== 'select' && nextOptions.length > 0) {\n throw new InvalidNotePropertyInputError('Only select properties can have options.');\n }\n\n if (definition.valueType === 'select' && nextOptions.length === 0) {\n throw new InvalidNotePropertyInputError('Select properties require at least one option.');\n }\n\n assertPropertyOptionUpdateKeepsUsedValues({\n existingOptions: definition.options,\n nextOptions,\n });\n\n const nextValues = new Set(nextOptions.map((option) => option.value));\n const removedValues = new Set(\n definition.options.filter((option) => !nextValues.has(option.value)).map((option) => option.value),\n );\n\n if (removedValues.size > 0) {\n const referencingViewSections = await tx.viewSection.findMany({\n where: { query: { contains: definition.key } },\n select: { title: true, query: true },\n });\n\n for (const section of referencingViewSections) {\n const removedReferencedValue = findReferencedRemovedPropertyOptionValue({\n query: section.query,\n key: definition.key,\n removedValues,\n });\n\n if (removedReferencedValue) {\n throw new InvalidNotePropertyInputError(\n `Property option ${removedReferencedValue} is used by view ${section.title} and cannot be removed.`,\n );\n }\n }\n }\n }\n\n await tx.propertyDefinition.update({\n where: { id: definition.id },\n data: { name: nextName },\n });\n\n if (nextName !== definition.name) {\n const referencingViewSections = await tx.viewSection.findMany({\n where: { query: { contains: definition.key } },\n select: { id: true, query: true },\n });\n\n for (const section of referencingViewSections) {\n const nextQuery = renamePropertyFiltersInViewQuery({\n query: section.query,\n key: definition.key,\n name: nextName,\n });\n\n if (nextQuery) {\n await tx.viewSection.update({\n where: { id: section.id },\n data: { query: nextQuery },\n });\n }\n }\n }\n\n if (nextOptions) {\n const existingOptionByValue = new Map(definition.options.map((option) => [option.value, option]));\n const nextValues = new Set(nextOptions.map((option) => option.value));\n const removableOptionIds = definition.options\n .filter((option) => !nextValues.has(option.value) && option._count.properties === 0)\n .map((option) => option.id);\n\n if (removableOptionIds.length > 0) {\n await tx.propertyOption.deleteMany({\n where: {\n id: { in: removableOptionIds },\n },\n });\n }\n\n for (const option of nextOptions) {\n const existingOption = existingOptionByValue.get(option.value);\n\n if (existingOption) {\n await tx.propertyOption.update({\n where: { id: existingOption.id },\n data: {\n label: option.label,\n color: option.color,\n order: option.order,\n },\n });\n continue;\n }\n\n await tx.propertyOption.create({\n data: {\n propertyDefinitionId: definition.id,\n label: option.label,\n value: option.value,\n color: option.color,\n order: option.order,\n },\n });\n }\n }\n\n const updatedDefinition = await tx.propertyDefinition.findUniqueOrThrow({\n where: { id: definition.id },\n include: { options: { orderBy: { order: 'asc' } }, _count: { select: { properties: true } } },\n });\n\n return serializePropertyDefinition(updatedDefinition);\n });\n};\n\nexport const deleteNotePropertyDefinition = async ({\n key,\n confirmImpact = false,\n}: {\n key: string;\n confirmImpact?: boolean;\n}): Promise<SerializedNotePropertyDeleteResult | null> => {\n const normalizedKey = normalizePropertyKey(key);\n\n return models.$transaction(async (tx) => {\n const definition = await tx.propertyDefinition.findUnique({\n where: { key: normalizedKey },\n include: { _count: { select: { properties: true } } },\n });\n\n if (!definition) {\n return null;\n }\n\n const affectedNoteCount = definition._count.properties;\n\n if (affectedNoteCount > 0 && !confirmImpact) {\n throw new NotePropertyDeleteConfirmationRequiredError(normalizedKey, affectedNoteCount);\n }\n\n if (affectedNoteCount > 0) {\n await tx.note.updateMany({\n where: {\n properties: {\n some: {\n propertyDefinitionId: definition.id,\n },\n },\n },\n data: {\n updatedAt: new Date(),\n },\n });\n }\n\n await tx.propertyDefinition.delete({\n where: { id: definition.id },\n });\n\n return {\n key: definition.key,\n name: definition.name,\n valueType: definition.valueType,\n affectedNoteCount,\n deleted: true,\n };\n });\n};\n\ninterface UpdateNotePropertiesWithVersionGuardInput {\n id: number;\n patch: NotePropertiesPatchInput;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n force?: boolean;\n noteData?: {\n title?: string;\n layout?: NoteLayout;\n };\n snapshotMeta?: string;\n}\n\nexport interface GuardedNotePropertiesWriteResult {\n note: Note;\n snapshot: unknown;\n}\n\nexport const updateNotePropertiesWithVersionGuardAndSnapshot = async ({\n id,\n patch,\n editSessionId,\n expectedUpdatedAt,\n force = false,\n noteData,\n snapshotMeta,\n}: UpdateNotePropertiesWithVersionGuardInput): Promise<GuardedNotePropertiesWriteResult | null> => {\n const normalizedPatch = normalizePatch(patch);\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n if (expectedTimestamp === null && !force) {\n throw new MissingNoteVersionError();\n }\n\n try {\n const result = await models.$transaction(async (tx) => {\n const existingNote = await tx.note.findUnique({\n where: { id },\n include: { properties: { include: { definition: true, option: true } } },\n });\n\n if (!existingNote) {\n return null;\n }\n\n if (!force && existingNote.updatedAt.getTime() !== expectedTimestamp) {\n throw createNoteVersionConflictError({\n currentUpdatedAt: existingNote.updatedAt.getTime(),\n expectedUpdatedAt: expectedTimestamp ?? existingNote.updatedAt.getTime(),\n });\n }\n\n const baseline = {\n ...existingNote,\n properties: existingNote.properties.map((property) => ({\n key: property.definition.key,\n name: property.definition.name,\n valueType: property.definition.valueType,\n textValue: property.textValue,\n textValueNormalized: property.textValueNormalized,\n numberValue: property.numberValue,\n dateValue: property.dateValue,\n boolValue: property.boolValue,\n optionValue: property.option?.value ?? null,\n optionLabel: property.option?.label ?? null,\n optionColor: property.option?.color ?? null,\n })),\n };\n\n for (const item of normalizedPatch.set) {\n const definition = await tx.propertyDefinition.findUnique({ where: { key: item.key } });\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} is not defined. Create it in property settings first.`,\n );\n }\n\n if (definition.valueType !== item.valueType) {\n throw new InvalidNotePropertyInputError(\n `Property ${item.key} already uses ${definition.valueType} values.`,\n );\n }\n\n const valueData = await buildTypedValueData(item, definition.id, tx);\n\n await tx.noteProperty.upsert({\n where: {\n noteId_propertyDefinitionId: {\n noteId: id,\n propertyDefinitionId: definition.id,\n },\n },\n create: {\n noteId: id,\n propertyDefinitionId: definition.id,\n ...valueData,\n },\n update: valueData,\n });\n }\n\n if (normalizedPatch.deleteKeys.length > 0) {\n const definitions = await tx.propertyDefinition.findMany({\n where: { key: { in: normalizedPatch.deleteKeys } },\n select: { id: true },\n });\n\n if (definitions.length > 0) {\n await tx.noteProperty.deleteMany({\n where: {\n noteId: id,\n propertyDefinitionId: { in: definitions.map((definition) => definition.id) },\n },\n });\n }\n }\n\n const updatedNote = await tx.note.update({\n where: { id },\n data: {\n ...(noteData?.title !== undefined ? { title: noteData.title } : {}),\n ...(noteData?.layout !== undefined ? { layout: noteData.layout } : {}),\n ...(noteData?.title !== undefined\n ? buildNoteSearchProjection({\n title: noteData.title,\n content: existingNote.content,\n })\n : {}),\n updatedAt: new Date(),\n },\n });\n\n return { updatedNote, baseline };\n });\n\n if (!result) {\n return null;\n }\n\n const snapshot = await captureNoteBaseline({\n noteId: id,\n baseline: result.baseline,\n ...(editSessionId && !force ? { editSessionId } : {}),\n ...(snapshotMeta ? { meta: snapshotMeta } : {}),\n ...(force ? { force: true } : {}),\n });\n\n return {\n note: result.updatedNote,\n snapshot,\n };\n } catch (error) {\n if (isRecordNotFoundError(error)) {\n return null;\n }\n\n throw error;\n }\n};\n\nexport const updateNotePropertiesWithVersionGuard = async (input: UpdateNotePropertiesWithVersionGuardInput) => {\n const result = await updateNotePropertiesWithVersionGuardAndSnapshot(input);\n return result?.note ?? null;\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/search-backfill.ts"],"sourcesContent":["import models from '~/models.js';\nimport { buildNoteSearchProjection, NOTE_SEARCH_TEXT_SCHEMA_VERSION, type NoteSearchProjection } from './search.js';\n\ninterface BackfillCandidate {\n id: number;\n title: string;\n content: string;\n}\n\ninterface NoteSearchBackfillDeps {\n listStaleNotes: (limit: number) => Promise<BackfillCandidate[]>;\n updateNotes: (updates: Array<{ id: number; projection: NoteSearchProjection }>) => Promise<void>;\n}\n\nconst DEFAULT_BACKFILL_BATCH_SIZE = 100;\nlet activeNoteSearchBackfillPromise: Promise<number> | null = null;\n\nconst yieldToEventLoop = async () => {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n};\n\nexport const createNoteSearchBackfillService = (deps: NoteSearchBackfillDeps) => {\n const backfillBatch = async (limit = DEFAULT_BACKFILL_BATCH_SIZE) => {\n const staleNotes = await deps.listStaleNotes(limit);\n\n if (staleNotes.length === 0) {\n return 0;\n }\n\n await deps.updateNotes(\n staleNotes.map((note) => ({\n id: note.id,\n projection: buildNoteSearchProjection(note),\n })),\n );\n\n return staleNotes.length;\n };\n\n const backfillAll = async (limit = DEFAULT_BACKFILL_BATCH_SIZE) => {\n let total = 0;\n\n while (true) {\n const count = await backfillBatch(limit);\n\n if (!count) {\n return total;\n }\n\n total += count;\n\n if (count < limit) {\n return total;\n }\n\n await yieldToEventLoop();\n }\n };\n\n return {\n backfillBatch,\n backfillAll,\n };\n};\n\nconst defaultNoteSearchBackfillService = createNoteSearchBackfillService({\n listStaleNotes: async (limit) => {\n return models.note.findMany({\n where: { searchableTextVersion: { not: NOTE_SEARCH_TEXT_SCHEMA_VERSION } },\n orderBy: { id: 'asc' },\n take: limit,\n select: {\n id: true,\n title: true,\n content: true,\n },\n });\n },\n updateNotes: async (updates) => {\n await models.$transaction(\n updates.map(({ id, projection }) =>\n models.note.update({\n where: { id },\n data: projection,\n }),\n ),\n );\n },\n});\n\nexport const backfillStaleNoteSearchText = async (limit?: number) => {\n return defaultNoteSearchBackfillService.backfillAll(limit);\n};\n\nexport const runNoteSearchBackfillInBackground = (limit?: number) => {\n if (activeNoteSearchBackfillPromise) {\n return activeNoteSearchBackfillPromise;\n }\n\n activeNoteSearchBackfillPromise = defaultNoteSearchBackfillService.backfillAll(limit).finally(() => {\n activeNoteSearchBackfillPromise = null;\n });\n\n return activeNoteSearchBackfillPromise;\n};\n"],"mappings":"AAAA,OAAO,YAAY;AACnB,SAAS,2BAA2B,uCAAkE;AAatG,MAAM,8BAA8B;AACpC,IAAI,kCAA0D;AAE9D,MAAM,mBAAmB,YAAY;AACjC,QAAM,IAAI,QAAc,CAAC,YAAY;AACjC,eAAW,SAAS,CAAC;AAAA,EACzB,CAAC;AACL;AAEO,MAAM,kCAAkC,CAAC,SAAiC;AAC7E,QAAM,gBAAgB,OAAO,QAAQ,gCAAgC;AACjE,UAAM,aAAa,MAAM,KAAK,eAAe,KAAK;AAElD,QAAI,WAAW,WAAW,GAAG;AACzB,aAAO;AAAA,IACX;AAEA,UAAM,KAAK;AAAA,MACP,WAAW,IAAI,CAAC,UAAU;AAAA,QACtB,IAAI,KAAK;AAAA,QACT,YAAY,0BAA0B,IAAI;AAAA,MAC9C,EAAE;AAAA,IACN;AAEA,WAAO,WAAW;AAAA,EACtB;AAEA,QAAM,cAAc,OAAO,QAAQ,gCAAgC;AAC/D,QAAI,QAAQ;AAEZ,WAAO,MAAM;AACT,YAAM,QAAQ,MAAM,cAAc,KAAK;AAEvC,UAAI,CAAC,OAAO;AACR,eAAO;AAAA,MACX;AAEA,eAAS;AAET,UAAI,QAAQ,OAAO;AACf,eAAO;AAAA,MACX;AAEA,YAAM,iBAAiB;AAAA,IAC3B;AAAA,EACJ;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,EACJ;AACJ;AAEA,MAAM,mCAAmC,gCAAgC;AAAA,EACrE,gBAAgB,OAAO,UAAU;AAC7B,WAAO,OAAO,KAAK,SAAS;AAAA,MACxB,OAAO,EAAE,uBAAuB,EAAE,KAAK,gCAAgC,EAAE;AAAA,MACzE,SAAS,EAAE,IAAI,MAAM;AAAA,MACrB,MAAM;AAAA,MACN,QAAQ;AAAA,QACJ,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,SAAS;AAAA,MACb;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EACA,aAAa,OAAO,YAAY;AAC5B,UAAM,OAAO;AAAA,MACT,QAAQ;AAAA,QAAI,CAAC,EAAE,IAAI,WAAW,MAC1B,OAAO,KAAK,OAAO;AAAA,UACf,OAAO,EAAE,GAAG;AAAA,UACZ,MAAM;AAAA,QACV,CAAC;AAAA,MACL;AAAA,IACJ;AAAA,EACJ;AACJ,CAAC;AAEM,MAAM,8BAA8B,OAAO,UAAmB;AACjE,SAAO,iCAAiC,YAAY,KAAK;AAC7D;AAEO,MAAM,oCAAoC,CAAC,UAAmB;AACjE,MAAI,iCAAiC;AACjC,WAAO;AAAA,EACX;AAEA,oCAAkC,iCAAiC,YAAY,KAAK,EAAE,QAAQ,MAAM;AAChG,sCAAkC;AAAA,EACtC,CAAC;AAED,SAAO;AACX;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/search.ts"],"sourcesContent":["import { isBlockNoteTreeNode, walkBlockNoteTree } from '~/modules/blocknote-tree.js';\n\nexport interface NoteSearchQuery {\n included: string[];\n excluded: string[];\n hasFilters: boolean;\n}\n\ninterface SearchableNoteLike {\n title: string;\n content: string;\n searchableText?: string;\n searchableTextVersion?: number;\n}\n\nexport interface NoteSearchProjection {\n searchableText: string;\n searchableTextVersion: number;\n}\n\ninterface SearchProjectionContext {\n segments: string[];\n}\n\ntype SearchableJsonNode = Record<string, unknown>;\ntype SearchNodeExtractor = (node: SearchableJsonNode, context: SearchProjectionContext) => void;\ntype SearchExtractorNodeType = (typeof NOTE_SEARCH_EXTRACTOR_NODE_TYPES)[number];\n\n// If we later materialize searchable text into a DB column, bump this when the\n// extraction meaning changes so stored projections can be rebuilt safely.\nexport const NOTE_SEARCH_TEXT_SCHEMA_VERSION = 1;\n\nconst FILE_BLOCK_VISIBLE_PROP_KEYS = ['name', 'caption'] as const;\n\nexport const NOTE_SEARCH_EXTRACTOR_NODE_TYPES = [\n 'text',\n 'tag',\n 'reference',\n 'image',\n 'video',\n 'audio',\n 'file',\n] as const;\n\nexport const NOTE_SEARCH_PASS_THROUGH_NODE_TYPES = [\n 'link',\n 'paragraph',\n 'heading',\n 'bulletListItem',\n 'numberedListItem',\n 'checkListItem',\n 'toggleListItem',\n 'quote',\n 'codeBlock',\n 'divider',\n 'table',\n 'tableContent',\n 'tableCell',\n] as const;\n\nexport const NOTE_SEARCH_IGNORED_NODE_TYPES = ['tableOfContents'] as const;\n\nconst KNOWN_NOTE_SEARCH_NODE_TYPES = new Set<string>([\n ...NOTE_SEARCH_EXTRACTOR_NODE_TYPES,\n ...NOTE_SEARCH_PASS_THROUGH_NODE_TYPES,\n ...NOTE_SEARCH_IGNORED_NODE_TYPES,\n]);\nconst SEARCH_NODE_EXTRACTOR_TYPE_SET = new Set<string>(NOTE_SEARCH_EXTRACTOR_NODE_TYPES);\n\nconst unknownNoteSearchNodeTypeCounts = new Map<string, number>();\n\nconst SEARCH_NODE_EXTRACTORS: Record<SearchExtractorNodeType, SearchNodeExtractor> = {\n text: (node, context) => {\n pushVisibleSegment(context, node.text);\n },\n tag: (node, context) => {\n pushVisibleProps(context, node.props, ['tag']);\n },\n reference: (node, context) => {\n pushVisibleProps(context, node.props, ['title']);\n },\n image: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n video: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n audio: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n file: (node, context) => {\n pushVisibleProps(context, node.props, FILE_BLOCK_VISIBLE_PROP_KEYS);\n },\n};\n\nconst recordUnknownNoteSearchNodeType = (type: string) => {\n if (KNOWN_NOTE_SEARCH_NODE_TYPES.has(type)) {\n return;\n }\n\n const nextCount = (unknownNoteSearchNodeTypeCounts.get(type) ?? 0) + 1;\n unknownNoteSearchNodeTypeCounts.set(type, nextCount);\n\n if (nextCount === 1) {\n process.emitWarning(\n `note-search encountered unsupported BlockNote node type \"${type}\". Only nested text will be indexed until explicit support is added.`,\n { code: 'OCEAN_BRAIN_NOTE_SEARCH_UNKNOWN_NODE' },\n );\n }\n};\n\nconst isSearchExtractorNodeType = (type: string): type is SearchExtractorNodeType => {\n return SEARCH_NODE_EXTRACTOR_TYPE_SET.has(type);\n};\n\nconst normalizeSearchText = (value: string) => value.replace(/\\s+/g, ' ').trim();\n\nconst normalizeSearchToken = (value: string) => normalizeSearchText(value).toLowerCase();\n\nconst pushVisibleSegment = (context: SearchProjectionContext, value: unknown) => {\n if (typeof value !== 'string') {\n return;\n }\n\n const normalizedValue = normalizeSearchText(value);\n\n if (!normalizedValue) {\n return;\n }\n\n context.segments.push(normalizedValue);\n};\n\nconst pushVisibleProps = (context: SearchProjectionContext, props: unknown, keys: readonly string[]) => {\n if (!isBlockNoteTreeNode(props)) {\n return;\n }\n\n for (const key of keys) {\n pushVisibleSegment(context, props[key]);\n }\n};\n\nconst collectVisibleSearchSegments = (node: unknown, context: SearchProjectionContext) => {\n walkBlockNoteTree(node, (item) => {\n const type = typeof item.type === 'string' ? item.type : undefined;\n\n if (type) {\n if (isSearchExtractorNodeType(type)) {\n SEARCH_NODE_EXTRACTORS[type](item, context);\n }\n\n recordUnknownNoteSearchNodeType(type);\n }\n });\n};\n\nexport const buildNoteSearchText = (note: Pick<SearchableNoteLike, 'title' | 'content'>) => {\n const normalizedTitle = normalizeSearchText(note.title);\n const normalizedContent = extractVisibleSearchTextFromContent(note.content);\n\n return normalizeSearchToken(`${normalizedTitle} ${normalizedContent}`);\n};\n\nexport const buildNoteSearchProjection = (\n note: Pick<SearchableNoteLike, 'title' | 'content'>,\n): NoteSearchProjection => {\n return {\n searchableText: buildNoteSearchText(note),\n searchableTextVersion: NOTE_SEARCH_TEXT_SCHEMA_VERSION,\n };\n};\n\nexport const parseNoteSearchQuery = (query: string): NoteSearchQuery => {\n const included: string[] = [];\n const excluded: string[] = [];\n\n for (const item of query\n .split(/\\s+/)\n .map((value) => value.trim())\n .filter(Boolean)) {\n if (item.startsWith('-') && item.length > 1) {\n excluded.push(normalizeSearchToken(item.slice(1)));\n continue;\n }\n\n included.push(normalizeSearchToken(item));\n }\n\n return {\n included,\n excluded,\n hasFilters: included.length > 0 || excluded.length > 0,\n };\n};\n\nexport const extractVisibleSearchTextFromContent = (content: string) => {\n try {\n const parsed = JSON.parse(content) as unknown;\n const context: SearchProjectionContext = { segments: [] };\n\n collectVisibleSearchSegments(parsed, context);\n\n return normalizeSearchText(context.segments.join(' '));\n } catch {\n return '';\n }\n};\n\nexport const matchesNoteSearchQuery = (note: SearchableNoteLike, query: string | NoteSearchQuery) => {\n const parsedQuery = typeof query === 'string' ? parseNoteSearchQuery(query) : query;\n\n if (!parsedQuery.hasFilters) {\n return true;\n }\n\n const haystack =\n note.searchableTextVersion === NOTE_SEARCH_TEXT_SCHEMA_VERSION && typeof note.searchableText === 'string'\n ? normalizeSearchToken(note.searchableText)\n : buildNoteSearchText(note);\n\n return (\n parsedQuery.included.every((term) => haystack.includes(term)) &&\n parsedQuery.excluded.every((term) => !haystack.includes(term))\n );\n};\n\nexport const filterNotesBySearchQuery = <T extends SearchableNoteLike>(notes: T[], query: string | NoteSearchQuery) => {\n const parsedQuery = typeof query === 'string' ? parseNoteSearchQuery(query) : query;\n\n if (!parsedQuery.hasFilters) {\n return notes;\n }\n\n return notes.filter((note) => matchesNoteSearchQuery(note, parsedQuery));\n};\n\nexport const getUnknownNoteSearchNodeTypeCounts = () => {\n return new Map(unknownNoteSearchNodeTypeCounts);\n};\n\nexport const resetUnknownNoteSearchNodeTypeCountsForTest = () => {\n unknownNoteSearchNodeTypeCounts.clear();\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/tag-filter.ts"],"sourcesContent":["import type { Prisma } from '~/models.js';\n\nexport type NoteTagMatchMode = 'and' | 'or';\n\nexport const normalizeNoteTagNames = (tagNames: string[]) => {\n return Array.from(new Set(tagNames.map((tagName) => tagName.trim()).filter(Boolean)));\n};\n\nexport const buildNoteTagNamesWhere = (tagNames: string[], mode: NoteTagMatchMode): Prisma.NoteWhereInput => {\n const normalizedTagNames = normalizeNoteTagNames(tagNames);\n\n if (normalizedTagNames.length === 0) {\n return { id: -1 };\n }\n\n if (mode === 'or') {\n return { tags: { some: { name: { in: normalizedTagNames } } } };\n }\n\n return { AND: normalizedTagNames.map((tagName) => ({ tags: { some: { name: tagName } } })) };\n};\n"],"mappings":"AAIO,MAAM,wBAAwB,CAAC,aAAuB;AACzD,SAAO,MAAM,KAAK,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,QAAQ,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC;AACxF;AAEO,MAAM,yBAAyB,CAAC,UAAoB,SAAkD;AACzG,QAAM,qBAAqB,sBAAsB,QAAQ;AAEzD,MAAI,mBAAmB,WAAW,GAAG;AACjC,WAAO,EAAE,IAAI,GAAG;AAAA,EACpB;AAEA,MAAI,SAAS,MAAM;AACf,WAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,mBAAmB,EAAE,EAAE,EAAE;AAAA,EAClE;AAEA,SAAO,EAAE,KAAK,mBAAmB,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE,EAAE,EAAE,EAAE;AAC/F;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/trash.ts"],"sourcesContent":["import models, { type NoteLayout, type PropertyValueType, type ReminderPriority } from '~/models.js';\nimport {\n createRetentionCutoff,\n RECOVERY_CLEANUP_BATCH_LIMIT,\n TRASH_RETENTION_DAYS,\n} from '~/modules/recovery-retention.js';\nimport { buildNoteSearchProjection, extractVisibleSearchTextFromContent } from './search.js';\n\nconst TRASHED_NOTE_CONTENT_PREVIEW_MAX_LENGTH = 240;\n\ninterface LiveTagRecord {\n id: number;\n name: string;\n}\n\ninterface LiveReminderRecord {\n id: number;\n reminderDate: Date;\n completed: boolean;\n priority: ReminderPriority;\n content: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\ninterface LiveNoteRecord {\n id: number;\n title: string;\n content: string;\n createdAt: Date;\n updatedAt: Date;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n tags: LiveTagRecord[];\n reminders: LiveReminderRecord[];\n properties?: LivePropertyRecord[];\n}\n\ninterface LivePropertyRecord {\n id: number;\n textValue: string | null;\n textValueNormalized: string | null;\n numberValue: number | null;\n dateValue: Date | null;\n boolValue: boolean | null;\n option: { value: string; label: string; color: string | null } | null;\n createdAt: Date;\n updatedAt: Date;\n definition: {\n key: string;\n name: string;\n valueType: PropertyValueType;\n };\n}\n\ninterface RestoredNoteRecord {\n id: number;\n title: string;\n content: string;\n createdAt: Date;\n updatedAt: Date;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n}\n\ninterface DeletedTagRecord {\n name: string;\n}\n\ninterface DeletedReminderRecord {\n originalId: number | null;\n reminderDate: Date;\n completed: boolean;\n priority: ReminderPriority;\n content: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\ninterface DeletedNoteRecord {\n id: number;\n title: string;\n content: string;\n createdAt: Date;\n updatedAt: Date;\n deletedAt: Date;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n tags: DeletedTagRecord[];\n reminders: DeletedReminderRecord[];\n properties?: DeletedPropertyRecord[];\n}\n\ninterface DeletedPropertyRecord {\n key: string;\n name: string;\n valueType: PropertyValueType;\n textValue: string | null;\n textValueNormalized: string | null;\n numberValue: number | null;\n dateValue: Date | null;\n boolValue: boolean | null;\n optionValue: string | null;\n optionLabel: string | null;\n optionColor: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\ninterface BlockNoteNode {\n type: string;\n props?: Record<string, unknown>;\n content?: BlockNoteNode[];\n children?: BlockNoteNode[];\n}\n\nexport interface TrashedNoteSummary {\n id: string;\n title: string;\n createdAt: string;\n updatedAt: string;\n deletedAt: string;\n contentPreview: string;\n pinned: boolean;\n order: number;\n layout: NoteLayout;\n tagNames: string[];\n}\n\nexport interface TrashedNotesResult {\n totalCount: number;\n notes: TrashedNoteSummary[];\n}\n\nexport interface TrashedNoteDetail extends TrashedNoteSummary {\n contentAsMarkdown: string;\n}\n\nexport class NoteRestoreConflictError extends Error {\n constructor(message = 'A live note with the same id already exists.') {\n super(message);\n this.name = 'NoteRestoreConflictError';\n }\n}\n\ninterface NoteTrashServiceDeps {\n countDeletedNotes: () => Promise<number>;\n findDeletedNote: (id: number) => Promise<DeletedNoteRecord | null>;\n findLiveNote: (id: number) => Promise<LiveNoteRecord | null>;\n listDeletedNotes: (skip: number, take: number) => Promise<DeletedNoteRecord[]>;\n liveNoteExists: (id: number) => Promise<boolean>;\n moveNoteToTrash: (note: LiveNoteRecord) => Promise<DeletedNoteRecord>;\n purgeDeletedNote: (note: DeletedNoteRecord) => Promise<void>;\n purgeExpiredDeletedNotes: (before: Date, limit: number) => Promise<number>;\n restoreDeletedNote: (note: DeletedNoteRecord) => Promise<RestoredNoteRecord>;\n}\n\nconst buildTrashedNoteContentPreview = (content: string) => {\n const text = extractVisibleSearchTextFromContent(content);\n\n if (text.length <= TRASHED_NOTE_CONTENT_PREVIEW_MAX_LENGTH) {\n return text;\n }\n\n return `${text.slice(0, TRASHED_NOTE_CONTENT_PREVIEW_MAX_LENGTH).trimEnd()}…`;\n};\n\nconst serializeTrashedNote = (note: DeletedNoteRecord): TrashedNoteSummary => {\n return {\n id: String(note.id),\n title: note.title,\n createdAt: note.createdAt.toISOString(),\n updatedAt: note.updatedAt.toISOString(),\n deletedAt: note.deletedAt.toISOString(),\n contentPreview: buildTrashedNoteContentPreview(note.content),\n pinned: note.pinned,\n order: note.order,\n layout: note.layout,\n tagNames: note.tags.map((tag) => tag.name),\n };\n};\n\nconst restoreTagIdsInContent = (content: string, tagIdByName: Map<string, number>) => {\n const rewriteNodes = (nodes: BlockNoteNode[]): BlockNoteNode[] => {\n return nodes.map((node) => ({\n ...node,\n content: node.content?.map((contentNode) => {\n if (contentNode.type !== 'tag') {\n return contentNode;\n }\n\n const tagName = typeof contentNode.props?.tag === 'string' ? contentNode.props.tag : null;\n const restoredTagId = tagName ? tagIdByName.get(tagName) : null;\n\n if (!restoredTagId) {\n return contentNode;\n }\n\n return {\n ...contentNode,\n props: {\n ...contentNode.props,\n id: String(restoredTagId),\n },\n };\n }),\n children: node.children ? rewriteNodes(node.children) : node.children,\n }));\n };\n\n try {\n const parsed = JSON.parse(content) as BlockNoteNode[];\n return JSON.stringify(rewriteNodes(parsed));\n } catch {\n return content;\n }\n};\n\nexport const createNoteTrashService = (deps: NoteTrashServiceDeps) => ({\n listTrashedNotes: async (input?: { limit?: number; offset?: number }): Promise<TrashedNotesResult> => {\n await deps.purgeExpiredDeletedNotes(createRetentionCutoff(TRASH_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const limit = Math.max(1, Number(input?.limit ?? 25));\n const offset = Math.max(0, Number(input?.offset ?? 0));\n const [totalCount, notes] = await Promise.all([deps.countDeletedNotes(), deps.listDeletedNotes(offset, limit)]);\n\n return {\n totalCount,\n notes: notes.map(serializeTrashedNote),\n };\n },\n\n trashNoteById: async (id: number): Promise<TrashedNoteSummary | null> => {\n await deps.purgeExpiredDeletedNotes(createRetentionCutoff(TRASH_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const note = await deps.findLiveNote(id);\n\n if (!note) {\n return null;\n }\n\n const trashedNote = await deps.moveNoteToTrash(note);\n return serializeTrashedNote(trashedNote);\n },\n\n getNoteById: async (id: number): Promise<TrashedNoteDetail | null> => {\n await deps.purgeExpiredDeletedNotes(createRetentionCutoff(TRASH_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const deletedNote = await deps.findDeletedNote(id);\n\n if (!deletedNote) {\n return null;\n }\n\n const { blocksToMarkdown } = await import('~/modules/blocknote.js');\n\n return {\n ...serializeTrashedNote(deletedNote),\n contentAsMarkdown: await blocksToMarkdown(deletedNote.content),\n };\n },\n\n restoreNoteById: async (id: number) => {\n await deps.purgeExpiredDeletedNotes(createRetentionCutoff(TRASH_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const deletedNote = await deps.findDeletedNote(id);\n\n if (!deletedNote) {\n return null;\n }\n\n if (await deps.liveNoteExists(id)) {\n throw new NoteRestoreConflictError();\n }\n\n return deps.restoreDeletedNote(deletedNote);\n },\n\n purgeNoteById: async (id: number): Promise<TrashedNoteSummary | null> => {\n await deps.purgeExpiredDeletedNotes(createRetentionCutoff(TRASH_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n\n const deletedNote = await deps.findDeletedNote(id);\n\n if (!deletedNote) {\n return null;\n }\n\n const summary = serializeTrashedNote(deletedNote);\n await deps.purgeDeletedNote(deletedNote);\n return summary;\n },\n});\n\nconst includeDeletedNote = {\n reminders: { orderBy: { reminderDate: 'asc' as const } },\n tags: { orderBy: { name: 'asc' as const } },\n properties: { orderBy: { key: 'asc' as const } },\n};\n\nconst defaultPurgeExpiredDeletedNotes = async (before: Date, limit: number) => {\n const expiredNotes = await models.deletedNote.findMany({\n where: { deletedAt: { lt: before } },\n orderBy: { deletedAt: 'asc' },\n take: limit,\n select: { id: true },\n });\n\n if (expiredNotes.length === 0) {\n return 0;\n }\n\n const deleted = await models.deletedNote.deleteMany({ where: { id: { in: expiredNotes.map((note) => note.id) } } });\n\n return deleted.count;\n};\n\nconst noteTrashService = createNoteTrashService({\n countDeletedNotes: async () => models.deletedNote.count(),\n findDeletedNote: async (id) => {\n return models.deletedNote.findUnique({\n where: { id },\n include: includeDeletedNote,\n });\n },\n findLiveNote: async (id) => {\n return models.note.findUnique({\n where: { id },\n include: {\n reminders: { orderBy: { reminderDate: 'asc' } },\n tags: { orderBy: { name: 'asc' } },\n properties: {\n orderBy: { definition: { key: 'asc' } },\n include: { definition: true, option: true },\n },\n },\n });\n },\n listDeletedNotes: async (skip, take) => {\n return models.deletedNote.findMany({\n skip,\n take,\n orderBy: { deletedAt: 'desc' },\n include: includeDeletedNote,\n });\n },\n liveNoteExists: async (id) => {\n const note = await models.note.findUnique({\n where: { id },\n select: { id: true },\n });\n return Boolean(note);\n },\n purgeExpiredDeletedNotes: defaultPurgeExpiredDeletedNotes,\n purgeDeletedNote: async (deletedNote) => {\n await models.deletedNote.delete({ where: { id: deletedNote.id } });\n },\n moveNoteToTrash: async (note) => {\n return models.$transaction(async (tx) => {\n await tx.deletedNote.create({\n data: {\n id: note.id,\n title: note.title,\n content: note.content,\n createdAt: note.createdAt,\n updatedAt: note.updatedAt,\n pinned: note.pinned,\n order: note.order,\n layout: note.layout,\n tags: { create: note.tags.map((tag) => ({ name: tag.name })) },\n reminders: {\n create: note.reminders.map((reminder) => ({\n originalId: reminder.id,\n reminderDate: reminder.reminderDate,\n completed: reminder.completed,\n priority: reminder.priority,\n content: reminder.content,\n createdAt: reminder.createdAt,\n updatedAt: reminder.updatedAt,\n })),\n },\n properties: {\n create: (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 createdAt: property.createdAt,\n updatedAt: property.updatedAt,\n })),\n },\n },\n });\n\n await tx.note.delete({ where: { id: note.id } });\n\n if (note.tags.length > 0) {\n await tx.tag.deleteMany({\n where: {\n id: { in: note.tags.map((tag) => tag.id) },\n notes: { none: {} },\n },\n });\n }\n\n return tx.deletedNote.findUniqueOrThrow({\n where: { id: note.id },\n include: includeDeletedNote,\n });\n });\n },\n restoreDeletedNote: async (deletedNote) => {\n return models.$transaction(async (tx) => {\n const tagNames = Array.from(new Set(deletedNote.tags.map((tag) => tag.name).filter(Boolean)));\n\n if (tagNames.length > 0) {\n const existingTags = await tx.tag.findMany({\n where: { name: { in: tagNames } },\n select: { name: true },\n });\n const existingTagNames = new Set(existingTags.map((tag) => tag.name));\n const missingTagNames = tagNames.filter((tagName) => !existingTagNames.has(tagName));\n\n for (const tagName of missingTagNames) {\n await tx.tag.create({ data: { name: tagName } });\n }\n }\n\n const restoreTagIds =\n tagNames.length > 0\n ? await tx.tag.findMany({\n where: { name: { in: tagNames } },\n select: {\n id: true,\n name: true,\n },\n })\n : [];\n\n const restoredContent =\n restoreTagIds.length > 0\n ? restoreTagIdsInContent(\n deletedNote.content,\n new Map(restoreTagIds.map((tag) => [tag.name, tag.id])),\n )\n : deletedNote.content;\n\n const note = await tx.note.create({\n data: {\n id: deletedNote.id,\n title: deletedNote.title,\n content: restoredContent,\n ...buildNoteSearchProjection({\n title: deletedNote.title,\n content: restoredContent,\n }),\n createdAt: deletedNote.createdAt,\n updatedAt: deletedNote.updatedAt,\n pinned: deletedNote.pinned,\n order: deletedNote.order,\n layout: deletedNote.layout,\n ...(restoreTagIds.length > 0\n ? { tags: { connect: restoreTagIds.map((tag) => ({ id: tag.id })) } }\n : {}),\n ...(deletedNote.reminders.length > 0\n ? {\n reminders: {\n create: deletedNote.reminders.map((reminder) => ({\n reminderDate: reminder.reminderDate,\n completed: reminder.completed,\n priority: reminder.priority,\n content: reminder.content,\n createdAt: reminder.createdAt,\n updatedAt: reminder.updatedAt,\n })),\n },\n }\n : {}),\n },\n });\n\n for (const property of deletedNote.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,\n },\n update: {\n label: property.optionLabel ?? property.optionValue,\n color: property.optionColor,\n },\n })\n : null;\n\n await tx.noteProperty.create({\n data: {\n noteId: note.id,\n propertyDefinitionId: definition.id,\n optionId: option?.id ?? null,\n textValue: property.textValue,\n textValueNormalized: property.textValueNormalized,\n numberValue: property.numberValue,\n dateValue: property.dateValue,\n boolValue: property.boolValue,\n createdAt: property.createdAt,\n updatedAt: property.updatedAt,\n },\n });\n }\n\n await tx.deletedNote.delete({ where: { id: deletedNote.id } });\n\n return note;\n });\n },\n});\n\nexport const listTrashedNotes = noteTrashService.listTrashedNotes;\nexport const getTrashedNoteById = noteTrashService.getNoteById;\nexport const trashNoteById = noteTrashService.trashNoteById;\nexport const restoreTrashedNoteById = noteTrashService.restoreNoteById;\nexport const purgeTrashedNoteById = noteTrashService.purgeNoteById;\nexport const purgeExpiredTrashedNotes = async () => {\n return defaultPurgeExpiredDeletedNotes(createRetentionCutoff(TRASH_RETENTION_DAYS), RECOVERY_CLEANUP_BATCH_LIMIT);\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/write-conflict.ts"],"sourcesContent":["export const NOTE_UPDATE_CONFLICT_CODE = 'NOTE_UPDATE_CONFLICT';\nexport const INVALID_NOTE_VERSION_CODE = 'INVALID_NOTE_VERSION';\nexport const MISSING_NOTE_VERSION_CODE = 'MISSING_NOTE_VERSION';\n\nexport class InvalidNoteVersionError extends Error {\n code = INVALID_NOTE_VERSION_CODE;\n\n constructor() {\n super('Invalid note version.');\n this.name = 'InvalidNoteVersionError';\n }\n}\n\nexport class MissingNoteVersionError extends Error {\n code = MISSING_NOTE_VERSION_CODE;\n\n constructor() {\n super('Expected note update time is required for this write.');\n this.name = 'MissingNoteVersionError';\n }\n}\n\nexport class NoteVersionConflictError extends Error {\n code = NOTE_UPDATE_CONFLICT_CODE;\n currentUpdatedAt: string;\n expectedUpdatedAt: string;\n\n constructor(input: { currentUpdatedAt: string | number; expectedUpdatedAt: string | number }) {\n super('This note changed elsewhere. Reload the latest version before saving.');\n this.name = 'NoteVersionConflictError';\n this.currentUpdatedAt = String(input.currentUpdatedAt);\n this.expectedUpdatedAt = String(input.expectedUpdatedAt);\n }\n}\n\nexport const parseNoteVersion = (value?: string | null) => {\n if (value === undefined || value === null || value === '') {\n return null;\n }\n\n const timestamp = /^\\d+$/.test(value) ? Number(value) : Date.parse(value);\n\n if (!Number.isFinite(timestamp)) {\n throw new InvalidNoteVersionError();\n }\n\n return timestamp;\n};\n\nexport const createNoteVersionConflictError = ({\n expectedUpdatedAt,\n currentUpdatedAt,\n}: {\n expectedUpdatedAt: string | number;\n currentUpdatedAt: string | number;\n}) => {\n return new NoteVersionConflictError({\n expectedUpdatedAt,\n currentUpdatedAt,\n });\n};\n\nexport const assertExpectedNoteVersion = ({\n expectedUpdatedAt,\n currentUpdatedAt,\n}: {\n expectedUpdatedAt?: string | null;\n currentUpdatedAt: Date;\n}) => {\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n if (expectedTimestamp === null) {\n return;\n }\n\n const currentTimestamp = currentUpdatedAt.getTime();\n\n if (expectedTimestamp !== currentTimestamp) {\n throw createNoteVersionConflictError({\n expectedUpdatedAt: expectedTimestamp,\n currentUpdatedAt: currentTimestamp,\n });\n }\n};\n\nexport const isNoteVersionConflictError = (error: unknown): error is NoteVersionConflictError => {\n return error instanceof NoteVersionConflictError;\n};\n\nexport const isInvalidNoteVersionError = (error: unknown): error is InvalidNoteVersionError => {\n return error instanceof InvalidNoteVersionError;\n};\n\nexport const isMissingNoteVersionError = (error: unknown): error is MissingNoteVersionError => {\n return error instanceof MissingNoteVersionError;\n};\n"],"mappings":"AAAO,MAAM,4BAA4B;AAClC,MAAM,4BAA4B;AAClC,MAAM,4BAA4B;AAElC,MAAM,gCAAgC,MAAM;AAAA,EAC/C,OAAO;AAAA,EAEP,cAAc;AACV,UAAM,uBAAuB;AAC7B,SAAK,OAAO;AAAA,EAChB;AACJ;AAEO,MAAM,gCAAgC,MAAM;AAAA,EAC/C,OAAO;AAAA,EAEP,cAAc;AACV,UAAM,uDAAuD;AAC7D,SAAK,OAAO;AAAA,EAChB;AACJ;AAEO,MAAM,iCAAiC,MAAM;AAAA,EAChD,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EAEA,YAAY,OAAkF;AAC1F,UAAM,uEAAuE;AAC7E,SAAK,OAAO;AACZ,SAAK,mBAAmB,OAAO,MAAM,gBAAgB;AACrD,SAAK,oBAAoB,OAAO,MAAM,iBAAiB;AAAA,EAC3D;AACJ;AAEO,MAAM,mBAAmB,CAAC,UAA0B;AACvD,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACvD,WAAO;AAAA,EACX;AAEA,QAAM,YAAY,QAAQ,KAAK,KAAK,IAAI,OAAO,KAAK,IAAI,KAAK,MAAM,KAAK;AAExE,MAAI,CAAC,OAAO,SAAS,SAAS,GAAG;AAC7B,UAAM,IAAI,wBAAwB;AAAA,EACtC;AAEA,SAAO;AACX;AAEO,MAAM,iCAAiC,CAAC;AAAA,EAC3C;AAAA,EACA;AACJ,MAGM;AACF,SAAO,IAAI,yBAAyB;AAAA,IAChC;AAAA,IACA;AAAA,EACJ,CAAC;AACL;AAEO,MAAM,4BAA4B,CAAC;AAAA,EACtC;AAAA,EACA;AACJ,MAGM;AACF,QAAM,oBAAoB,iBAAiB,iBAAiB;AAE5D,MAAI,sBAAsB,MAAM;AAC5B;AAAA,EACJ;AAEA,QAAM,mBAAmB,iBAAiB,QAAQ;AAElD,MAAI,sBAAsB,kBAAkB;AACxC,UAAM,+BAA+B;AAAA,MACjC,mBAAmB;AAAA,MACnB,kBAAkB;AAAA,IACtB,CAAC;AAAA,EACL;AACJ;AAEO,MAAM,6BAA6B,CAAC,UAAsD;AAC7F,SAAO,iBAAiB;AAC5B;AAEO,MAAM,4BAA4B,CAAC,UAAqD;AAC3F,SAAO,iBAAiB;AAC5B;AAEO,MAAM,4BAA4B,CAAC,UAAqD;AAC3F,SAAO,iBAAiB;AAC5B;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/note/services/write.ts"],"sourcesContent":["import models, { type NoteLayout, Prisma } from '~/models.js';\nimport { buildNoteSearchProjection } from './search.js';\nimport { captureNoteBaseline } from './snapshot.js';\nimport { createNoteVersionConflictError, MissingNoteVersionError, parseNoteVersion } from './write-conflict.js';\n\nexport interface NoteWriteRecord {\n id: number;\n title: string;\n content: string;\n layout: NoteLayout;\n createdAt: Date;\n updatedAt: Date;\n pinned: boolean;\n order: number;\n}\n\ntype NoteWriteData = {\n title?: string;\n content?: string;\n layout?: NoteLayout;\n tagIds?: number[];\n};\n\ninterface UpdateNoteWithVersionGuardInput {\n id: number;\n data: NoteWriteData;\n editSessionId?: string;\n expectedUpdatedAt?: string;\n snapshotMeta?: string;\n force?: boolean;\n}\n\nexport interface GuardedNoteWriteResult {\n note: NoteWriteRecord;\n snapshot: unknown;\n}\n\ninterface NoteWriteDeps {\n findNoteForWrite: (\n id: number,\n ) => Promise<Pick<NoteWriteRecord, 'title' | 'content' | 'updatedAt' | 'pinned' | 'order' | 'layout'> | null>;\n findNoteVersion: (id: number) => Promise<Pick<NoteWriteRecord, 'updatedAt'> | null>;\n captureBaseline: (input: {\n noteId: number;\n editSessionId?: string;\n meta?: string;\n baseline: Pick<NoteWriteRecord, 'id' | 'title' | 'content' | 'pinned' | 'order' | 'layout'>;\n force?: boolean;\n }) => Promise<unknown>;\n updateNote: (input: {\n where: {\n id: number;\n updatedAt?: Date;\n };\n data: {\n title?: string;\n content?: string;\n layout?: NoteLayout;\n searchableText: string;\n searchableTextVersion: number;\n tags?: {\n set: Array<{ id: number }>;\n };\n };\n }) => Promise<NoteWriteRecord>;\n isRecordNotFoundError: (error: unknown) => boolean;\n}\n\nconst isRecordNotFoundError = (error: unknown) => {\n return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025';\n};\n\nexport const createNoteWriteService = (deps: NoteWriteDeps) => {\n const updateNoteWithVersionGuardAndSnapshot = async ({\n id,\n data,\n editSessionId,\n expectedUpdatedAt,\n snapshotMeta,\n force = false,\n }: UpdateNoteWithVersionGuardInput): Promise<GuardedNoteWriteResult | null> => {\n const existingNote = await deps.findNoteForWrite(id);\n\n if (!existingNote) {\n return null;\n }\n\n const expectedTimestamp = parseNoteVersion(expectedUpdatedAt);\n\n if (expectedTimestamp === null && !force) {\n throw new MissingNoteVersionError();\n }\n\n const nextTitle = data.title ?? existingNote.title;\n const nextContent = data.content ?? existingNote.content;\n const where = force\n ? { id }\n : { id, updatedAt: new Date(expectedTimestamp ?? existingNote.updatedAt.getTime()) };\n\n try {\n const snapshot = await deps.captureBaseline({\n noteId: id,\n baseline: {\n id,\n title: existingNote.title,\n content: existingNote.content,\n pinned: existingNote.pinned,\n order: existingNote.order,\n layout: existingNote.layout,\n },\n ...(editSessionId && !force ? { editSessionId } : {}),\n ...(snapshotMeta ? { meta: snapshotMeta } : {}),\n ...(force ? { force: true } : {}),\n });\n\n const updatedNote = await deps.updateNote({\n where,\n data: {\n ...(data.title !== undefined ? { title: data.title } : {}),\n ...(data.content !== undefined ? { content: data.content } : {}),\n ...(data.layout !== undefined ? { layout: data.layout } : {}),\n ...buildNoteSearchProjection({\n title: nextTitle,\n content: nextContent,\n }),\n ...(data.tagIds ? { tags: { set: data.tagIds.map((tagId) => ({ id: tagId })) } } : {}),\n },\n });\n\n return {\n note: updatedNote,\n snapshot,\n };\n } catch (error) {\n if (deps.isRecordNotFoundError(error)) {\n const currentNote = await deps.findNoteVersion(id);\n\n if (!currentNote) {\n return null;\n }\n\n if (expectedTimestamp === null || currentNote.updatedAt.getTime() === expectedTimestamp) {\n throw error;\n }\n\n throw createNoteVersionConflictError({\n expectedUpdatedAt: expectedTimestamp,\n currentUpdatedAt: currentNote.updatedAt.getTime(),\n });\n }\n\n throw error;\n }\n };\n\n return {\n updateNoteWithVersionGuard: async (input: UpdateNoteWithVersionGuardInput): Promise<NoteWriteRecord | null> => {\n const result = await updateNoteWithVersionGuardAndSnapshot(input);\n return result?.note ?? null;\n },\n updateNoteWithVersionGuardAndSnapshot,\n };\n};\n\nconst defaultNoteWriteService = createNoteWriteService({\n findNoteForWrite: (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n title: true,\n content: true,\n updatedAt: true,\n pinned: true,\n order: true,\n layout: true,\n },\n }),\n findNoteVersion: (id) =>\n models.note.findUnique({\n where: { id },\n select: {\n updatedAt: true,\n },\n }),\n captureBaseline: captureNoteBaseline,\n updateNote: ({ where, data }) =>\n models.note.update({\n where,\n data,\n }),\n isRecordNotFoundError,\n});\n\nexport const updateNoteWithVersionGuard = async (input: UpdateNoteWithVersionGuardInput) => {\n return defaultNoteWriteService.updateNoteWithVersionGuard(input);\n};\n\nexport const updateNoteWithVersionGuardAndSnapshot = async (input: UpdateNoteWithVersionGuardInput) => {\n return defaultNoteWriteService.updateNoteWithVersionGuardAndSnapshot(input);\n};\n"],"mappings":"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","names":["updateNoteWithVersionGuardAndSnapshot"]}
|