ocean-brain 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/client/dist/assets/{Calendar-BytW4PeT.js → Calendar-DbPut8EU.js} +1 -1
- package/server/client/dist/assets/{Callout-9sJgGNRb.js → Callout-DfX-gIUB.js} +1 -1
- package/server/client/dist/assets/{Graph-o5u-NzrA.js → Graph-yHE9m5fy.js} +1 -1
- package/server/client/dist/assets/{Image-DxWhlIDG.js → Image-DK0lnaK2.js} +1 -1
- package/server/client/dist/assets/{Image.es-B0LL3pb7.js → Image.es-CtMc7BW4.js} +1 -1
- package/server/client/dist/assets/{Note-CYoYzZNu.js → Note-BECn-AoI.js} +2 -2
- package/server/client/dist/assets/{Plus.es-DpxzgRtY.js → Plus.es-BGVIIQtM.js} +1 -1
- package/server/client/dist/assets/{Reminders-DAepTZg1.js → Reminders-CvEJf0eQ.js} +1 -1
- package/server/client/dist/assets/{Search-6KH7An44.js → Search-CKZhztuV.js} +1 -1
- package/server/client/dist/assets/{SurfaceCard-EMs50xSF.js → SurfaceCard-Bnfo5EOl.js} +1 -1
- package/server/client/dist/assets/Tag-C4oAwjs0.js +1 -0
- package/server/client/dist/assets/{TagNotes-BTbu-f6v.js → TagNotes-Rl1ZSJP6.js} +1 -1
- package/server/client/dist/assets/{Trash.es-C5x35NKF.js → Trash.es-BcwUz2yM.js} +1 -1
- package/server/client/dist/assets/ViewNotes-B9Yc0A87.js +1 -0
- package/server/client/dist/assets/ViewSectionTableRenderer-C5yJBO7C.js +102 -0
- package/server/client/dist/assets/Views-Bqq0pUEE.js +1 -0
- package/server/client/dist/assets/{image.api-DRhRQ0Gf.js → image.api-CuGTgzyQ.js} +1 -1
- package/server/client/dist/assets/{index-DYlYUrjH.js → index-B43ulIPo.js} +40 -40
- package/server/client/dist/assets/{index-DswSFFMb.js → index-BQDR6hIA.js} +1 -1
- package/server/client/dist/assets/{index-Cd81UxEh.css → index-CDjYEIVf.css} +1 -1
- package/server/client/dist/assets/manage-image-Bs4Kmevx.js +1 -0
- package/server/client/dist/assets/{manage-image-detail-CJkfybR9.js → manage-image-detail-WARala-v.js} +1 -1
- package/server/client/dist/assets/{mcp-piouFDNm.js → mcp-C0Z3vKBa.js} +1 -1
- package/server/client/dist/assets/{note-core-Z6kqulGB.js → note-core-DMZcZ4yV.js} +53 -53
- package/server/client/dist/assets/{note-vendor-i1M2FSI2.js → note-vendor-uuJO2MTD.js} +1 -1
- package/server/client/dist/assets/{placeholder-DGz3CHej.js → placeholder-D080guzn.js} +3 -3
- package/server/client/dist/assets/{properties-CM4khczg.js → properties-DbnmfbT5.js} +1 -1
- package/server/client/dist/assets/trash-D5ZiZfTk.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-DHjDDHKg.js → useReminderMutate-h7d_S_X2.js} +2 -2
- package/server/client/dist/index.html +4 -4
- package/server/dist/features/note/services/markdown-intent-write.js +17 -0
- package/server/dist/features/note/services/markdown-intent-write.js.map +1 -1
- package/server/dist/features/note/services/markdown-patch.js.map +1 -1
- package/server/dist/features/view/graphql/view.type-defs.js +22 -0
- package/server/dist/features/view/graphql/view.type-defs.js.map +1 -1
- package/server/dist/features/view/services/workspace.js +77 -16
- package/server/dist/features/view/services/workspace.js.map +1 -1
- package/server/dist/modules/blocknote.js +320 -5
- package/server/dist/modules/blocknote.js.map +1 -1
- package/server/prisma/schema.prisma +1 -0
- package/server/client/dist/assets/Tag-C89j8x56.js +0 -1
- package/server/client/dist/assets/ViewNotes-DPEuki3y.js +0 -1
- package/server/client/dist/assets/Views-BGkuNhVd.js +0 -1
- package/server/client/dist/assets/manage-image-C6sHhWnI.js +0 -1
- package/server/client/dist/assets/trash-C2lItz-b.js +0 -1
- package/server/client/dist/assets/view-dashboard-C17WsdL7.js +0 -99
|
@@ -1 +1 @@
|
|
|
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;\nconst DIFF_CONTEXT_LINES = 3;\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 maxChangedChars?: number;\n maxChangedLines?: number;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface PreviewMarkdownPatchInput {\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 PreviewMarkdownAppendInput {\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 PreviewMarkdownReplaceInput {\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 MarkdownChangeDryRun {\n status: 'dry_run';\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 diff: string;\n };\n warnings: string[];\n}\n\nexport interface MarkdownChangePlan extends MarkdownChangeDryRun {\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 | 'CHANGE_LIMIT_EXCEEDED'\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 | '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}\n\nexport type MarkdownPatchPreviewResult =\n | MarkdownChangeDryRun\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure;\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 formatDiffRange = (lineIndex: number, lineCount: number) => {\n const lineNumber = lineIndex + 1;\n\n return lineCount === 1 ? String(lineNumber) : `${lineNumber},${lineCount}`;\n};\n\nexport const createUnifiedMarkdownDiff = (\n beforeMarkdown: string,\n afterMarkdown: string,\n contextLines: number | null = DIFF_CONTEXT_LINES,\n) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const changedBeforeStart = prefix;\n const changedBeforeEnd = beforeLines.length - suffix;\n const changedAfterStart = prefix;\n const changedAfterEnd = afterLines.length - suffix;\n const displayBeforeStart = contextLines === null ? 0 : Math.max(0, changedBeforeStart - contextLines);\n const displayBeforeEnd =\n contextLines === null ? beforeLines.length : Math.min(beforeLines.length, changedBeforeEnd + contextLines);\n const displayAfterStart = contextLines === null ? 0 : Math.max(0, changedAfterStart - contextLines);\n const displayAfterEnd =\n contextLines === null ? afterLines.length : Math.min(afterLines.length, changedAfterEnd + contextLines);\n const diffLines = [\n '--- before.md',\n '+++ after.md',\n `@@ -${formatDiffRange(displayBeforeStart, displayBeforeEnd - displayBeforeStart)} +${formatDiffRange(displayAfterStart, displayAfterEnd - displayAfterStart)} @@`,\n ];\n\n for (let index = displayBeforeStart; index < changedBeforeStart; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n for (let index = changedBeforeStart; index < changedBeforeEnd; index += 1) {\n diffLines.push(`-${beforeLines[index]}`);\n }\n\n for (let index = changedAfterStart; index < changedAfterEnd; index += 1) {\n diffLines.push(`+${afterLines[index]}`);\n }\n\n for (let index = changedBeforeEnd; index < displayBeforeEnd; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n return diffLines.join('\\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: 'The markdown write baseline does not match the current note.',\n };\n }\n\n return null;\n};\n\nconst validateChangeLimits = (\n beforeMarkdown: string,\n afterMarkdown: string,\n changedCharCount: number,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const changedLineCount = countChangedLines(beforeMarkdown, afterMarkdown);\n\n if (\n (policy?.maxChangedLines !== undefined && changedLineCount > policy.maxChangedLines) ||\n (policy?.maxChangedChars !== undefined && changedCharCount > policy.maxChangedChars)\n ) {\n return {\n status: 'failed',\n reason: 'CHANGE_LIMIT_EXCEEDED',\n message: 'The markdown write exceeds the configured change limit.',\n };\n }\n\n return null;\n};\n\nconst createChangePlan = ({\n note,\n afterMarkdown,\n changedCharCount,\n policy,\n diffContextLines = DIFF_CONTEXT_LINES,\n match,\n placement,\n}: {\n note: MarkdownPatchNoteSnapshot;\n afterMarkdown: string;\n changedCharCount: number;\n policy: MarkdownChangePolicy | undefined;\n diffContextLines?: number | null;\n match?: MarkdownChangeDryRun['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 const limitFailure = validateChangeLimits(note.markdown, afterMarkdown, changedCharCount, policy);\n\n if (limitFailure) {\n return limitFailure;\n }\n\n return {\n status: 'dry_run',\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 diff: createUnifiedMarkdownDiff(note.markdown, afterMarkdown, diffContextLines),\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: PreviewMarkdownPatchInput): 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: PreviewMarkdownAppendInput): 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: PreviewMarkdownReplaceInput): 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 diffContextLines: null,\n });\n};\n\nconst toPreviewResult = (plan: MarkdownChangePlan): MarkdownChangeDryRun => {\n return {\n status: 'dry_run',\n note: plan.note,\n ...(plan.match ? { match: plan.match } : {}),\n ...(plan.placement ? { placement: plan.placement } : {}),\n proposed: plan.proposed,\n warnings: plan.warnings,\n };\n};\n\nexport const previewMarkdownPatch = (input: PreviewMarkdownPatchInput): MarkdownPatchPreviewResult => {\n const plan = buildMarkdownPatchPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownAppend = (\n input: PreviewMarkdownAppendInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownAppendPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownReplace = (\n input: PreviewMarkdownReplaceInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownReplacePlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n"],"mappings":"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","names":["match"]}
|
|
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;\nconst DIFF_CONTEXT_LINES = 3;\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 maxChangedChars?: number;\n maxChangedLines?: number;\n preserveReferences?: boolean | 'warn';\n preserveTags?: boolean | 'warn';\n}\n\nexport interface PreviewMarkdownPatchInput {\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 PreviewMarkdownAppendInput {\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 PreviewMarkdownReplaceInput {\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 MarkdownChangeDryRun {\n status: 'dry_run';\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 diff: string;\n };\n warnings: string[];\n}\n\nexport interface MarkdownChangePlan extends MarkdownChangeDryRun {\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 | 'CHANGE_LIMIT_EXCEEDED'\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}\n\nexport type MarkdownPatchPreviewResult =\n | MarkdownChangeDryRun\n | MarkdownPatchNeedsDisambiguation\n | MarkdownChangeFailure;\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 formatDiffRange = (lineIndex: number, lineCount: number) => {\n const lineNumber = lineIndex + 1;\n\n return lineCount === 1 ? String(lineNumber) : `${lineNumber},${lineCount}`;\n};\n\nexport const createUnifiedMarkdownDiff = (\n beforeMarkdown: string,\n afterMarkdown: string,\n contextLines: number | null = DIFF_CONTEXT_LINES,\n) => {\n const beforeLines = splitLines(beforeMarkdown);\n const afterLines = splitLines(afterMarkdown);\n const { prefix, suffix } = findSharedLineEdges(beforeLines, afterLines);\n const changedBeforeStart = prefix;\n const changedBeforeEnd = beforeLines.length - suffix;\n const changedAfterStart = prefix;\n const changedAfterEnd = afterLines.length - suffix;\n const displayBeforeStart = contextLines === null ? 0 : Math.max(0, changedBeforeStart - contextLines);\n const displayBeforeEnd =\n contextLines === null ? beforeLines.length : Math.min(beforeLines.length, changedBeforeEnd + contextLines);\n const displayAfterStart = contextLines === null ? 0 : Math.max(0, changedAfterStart - contextLines);\n const displayAfterEnd =\n contextLines === null ? afterLines.length : Math.min(afterLines.length, changedAfterEnd + contextLines);\n const diffLines = [\n '--- before.md',\n '+++ after.md',\n `@@ -${formatDiffRange(displayBeforeStart, displayBeforeEnd - displayBeforeStart)} +${formatDiffRange(displayAfterStart, displayAfterEnd - displayAfterStart)} @@`,\n ];\n\n for (let index = displayBeforeStart; index < changedBeforeStart; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n for (let index = changedBeforeStart; index < changedBeforeEnd; index += 1) {\n diffLines.push(`-${beforeLines[index]}`);\n }\n\n for (let index = changedAfterStart; index < changedAfterEnd; index += 1) {\n diffLines.push(`+${afterLines[index]}`);\n }\n\n for (let index = changedBeforeEnd; index < displayBeforeEnd; index += 1) {\n diffLines.push(` ${beforeLines[index]}`);\n }\n\n return diffLines.join('\\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: 'The markdown write baseline does not match the current note.',\n };\n }\n\n return null;\n};\n\nconst validateChangeLimits = (\n beforeMarkdown: string,\n afterMarkdown: string,\n changedCharCount: number,\n policy: MarkdownChangePolicy | undefined,\n): MarkdownChangeFailure | null => {\n const changedLineCount = countChangedLines(beforeMarkdown, afterMarkdown);\n\n if (\n (policy?.maxChangedLines !== undefined && changedLineCount > policy.maxChangedLines) ||\n (policy?.maxChangedChars !== undefined && changedCharCount > policy.maxChangedChars)\n ) {\n return {\n status: 'failed',\n reason: 'CHANGE_LIMIT_EXCEEDED',\n message: 'The markdown write exceeds the configured change limit.',\n };\n }\n\n return null;\n};\n\nconst createChangePlan = ({\n note,\n afterMarkdown,\n changedCharCount,\n policy,\n diffContextLines = DIFF_CONTEXT_LINES,\n match,\n placement,\n}: {\n note: MarkdownPatchNoteSnapshot;\n afterMarkdown: string;\n changedCharCount: number;\n policy: MarkdownChangePolicy | undefined;\n diffContextLines?: number | null;\n match?: MarkdownChangeDryRun['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 const limitFailure = validateChangeLimits(note.markdown, afterMarkdown, changedCharCount, policy);\n\n if (limitFailure) {\n return limitFailure;\n }\n\n return {\n status: 'dry_run',\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 diff: createUnifiedMarkdownDiff(note.markdown, afterMarkdown, diffContextLines),\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: PreviewMarkdownPatchInput): 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: PreviewMarkdownAppendInput): 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: PreviewMarkdownReplaceInput): 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 diffContextLines: null,\n });\n};\n\nconst toPreviewResult = (plan: MarkdownChangePlan): MarkdownChangeDryRun => {\n return {\n status: 'dry_run',\n note: plan.note,\n ...(plan.match ? { match: plan.match } : {}),\n ...(plan.placement ? { placement: plan.placement } : {}),\n proposed: plan.proposed,\n warnings: plan.warnings,\n };\n};\n\nexport const previewMarkdownPatch = (input: PreviewMarkdownPatchInput): MarkdownPatchPreviewResult => {\n const plan = buildMarkdownPatchPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownAppend = (\n input: PreviewMarkdownAppendInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownAppendPlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n\nexport const previewMarkdownReplace = (\n input: PreviewMarkdownReplaceInput,\n): MarkdownChangeDryRun | MarkdownChangeFailure => {\n const plan = buildMarkdownReplacePlan(input);\n\n if (plan.status !== 'dry_run') {\n return plan;\n }\n\n return toPreviewResult(plan);\n};\n"],"mappings":"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","names":["match"]}
|
|
@@ -17,6 +17,7 @@ const viewType = gql`
|
|
|
17
17
|
tabId: ID!
|
|
18
18
|
title: String!
|
|
19
19
|
displayType: ViewDisplayType!
|
|
20
|
+
displayOptions: ViewDisplayOptions!
|
|
20
21
|
tagNames: [String!]!
|
|
21
22
|
mode: TagMatchMode!
|
|
22
23
|
propertyFilters: [ViewPropertyFilter!]!
|
|
@@ -34,13 +35,29 @@ const viewType = gql`
|
|
|
34
35
|
value: String
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
type ViewDisplayOptions {
|
|
39
|
+
tableColumns: [ViewTableColumn!]!
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
enum ViewDisplayType {
|
|
38
43
|
list
|
|
44
|
+
table
|
|
39
45
|
calendar
|
|
40
46
|
}
|
|
41
47
|
|
|
48
|
+
enum ViewTableColumn {
|
|
49
|
+
title
|
|
50
|
+
tags
|
|
51
|
+
properties
|
|
52
|
+
createdAt
|
|
53
|
+
updatedAt
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
enum ViewPropertyFilterOperator {
|
|
43
57
|
equals
|
|
58
|
+
notEquals
|
|
59
|
+
contains
|
|
60
|
+
notContains
|
|
44
61
|
before
|
|
45
62
|
after
|
|
46
63
|
exists
|
|
@@ -65,9 +82,14 @@ const viewType = gql`
|
|
|
65
82
|
value: String
|
|
66
83
|
}
|
|
67
84
|
|
|
85
|
+
input ViewDisplayOptionsInput {
|
|
86
|
+
tableColumns: [ViewTableColumn!]
|
|
87
|
+
}
|
|
88
|
+
|
|
68
89
|
input ViewSectionInput {
|
|
69
90
|
title: String
|
|
70
91
|
displayType: ViewDisplayType
|
|
92
|
+
displayOptions: ViewDisplayOptionsInput
|
|
71
93
|
tagNames: [String!]
|
|
72
94
|
mode: TagMatchMode
|
|
73
95
|
propertyFilters: [ViewPropertyFilterInput!]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/view/graphql/view.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const viewType = gql`\n type ViewWorkspace {\n activeTabId: ID\n tabs: [ViewTab!]!\n }\n\n type ViewTab {\n id: ID!\n title: String!\n order: Int!\n sections: [ViewSection!]!\n }\n\n type ViewSection {\n id: ID!\n tabId: ID!\n title: String!\n displayType: ViewDisplayType!\n tagNames: [String!]!\n mode: TagMatchMode!\n propertyFilters: [ViewPropertyFilter!]!\n sortBy: ViewSortBy!\n sortOrder: ViewSortOrder!\n limit: Int!\n order: Int!\n }\n\n type ViewPropertyFilter {\n key: String!\n name: String!\n valueType: NotePropertyValueType!\n operator: ViewPropertyFilterOperator!\n value: String\n }\n\n enum ViewDisplayType {\n list\n calendar\n }\n\n enum ViewPropertyFilterOperator {\n equals\n before\n after\n exists\n notExists\n }\n\n enum ViewSortBy {\n updatedAt\n createdAt\n title\n }\n\n enum ViewSortOrder {\n asc\n desc\n }\n\n input ViewPropertyFilterInput {\n key: String!\n valueType: NotePropertyValueType!\n operator: ViewPropertyFilterOperator!\n value: String\n }\n\n input ViewSectionInput {\n title: String\n displayType: ViewDisplayType\n tagNames: [String!]\n mode: TagMatchMode\n propertyFilters: [ViewPropertyFilterInput!]\n sortBy: ViewSortBy\n sortOrder: ViewSortOrder\n limit: Int\n }\n\n input NotesByPropertiesInput {\n tagNames: [String!]\n mode: TagMatchMode\n propertyFilters: [ViewPropertyFilterInput!]!\n sortBy: ViewSortBy\n sortOrder: ViewSortOrder\n }\n`;\n\nexport const viewQuery = gql`\n type Query {\n viewWorkspace: ViewWorkspace!\n viewSection(id: ID!): ViewSection\n viewSectionNotes(id: ID!, pagination: PaginationInput): Notes!\n notesByProperties(input: NotesByPropertiesInput!, pagination: PaginationInput): Notes!\n }\n`;\n\nexport const viewMutation = gql`\n type Mutation {\n createViewTab(title: String!): ViewTab!\n updateViewTab(id: ID!, title: String!): ViewTab!\n deleteViewTab(id: ID!): Boolean!\n setActiveViewTab(id: ID!): ViewWorkspace!\n reorderViewTabs(tabIds: [ID!]!): [ViewTab!]!\n createViewSection(tabId: ID!, input: ViewSectionInput!): ViewSection!\n updateViewSection(id: ID!, input: ViewSectionInput!): ViewSection!\n deleteViewSection(id: ID!): Boolean!\n reorderViewSections(tabId: ID!, sectionIds: [ID!]!): [ViewSection!]!\n }\n`;\n\nexport const viewTypeDefs = `\n ${viewType}\n ${viewQuery}\n ${viewMutation}\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/view/graphql/view.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const viewType = gql`\n type ViewWorkspace {\n activeTabId: ID\n tabs: [ViewTab!]!\n }\n\n type ViewTab {\n id: ID!\n title: String!\n order: Int!\n sections: [ViewSection!]!\n }\n\n type ViewSection {\n id: ID!\n tabId: ID!\n title: String!\n displayType: ViewDisplayType!\n displayOptions: ViewDisplayOptions!\n tagNames: [String!]!\n mode: TagMatchMode!\n propertyFilters: [ViewPropertyFilter!]!\n sortBy: ViewSortBy!\n sortOrder: ViewSortOrder!\n limit: Int!\n order: Int!\n }\n\n type ViewPropertyFilter {\n key: String!\n name: String!\n valueType: NotePropertyValueType!\n operator: ViewPropertyFilterOperator!\n value: String\n }\n\n type ViewDisplayOptions {\n tableColumns: [ViewTableColumn!]!\n }\n\n enum ViewDisplayType {\n list\n table\n calendar\n }\n\n enum ViewTableColumn {\n title\n tags\n properties\n createdAt\n updatedAt\n }\n\n enum ViewPropertyFilterOperator {\n equals\n notEquals\n contains\n notContains\n before\n after\n exists\n notExists\n }\n\n enum ViewSortBy {\n updatedAt\n createdAt\n title\n }\n\n enum ViewSortOrder {\n asc\n desc\n }\n\n input ViewPropertyFilterInput {\n key: String!\n valueType: NotePropertyValueType!\n operator: ViewPropertyFilterOperator!\n value: String\n }\n\n input ViewDisplayOptionsInput {\n tableColumns: [ViewTableColumn!]\n }\n\n input ViewSectionInput {\n title: String\n displayType: ViewDisplayType\n displayOptions: ViewDisplayOptionsInput\n tagNames: [String!]\n mode: TagMatchMode\n propertyFilters: [ViewPropertyFilterInput!]\n sortBy: ViewSortBy\n sortOrder: ViewSortOrder\n limit: Int\n }\n\n input NotesByPropertiesInput {\n tagNames: [String!]\n mode: TagMatchMode\n propertyFilters: [ViewPropertyFilterInput!]!\n sortBy: ViewSortBy\n sortOrder: ViewSortOrder\n }\n`;\n\nexport const viewQuery = gql`\n type Query {\n viewWorkspace: ViewWorkspace!\n viewSection(id: ID!): ViewSection\n viewSectionNotes(id: ID!, pagination: PaginationInput): Notes!\n notesByProperties(input: NotesByPropertiesInput!, pagination: PaginationInput): Notes!\n }\n`;\n\nexport const viewMutation = gql`\n type Mutation {\n createViewTab(title: String!): ViewTab!\n updateViewTab(id: ID!, title: String!): ViewTab!\n deleteViewTab(id: ID!): Boolean!\n setActiveViewTab(id: ID!): ViewWorkspace!\n reorderViewTabs(tabIds: [ID!]!): [ViewTab!]!\n createViewSection(tabId: ID!, input: ViewSectionInput!): ViewSection!\n updateViewSection(id: ID!, input: ViewSectionInput!): ViewSection!\n deleteViewSection(id: ID!): Boolean!\n reorderViewSections(tabId: ID!, sectionIds: [ID!]!): [ViewSection!]!\n }\n`;\n\nexport const viewTypeDefs = `\n ${viewType}\n ${viewQuery}\n ${viewMutation}\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4GjB,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASlB,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcrB,MAAM,eAAe;AAAA,MACtB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA;","names":[]}
|
|
@@ -15,6 +15,7 @@ const MAX_VIEW_PROPERTY_FILTERS = 10;
|
|
|
15
15
|
const DEFAULT_VIEW_DISPLAY_TYPE = "list";
|
|
16
16
|
const DEFAULT_VIEW_SORT_BY = "updatedAt";
|
|
17
17
|
const DEFAULT_VIEW_SORT_ORDER = "desc";
|
|
18
|
+
const DEFAULT_VIEW_TABLE_COLUMNS = ["title", "tags", "properties", "createdAt", "updatedAt"];
|
|
18
19
|
const orderedViewSectionTagsInclude = {
|
|
19
20
|
tags: {
|
|
20
21
|
orderBy: [{ order: "asc" }, { createdAt: "asc" }]
|
|
@@ -44,13 +45,19 @@ const normalizeViewTagNames = (values) => {
|
|
|
44
45
|
return Array.from(new Set(normalizedTagNames.map(normalizeTagName).filter(Boolean)));
|
|
45
46
|
};
|
|
46
47
|
const isViewPropertyFilterOperator = (value) => {
|
|
47
|
-
return value === "equals" || value === "before" || value === "after" || value === "exists" || value === "notExists";
|
|
48
|
+
return value === "equals" || value === "notEquals" || value === "contains" || value === "notContains" || value === "before" || value === "after" || value === "exists" || value === "notExists";
|
|
48
49
|
};
|
|
49
50
|
const isPropertyValueType = (value) => {
|
|
50
51
|
return value === "text" || value === "url" || value === "number" || value === "date" || value === "boolean" || value === "select";
|
|
51
52
|
};
|
|
53
|
+
const isViewTableColumn = (value) => {
|
|
54
|
+
return value === "title" || value === "tags" || value === "properties" || value === "createdAt" || value === "updatedAt";
|
|
55
|
+
};
|
|
52
56
|
const normalizeViewDisplayType = (value) => {
|
|
53
|
-
|
|
57
|
+
if (value === "table" || value === "calendar") {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
return DEFAULT_VIEW_DISPLAY_TYPE;
|
|
54
61
|
};
|
|
55
62
|
const normalizeViewSortBy = (value) => {
|
|
56
63
|
return value === "createdAt" || value === "title" ? value : DEFAULT_VIEW_SORT_BY;
|
|
@@ -58,6 +65,19 @@ const normalizeViewSortBy = (value) => {
|
|
|
58
65
|
const normalizeViewSortOrder = (value) => {
|
|
59
66
|
return value === "asc" ? "asc" : DEFAULT_VIEW_SORT_ORDER;
|
|
60
67
|
};
|
|
68
|
+
const normalizeViewTableColumns = (columns) => {
|
|
69
|
+
const normalizedColumns = (columns ?? []).filter(isViewTableColumn);
|
|
70
|
+
const uniqueColumns = Array.from(new Set(normalizedColumns));
|
|
71
|
+
if (uniqueColumns.length === 0) {
|
|
72
|
+
return [...DEFAULT_VIEW_TABLE_COLUMNS];
|
|
73
|
+
}
|
|
74
|
+
return uniqueColumns.includes("title") ? uniqueColumns : ["title", ...uniqueColumns];
|
|
75
|
+
};
|
|
76
|
+
const normalizeViewDisplayOptions = (options) => {
|
|
77
|
+
return {
|
|
78
|
+
tableColumns: normalizeViewTableColumns(options?.tableColumns)
|
|
79
|
+
};
|
|
80
|
+
};
|
|
61
81
|
const normalizeFilterValue = ({
|
|
62
82
|
value,
|
|
63
83
|
valueType,
|
|
@@ -88,7 +108,10 @@ const normalizeFilterValue = ({
|
|
|
88
108
|
if ((operator === "before" || operator === "after") && valueType !== "date" && valueType !== "number") {
|
|
89
109
|
throw new InvalidNotePropertyInputError("Before and after filters require date or number properties.");
|
|
90
110
|
}
|
|
91
|
-
if (
|
|
111
|
+
if ((operator === "contains" || operator === "notContains") && valueType !== "text" && valueType !== "url") {
|
|
112
|
+
throw new InvalidNotePropertyInputError("Contains filters require text or url properties.");
|
|
113
|
+
}
|
|
114
|
+
if (valueType === "url" && operator !== "contains" && operator !== "notContains") {
|
|
92
115
|
return normalizeUrlValue(normalizedValue);
|
|
93
116
|
}
|
|
94
117
|
return normalizedValue;
|
|
@@ -128,7 +151,8 @@ const parseStoredViewQuery = (value) => {
|
|
|
128
151
|
return {
|
|
129
152
|
propertyFilters: [],
|
|
130
153
|
sortBy: DEFAULT_VIEW_SORT_BY,
|
|
131
|
-
sortOrder: DEFAULT_VIEW_SORT_ORDER
|
|
154
|
+
sortOrder: DEFAULT_VIEW_SORT_ORDER,
|
|
155
|
+
displayOptions: normalizeViewDisplayOptions(null)
|
|
132
156
|
};
|
|
133
157
|
}
|
|
134
158
|
try {
|
|
@@ -136,21 +160,24 @@ const parseStoredViewQuery = (value) => {
|
|
|
136
160
|
return {
|
|
137
161
|
propertyFilters: normalizeViewPropertyFilters(parsed.propertyFilters ?? []),
|
|
138
162
|
sortBy: normalizeViewSortBy(parsed.sortBy),
|
|
139
|
-
sortOrder: normalizeViewSortOrder(parsed.sortOrder)
|
|
163
|
+
sortOrder: normalizeViewSortOrder(parsed.sortOrder),
|
|
164
|
+
displayOptions: normalizeViewDisplayOptions(parsed.displayOptions)
|
|
140
165
|
};
|
|
141
166
|
} catch {
|
|
142
167
|
return {
|
|
143
168
|
propertyFilters: [],
|
|
144
169
|
sortBy: DEFAULT_VIEW_SORT_BY,
|
|
145
|
-
sortOrder: DEFAULT_VIEW_SORT_ORDER
|
|
170
|
+
sortOrder: DEFAULT_VIEW_SORT_ORDER,
|
|
171
|
+
displayOptions: normalizeViewDisplayOptions(null)
|
|
146
172
|
};
|
|
147
173
|
}
|
|
148
174
|
};
|
|
149
|
-
const serializeStoredViewQuery = ({ propertyFilters, sortBy, sortOrder }) => {
|
|
175
|
+
const serializeStoredViewQuery = ({ propertyFilters, sortBy, sortOrder, displayOptions }) => {
|
|
150
176
|
return JSON.stringify({
|
|
151
177
|
propertyFilters,
|
|
152
178
|
sortBy,
|
|
153
|
-
sortOrder
|
|
179
|
+
sortOrder,
|
|
180
|
+
displayOptions
|
|
154
181
|
});
|
|
155
182
|
};
|
|
156
183
|
const clampViewSectionLimit = (value) => {
|
|
@@ -179,9 +206,11 @@ const normalizeViewSectionInput = (input) => {
|
|
|
179
206
|
const trimmedTitle = input.title?.trim() ?? "";
|
|
180
207
|
const sortBy = normalizeViewSortBy(input.sortBy);
|
|
181
208
|
const sortOrder = normalizeViewSortOrder(input.sortOrder);
|
|
209
|
+
const displayOptions = normalizeViewDisplayOptions(input.displayOptions);
|
|
182
210
|
return {
|
|
183
211
|
title: trimmedTitle || buildDefaultSectionTitle(tagNames, propertyFilters),
|
|
184
212
|
displayType: normalizeViewDisplayType(input.displayType),
|
|
213
|
+
displayOptions,
|
|
185
214
|
tagNames,
|
|
186
215
|
mode: input.mode === "or" ? "or" : "and",
|
|
187
216
|
propertyFilters,
|
|
@@ -245,6 +274,7 @@ const serializeViewSection = (section) => {
|
|
|
245
274
|
tabId: String(section.tabId),
|
|
246
275
|
title: section.title,
|
|
247
276
|
displayType: normalizeViewDisplayType(section.displayType),
|
|
277
|
+
displayOptions: query.displayOptions,
|
|
248
278
|
tagNames: section.tags.map((tag) => tag.name),
|
|
249
279
|
mode: section.mode,
|
|
250
280
|
propertyFilters: query.propertyFilters,
|
|
@@ -331,7 +361,8 @@ const buildStoredViewQuery = async (db, section) => {
|
|
|
331
361
|
return serializeStoredViewQuery({
|
|
332
362
|
propertyFilters,
|
|
333
363
|
sortBy: section.sortBy,
|
|
334
|
-
sortOrder: section.sortOrder
|
|
364
|
+
sortOrder: section.sortOrder,
|
|
365
|
+
displayOptions: section.displayOptions
|
|
335
366
|
});
|
|
336
367
|
};
|
|
337
368
|
const getNextTabOrder = async (db) => {
|
|
@@ -397,6 +428,9 @@ const buildPropertyFilterValueWhere = (filter) => {
|
|
|
397
428
|
if (filter.operator === "after") {
|
|
398
429
|
return { numberValue: { gt: numberValue } };
|
|
399
430
|
}
|
|
431
|
+
if (filter.operator === "notEquals") {
|
|
432
|
+
return { numberValue: { not: numberValue } };
|
|
433
|
+
}
|
|
400
434
|
return { numberValue };
|
|
401
435
|
}
|
|
402
436
|
case "date": {
|
|
@@ -407,16 +441,37 @@ const buildPropertyFilterValueWhere = (filter) => {
|
|
|
407
441
|
if (filter.operator === "after") {
|
|
408
442
|
return { dateValue: { gt: dateValue } };
|
|
409
443
|
}
|
|
444
|
+
if (filter.operator === "notEquals") {
|
|
445
|
+
return { dateValue: { not: dateValue } };
|
|
446
|
+
}
|
|
410
447
|
return { dateValue };
|
|
411
448
|
}
|
|
412
|
-
case "boolean":
|
|
449
|
+
case "boolean": {
|
|
450
|
+
if (filter.operator === "notEquals") {
|
|
451
|
+
return { boolValue: { not: filter.value === "true" } };
|
|
452
|
+
}
|
|
413
453
|
return { boolValue: filter.value === "true" };
|
|
454
|
+
}
|
|
414
455
|
case "select":
|
|
456
|
+
if (filter.operator === "notEquals") {
|
|
457
|
+
return { option: { is: { value: { not: normalizeSelectFilterValue(filter.value ?? "") } } } };
|
|
458
|
+
}
|
|
415
459
|
return { option: { is: { value: normalizeSelectFilterValue(filter.value ?? "") } } };
|
|
416
460
|
case "url":
|
|
417
461
|
case "text":
|
|
418
|
-
default:
|
|
419
|
-
|
|
462
|
+
default: {
|
|
463
|
+
const textValue = filter.value?.toLowerCase() ?? "";
|
|
464
|
+
if (filter.operator === "notEquals") {
|
|
465
|
+
return { textValueNormalized: { not: textValue } };
|
|
466
|
+
}
|
|
467
|
+
if (filter.operator === "contains") {
|
|
468
|
+
return { textValueNormalized: { contains: textValue } };
|
|
469
|
+
}
|
|
470
|
+
if (filter.operator === "notContains") {
|
|
471
|
+
return { textValueNormalized: { not: { contains: textValue } } };
|
|
472
|
+
}
|
|
473
|
+
return { textValueNormalized: textValue };
|
|
474
|
+
}
|
|
420
475
|
}
|
|
421
476
|
};
|
|
422
477
|
const buildPropertyFilterWhere = (filter) => {
|
|
@@ -496,22 +551,22 @@ const getViewSectionNotes = async (id, pagination) => {
|
|
|
496
551
|
notes
|
|
497
552
|
};
|
|
498
553
|
};
|
|
499
|
-
const
|
|
554
|
+
const getNotesByPropertiesWithDb = async (db, input, pagination) => {
|
|
500
555
|
const normalizedQuery = normalizeViewNotesQueryInput(input);
|
|
501
556
|
if (normalizedQuery.propertyFilters.length === 0) {
|
|
502
557
|
throw new InvalidNotePropertyInputError("At least one property filter is required.");
|
|
503
558
|
}
|
|
504
559
|
const query = {
|
|
505
560
|
...normalizedQuery,
|
|
506
|
-
propertyFilters: await hydratePropertyFilters(
|
|
561
|
+
propertyFilters: await hydratePropertyFilters(db, normalizedQuery.propertyFilters, {
|
|
507
562
|
validateSelectOptions: true
|
|
508
563
|
})
|
|
509
564
|
};
|
|
510
565
|
const normalizedPagination = normalizeViewNotesPagination(pagination);
|
|
511
566
|
const where = buildViewNotesWhere(query);
|
|
512
567
|
const [totalCount, notes] = await Promise.all([
|
|
513
|
-
|
|
514
|
-
|
|
568
|
+
db.note.count({ where }),
|
|
569
|
+
db.note.findMany({
|
|
515
570
|
orderBy: buildViewNotesOrderBy(query),
|
|
516
571
|
where,
|
|
517
572
|
take: normalizedPagination.limit,
|
|
@@ -523,6 +578,9 @@ const getNotesByProperties = async (input, pagination) => {
|
|
|
523
578
|
notes
|
|
524
579
|
};
|
|
525
580
|
};
|
|
581
|
+
const getNotesByProperties = async (input, pagination) => {
|
|
582
|
+
return getNotesByPropertiesWithDb(models, input, pagination);
|
|
583
|
+
};
|
|
526
584
|
const createViewTab = async (title) => {
|
|
527
585
|
return models.$transaction(async (tx) => {
|
|
528
586
|
const workspace = await ensureViewWorkspace(tx);
|
|
@@ -736,15 +794,18 @@ export {
|
|
|
736
794
|
deleteViewSection,
|
|
737
795
|
deleteViewTab,
|
|
738
796
|
getNotesByProperties,
|
|
797
|
+
getNotesByPropertiesWithDb,
|
|
739
798
|
getViewSectionById,
|
|
740
799
|
getViewSectionNotes,
|
|
741
800
|
getViewWorkspace,
|
|
742
801
|
hydratePropertyFilters,
|
|
802
|
+
normalizeViewDisplayOptions,
|
|
743
803
|
normalizeViewNotesPagination,
|
|
744
804
|
normalizeViewNotesQueryInput,
|
|
745
805
|
normalizeViewPropertyFilters,
|
|
746
806
|
normalizeViewSectionInput,
|
|
747
807
|
normalizeViewTabTitle,
|
|
808
|
+
normalizeViewTableColumns,
|
|
748
809
|
normalizeViewTagNames,
|
|
749
810
|
pickNextActiveViewTabId,
|
|
750
811
|
reorderViewSections,
|