@mp-lb/mdkit 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +8 -2
  2. package/dist/collaboration/useMdKitCollaboration.d.ts +5 -0
  3. package/dist/collaboration/useMdKitCollaboration.d.ts.map +1 -0
  4. package/dist/collaboration/useMdKitCollaboration.js +4 -0
  5. package/dist/core/checkpointPolicy.d.ts +10 -0
  6. package/dist/core/checkpointPolicy.d.ts.map +1 -0
  7. package/dist/core/checkpointPolicy.js +9 -0
  8. package/dist/core/documentEngine.d.ts +1 -0
  9. package/dist/core/documentEngine.d.ts.map +1 -0
  10. package/dist/core/index.d.ts +1 -0
  11. package/dist/core/index.d.ts.map +1 -0
  12. package/dist/document/MdKitConflictPanel.d.ts +5 -0
  13. package/dist/document/MdKitConflictPanel.d.ts.map +1 -0
  14. package/dist/document/MdKitConflictPanel.js +4 -0
  15. package/dist/document/MdKitDocumentToolbar.d.ts +6 -0
  16. package/dist/document/MdKitDocumentToolbar.d.ts.map +1 -0
  17. package/dist/document/MdKitDocumentToolbar.js +5 -0
  18. package/dist/document/documentTypes.d.ts +6 -0
  19. package/dist/document/documentTypes.d.ts.map +1 -0
  20. package/dist/document/useMdKitDocument.d.ts +5 -0
  21. package/dist/document/useMdKitDocument.d.ts.map +1 -0
  22. package/dist/document/useMdKitDocument.js +4 -0
  23. package/dist/fastify.d.ts +1 -0
  24. package/dist/fastify.d.ts.map +1 -0
  25. package/dist/index.d.ts +4 -1
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/markdown/MarkdownBubbleMenu.d.ts +1 -0
  28. package/dist/markdown/MarkdownBubbleMenu.d.ts.map +1 -0
  29. package/dist/markdown/MarkdownPasteExtension.d.ts +1 -0
  30. package/dist/markdown/MarkdownPasteExtension.d.ts.map +1 -0
  31. package/dist/markdown/MarkdownSearchExtension.d.ts +1 -0
  32. package/dist/markdown/MarkdownSearchExtension.d.ts.map +1 -0
  33. package/dist/markdown/MarkdownSearchPanel.d.ts +1 -0
  34. package/dist/markdown/MarkdownSearchPanel.d.ts.map +1 -0
  35. package/dist/markdown/MdKitEditor.d.ts +11 -0
  36. package/dist/markdown/MdKitEditor.d.ts.map +1 -0
  37. package/dist/markdown/MdKitEditor.js +10 -2
  38. package/dist/markdown/MdKitView.d.ts +9 -1
  39. package/dist/markdown/MdKitView.d.ts.map +1 -0
  40. package/dist/markdown/MdKitView.js +7 -2
  41. package/dist/markdown/TiptapMarkdownSurface.d.ts +1 -0
  42. package/dist/markdown/TiptapMarkdownSurface.d.ts.map +1 -0
  43. package/dist/markdown/TiptapMarkdownSurface.js +10 -22
  44. package/dist/markdown/createMdKitTiptapExtensions.d.ts +1 -0
  45. package/dist/markdown/createMdKitTiptapExtensions.d.ts.map +1 -0
  46. package/dist/markdown/editorDebug.d.ts +1 -0
  47. package/dist/markdown/editorDebug.d.ts.map +1 -0
  48. package/dist/markdown/markdownFenceRanges.d.ts +1 -0
  49. package/dist/markdown/markdownFenceRanges.d.ts.map +1 -0
  50. package/dist/markdown/normalizeMarkdownSerialization.d.ts +1 -0
  51. package/dist/markdown/normalizeMarkdownSerialization.d.ts.map +1 -0
  52. package/dist/markdown/prepareMarkdownForEditorHydration.d.ts +1 -0
  53. package/dist/markdown/prepareMarkdownForEditorHydration.d.ts.map +1 -0
  54. package/dist/markdown/preserveMarkdownWhitespace.d.ts +1 -0
  55. package/dist/markdown/preserveMarkdownWhitespace.d.ts.map +1 -0
  56. package/dist/markdown/yamlFrontMatter.d.ts +1 -0
  57. package/dist/markdown/yamlFrontMatter.d.ts.map +1 -0
  58. package/dist/server.d.ts +1 -0
  59. package/dist/server.d.ts.map +1 -0
  60. package/dist/theme/MdKitThemeEditor.d.ts +5 -0
  61. package/dist/theme/MdKitThemeEditor.d.ts.map +1 -0
  62. package/dist/theme/MdKitThemeEditor.js +4 -0
  63. package/dist/theme/editorTheme.d.ts +1 -0
  64. package/dist/theme/editorTheme.d.ts.map +1 -0
  65. package/dist/theme/editorTheme.js +8 -8
  66. package/dist/transport/backend.d.ts +13 -0
  67. package/dist/transport/backend.d.ts.map +1 -0
  68. package/dist/transport/backend.js +6 -0
  69. package/dist/transport/fastify.d.ts +5 -0
  70. package/dist/transport/fastify.d.ts.map +1 -0
  71. package/dist/transport/fastify.js +4 -0
  72. package/dist/transport/http.d.ts +1 -0
  73. package/dist/transport/http.d.ts.map +1 -0
  74. package/dist/transport/index.d.ts +1 -0
  75. package/dist/transport/index.d.ts.map +1 -0
  76. package/dist/transport/rest.d.ts +6 -0
  77. package/dist/transport/rest.d.ts.map +1 -0
  78. package/dist/transport/rest.js +5 -0
  79. package/dist/transport/store.d.ts +1 -0
  80. package/dist/transport/store.d.ts.map +1 -0
  81. package/dist/transport/trpcClient.d.ts +8 -0
  82. package/dist/transport/trpcClient.d.ts.map +1 -0
  83. package/dist/transport/trpcClient.js +7 -0
  84. package/dist/transport/trpcServer.d.ts +6 -0
  85. package/dist/transport/trpcServer.d.ts.map +1 -0
  86. package/dist/transport/trpcServer.js +5 -0
  87. package/dist/trpc/client.d.ts +1 -0
  88. package/dist/trpc/client.d.ts.map +1 -0
  89. package/dist/trpc/server.d.ts +1 -0
  90. package/dist/trpc/server.d.ts.map +1 -0
  91. package/dist/trpc.d.ts +1 -0
  92. package/dist/trpc.d.ts.map +1 -0
  93. package/dist/ui/joinClassNames.d.ts +1 -0
  94. package/dist/ui/joinClassNames.d.ts.map +1 -0
  95. package/dist/versioning/VersionHistoryPanel.d.ts +5 -0
  96. package/dist/versioning/VersionHistoryPanel.d.ts.map +1 -0
  97. package/dist/versioning/VersionHistoryPanel.js +4 -0
  98. package/dist/versioning/useMdKitDocumentVersions.d.ts +5 -0
  99. package/dist/versioning/useMdKitDocumentVersions.d.ts.map +1 -0
  100. package/dist/versioning/useMdKitDocumentVersions.js +4 -0
  101. package/dist/yjs/MdKitMarkdownYjs.d.ts +1 -0
  102. package/dist/yjs/MdKitMarkdownYjs.d.ts.map +1 -0
  103. package/dist/yjs/index.d.ts +1 -0
  104. package/dist/yjs/index.d.ts.map +1 -0
  105. package/package.json +10 -12
  106. package/src/collaboration/useMdKitCollaboration.ts +528 -0
  107. package/src/core/checkpointPolicy.ts +107 -0
  108. package/src/core/documentEngine.ts +175 -0
  109. package/src/core/index.ts +33 -0
  110. package/src/document/MdKitConflictPanel.tsx +129 -0
  111. package/src/document/MdKitDocumentToolbar.tsx +141 -0
  112. package/src/document/documentTypes.ts +89 -0
  113. package/src/document/useMdKitDocument.ts +543 -0
  114. package/src/fastify.ts +6 -0
  115. package/src/index.ts +89 -0
  116. package/src/markdown/MarkdownBubbleMenu.tsx +271 -0
  117. package/src/markdown/MarkdownPasteExtension.ts +81 -0
  118. package/src/markdown/MarkdownSearchExtension.ts +77 -0
  119. package/src/markdown/MarkdownSearchPanel.tsx +98 -0
  120. package/src/markdown/MdKitEditor.tsx +75 -0
  121. package/src/markdown/MdKitView.tsx +80 -0
  122. package/src/markdown/TiptapMarkdownSurface.tsx +923 -0
  123. package/src/markdown/createMdKitTiptapExtensions.ts +42 -0
  124. package/src/markdown/editorDebug.ts +5 -0
  125. package/src/markdown/markdownFenceRanges.ts +68 -0
  126. package/src/markdown/normalizeMarkdownSerialization.ts +55 -0
  127. package/src/markdown/prepareMarkdownForEditorHydration.ts +23 -0
  128. package/src/markdown/preserveMarkdownWhitespace.ts +143 -0
  129. package/src/markdown/yamlFrontMatter.ts +135 -0
  130. package/src/server.ts +6 -0
  131. package/src/styles.css +125 -53
  132. package/src/theme/MdKitThemeEditor.tsx +134 -0
  133. package/src/theme/editorTheme.ts +72 -0
  134. package/src/transport/backend.ts +220 -0
  135. package/src/transport/fastify.ts +57 -0
  136. package/src/transport/http.ts +126 -0
  137. package/src/transport/index.ts +12 -0
  138. package/src/transport/rest.ts +80 -0
  139. package/src/transport/store.ts +45 -0
  140. package/src/transport/trpcClient.ts +90 -0
  141. package/src/transport/trpcServer.ts +66 -0
  142. package/src/trpc/client.ts +11 -0
  143. package/src/trpc/server.ts +12 -0
  144. package/src/trpc.ts +11 -0
  145. package/src/ui/joinClassNames.ts +3 -0
  146. package/src/versioning/VersionHistoryPanel.tsx +146 -0
  147. package/src/versioning/useMdKitDocumentVersions.ts +146 -0
  148. package/src/yjs/MdKitMarkdownYjs.ts +111 -0
  149. package/src/yjs/index.ts +8 -0
  150. package/docs/.vitepress/config.ts +0 -47
  151. package/docs/api.md +0 -512
  152. package/docs/architecture.md +0 -96
  153. package/docs/collaboration-persistence.md +0 -147
  154. package/docs/index.md +0 -341
  155. package/docs/permissions.md +0 -139
  156. package/docs/plain-text.md +0 -131
  157. package/docs/rest.md +0 -98
  158. package/docs/shadcn.md +0 -125
  159. package/docs/styling.md +0 -373
  160. package/docs/use-cases.md +0 -148
@@ -0,0 +1,42 @@
1
+ import type { Extensions } from "@tiptap/core";
2
+ import { Markdown } from "@tiptap/markdown";
3
+ import Placeholder from "@tiptap/extension-placeholder";
4
+ import StarterKit from "@tiptap/starter-kit";
5
+ import { MarkdownPasteExtension } from "./MarkdownPasteExtension";
6
+ import { MarkdownSearchExtension } from "./MarkdownSearchExtension";
7
+
8
+ type CreateMdKitTiptapExtensionsOptions = {
9
+ placeholder?: string;
10
+ undoRedo?: boolean;
11
+ };
12
+
13
+ export const defaultMdKitMarkdownPlaceholder = "Start writing...";
14
+
15
+ export const createMdKitTiptapExtensions = ({
16
+ placeholder = defaultMdKitMarkdownPlaceholder,
17
+ undoRedo = true,
18
+ }: CreateMdKitTiptapExtensionsOptions = {}): Extensions => [
19
+ StarterKit.configure({
20
+ heading: { levels: [1, 2, 3, 4, 5, 6] },
21
+ link: {
22
+ HTMLAttributes: {
23
+ rel: "noopener noreferrer",
24
+ target: "_blank",
25
+ },
26
+ autolink: true,
27
+ linkOnPaste: true,
28
+ openOnClick: true,
29
+ },
30
+ undoRedo: undoRedo ? undefined : false,
31
+ }),
32
+ Placeholder.configure({
33
+ placeholder,
34
+ }),
35
+ Markdown.configure({
36
+ markedOptions: {
37
+ gfm: true,
38
+ },
39
+ }),
40
+ MarkdownPasteExtension,
41
+ MarkdownSearchExtension,
42
+ ];
@@ -0,0 +1,5 @@
1
+ export type MdKitEditorDebugEvent = {
2
+ detail: Record<string, unknown>;
3
+ timestamp: number;
4
+ type: string;
5
+ };
@@ -0,0 +1,68 @@
1
+ export type MarkdownFenceRange = {
2
+ end: number;
3
+ start: number;
4
+ };
5
+
6
+ const fenceStartPattern = /^ {0,3}(`{3,}|~{3,})/;
7
+
8
+ export const getMarkdownFenceRanges = (
9
+ markdown: string,
10
+ ): MarkdownFenceRange[] => {
11
+ const ranges: MarkdownFenceRange[] = [];
12
+ let activeFence: {
13
+ char: "`" | "~";
14
+ length: number;
15
+ start: number;
16
+ } | null = null;
17
+ let lineStart = 0;
18
+
19
+ while (lineStart < markdown.length) {
20
+ const newlineIndex = markdown.indexOf("\n", lineStart);
21
+ const lineEnd = newlineIndex === -1 ? markdown.length : newlineIndex;
22
+
23
+ const nextLineStart =
24
+ newlineIndex === -1 ? markdown.length : newlineIndex + 1;
25
+
26
+ const line = markdown.slice(lineStart, lineEnd);
27
+ const fenceMatch = line.match(fenceStartPattern);
28
+
29
+ if (fenceMatch) {
30
+ const marker = fenceMatch[1] ?? "";
31
+ const markerChar = marker[0] as "`" | "~";
32
+
33
+ if (!activeFence) {
34
+ activeFence = {
35
+ char: markerChar,
36
+ length: marker.length,
37
+ start: lineStart,
38
+ };
39
+ } else if (
40
+ markerChar === activeFence.char &&
41
+ marker.length >= activeFence.length
42
+ ) {
43
+ ranges.push({
44
+ start: activeFence.start,
45
+ end: nextLineStart,
46
+ });
47
+
48
+ activeFence = null;
49
+ }
50
+ }
51
+
52
+ lineStart = nextLineStart;
53
+ }
54
+
55
+ if (activeFence) {
56
+ ranges.push({
57
+ start: activeFence.start,
58
+ end: markdown.length,
59
+ });
60
+ }
61
+
62
+ return ranges;
63
+ };
64
+
65
+ export const isInsideMarkdownFence = (
66
+ index: number,
67
+ ranges: MarkdownFenceRange[],
68
+ ) => ranges.some((range) => index >= range.start && index < range.end - 1);
@@ -0,0 +1,55 @@
1
+ const placeholderLinePattern = /^[ \t]*(?:&nbsp;|\u00a0)[ \t]*$/;
2
+
3
+ const normalizePlaceholderParagraphs = (markdown: string) => {
4
+ const lines = markdown.split("\n");
5
+ const normalizedLines: string[] = [];
6
+ let index = 0;
7
+
8
+ while (index < lines.length) {
9
+ const line = lines[index] ?? "";
10
+
11
+ if (line !== "" && !placeholderLinePattern.test(line)) {
12
+ normalizedLines.push(line);
13
+ index += 1;
14
+ continue;
15
+ }
16
+
17
+ const start = index;
18
+ let placeholderCount = 0;
19
+
20
+ while (
21
+ index < lines.length &&
22
+ ((lines[index] ?? "") === "" ||
23
+ placeholderLinePattern.test(lines[index] ?? ""))
24
+ ) {
25
+ if (placeholderLinePattern.test(lines[index] ?? "")) {
26
+ placeholderCount += 1;
27
+ }
28
+
29
+ index += 1;
30
+ }
31
+
32
+ if (placeholderCount === 0) {
33
+ normalizedLines.push(...lines.slice(start, index));
34
+ continue;
35
+ }
36
+
37
+ normalizedLines.push(
38
+ ...Array.from({ length: placeholderCount + 1 }, () => ""),
39
+ );
40
+ }
41
+
42
+ const normalizedMarkdown = normalizedLines.join("\n");
43
+
44
+ return placeholderLinePattern.test(normalizedMarkdown.trim())
45
+ ? ""
46
+ : normalizedMarkdown;
47
+ };
48
+
49
+ export const normalizeMarkdownSerialization = (markdown: string) =>
50
+ normalizePlaceholderParagraphs(
51
+ markdown.replace(
52
+ /^([ \t]*(?:[-*+]|\d+[.)])[ \t]+)(?:&nbsp;|\u00a0)[ \t]*$/gm,
53
+ "$1",
54
+ ),
55
+ );
@@ -0,0 +1,23 @@
1
+ import {
2
+ getMarkdownFenceRanges,
3
+ isInsideMarkdownFence,
4
+ } from "./markdownFenceRanges";
5
+
6
+ const expandedBlankLineRunPattern = /\n{3,}/g;
7
+
8
+ export const prepareMarkdownForEditorHydration = (markdown: string) => {
9
+ const fenceRanges = getMarkdownFenceRanges(markdown);
10
+
11
+ return markdown.replace(expandedBlankLineRunPattern, (newlineRun, offset) => {
12
+ if (isInsideMarkdownFence(offset, fenceRanges)) {
13
+ return newlineRun;
14
+ }
15
+
16
+ const emptyParagraphs = Array.from(
17
+ { length: newlineRun.length - 2 },
18
+ () => "&nbsp;",
19
+ ).join("\n\n");
20
+
21
+ return `\n\n${emptyParagraphs}\n\n`;
22
+ });
23
+ };
@@ -0,0 +1,143 @@
1
+ import {
2
+ getMarkdownFenceRanges,
3
+ isInsideMarkdownFence,
4
+ } from "./markdownFenceRanges";
5
+
6
+ type MarkdownBlockToken = {
7
+ content: string;
8
+ separatorAfter: string;
9
+ };
10
+
11
+ const tokenizeMarkdownBlocks = (markdown: string): MarkdownBlockToken[] => {
12
+ const ranges = getMarkdownFenceRanges(markdown);
13
+ const tokens: MarkdownBlockToken[] = [];
14
+ const separatorPattern = /\n{2,}/g;
15
+ let contentStart = 0;
16
+ let match: RegExpExecArray | null;
17
+
18
+ while ((match = separatorPattern.exec(markdown))) {
19
+ const separatorStart = match.index;
20
+ const separator = match[0] ?? "";
21
+
22
+ if (isInsideMarkdownFence(separatorStart, ranges)) {
23
+ continue;
24
+ }
25
+
26
+ tokens.push({
27
+ content: markdown.slice(contentStart, separatorStart),
28
+ separatorAfter: separator,
29
+ });
30
+
31
+ contentStart = separatorStart + separator.length;
32
+ }
33
+
34
+ tokens.push({
35
+ content: markdown.slice(contentStart),
36
+ separatorAfter: "",
37
+ });
38
+
39
+ return tokens;
40
+ };
41
+
42
+ const blockPairKey = (left: MarkdownBlockToken, right: MarkdownBlockToken) =>
43
+ `${left.content.trim()}\u0000${right.content.trim()}`;
44
+
45
+ const leadingNewlineRunPattern = /^\n+/;
46
+ const trailingNewlineRunPattern = /\n+$/;
47
+
48
+ const preserveOuterBlankLines = (
49
+ previousRawMarkdown: string,
50
+ nextSerializedMarkdown: string,
51
+ ) => {
52
+ const previousLeading = previousRawMarkdown.match(leadingNewlineRunPattern);
53
+ const nextLeading = nextSerializedMarkdown.match(leadingNewlineRunPattern);
54
+ const previousTrailing = previousRawMarkdown.match(trailingNewlineRunPattern);
55
+ const nextTrailing = nextSerializedMarkdown.match(trailingNewlineRunPattern);
56
+
57
+ const isBodyUnchanged =
58
+ previousRawMarkdown.trim() === nextSerializedMarkdown.trim();
59
+
60
+ let nextMarkdown = nextSerializedMarkdown;
61
+
62
+ if (
63
+ isBodyUnchanged &&
64
+ previousLeading &&
65
+ previousLeading[0].length > (nextLeading?.[0].length ?? 0)
66
+ ) {
67
+ nextMarkdown = `${previousLeading[0]}${nextMarkdown.trimStart()}`;
68
+ }
69
+
70
+ if (
71
+ isBodyUnchanged &&
72
+ previousTrailing &&
73
+ previousTrailing[0].length > (nextTrailing?.[0].length ?? 0)
74
+ ) {
75
+ nextMarkdown = `${nextMarkdown.trimEnd()}${previousTrailing[0]}`;
76
+ }
77
+
78
+ return nextMarkdown;
79
+ };
80
+
81
+ export const preserveMarkdownWhitespace = (
82
+ previousRawMarkdown: string,
83
+ nextSerializedMarkdown: string,
84
+ ) => {
85
+ const outerPreservedMarkdown = preserveOuterBlankLines(
86
+ previousRawMarkdown,
87
+ nextSerializedMarkdown,
88
+ );
89
+
90
+ if (
91
+ previousRawMarkdown === outerPreservedMarkdown ||
92
+ !previousRawMarkdown.includes("\n\n") ||
93
+ !outerPreservedMarkdown.includes("\n\n")
94
+ ) {
95
+ return outerPreservedMarkdown;
96
+ }
97
+
98
+ const previousTokens = tokenizeMarkdownBlocks(previousRawMarkdown);
99
+ const nextTokens = tokenizeMarkdownBlocks(outerPreservedMarkdown);
100
+
101
+ if (previousTokens.length < 2 || nextTokens.length < 2) {
102
+ return outerPreservedMarkdown;
103
+ }
104
+
105
+ const preservedSeparators = new Map<string, string>();
106
+
107
+ for (let index = 0; index < previousTokens.length - 1; index += 1) {
108
+ const left = previousTokens[index];
109
+ const right = previousTokens[index + 1];
110
+
111
+ if (!left || !right || left.separatorAfter.length <= 2) {
112
+ continue;
113
+ }
114
+
115
+ preservedSeparators.set(blockPairKey(left, right), left.separatorAfter);
116
+ }
117
+
118
+ if (preservedSeparators.size === 0) {
119
+ return outerPreservedMarkdown;
120
+ }
121
+
122
+ return nextTokens
123
+ .map((token, index) => {
124
+ const nextToken = nextTokens[index + 1];
125
+
126
+ if (!nextToken || !token.separatorAfter) {
127
+ return token.content;
128
+ }
129
+
130
+ const preservedSeparator = preservedSeparators.get(
131
+ blockPairKey(token, nextToken),
132
+ );
133
+
134
+ const separator =
135
+ preservedSeparator &&
136
+ preservedSeparator.length > token.separatorAfter.length
137
+ ? preservedSeparator
138
+ : token.separatorAfter;
139
+
140
+ return `${token.content}${separator}`;
141
+ })
142
+ .join("");
143
+ };
@@ -0,0 +1,135 @@
1
+ import { parseDocument } from "yaml";
2
+
3
+ export type MdKitYamlFrontMatter = {
4
+ data: unknown;
5
+ raw: string;
6
+ trailingWhitespace: string;
7
+ yaml: string;
8
+ };
9
+
10
+ export type MdKitYamlFrontMatterExtraction = {
11
+ body: string;
12
+ errors: string[];
13
+ frontMatter: MdKitYamlFrontMatter | null;
14
+ };
15
+
16
+ const delimiter = "---";
17
+
18
+ const getLineEnd = (markdown: string, lineStart: number) => {
19
+ const newlineIndex = markdown.indexOf("\n", lineStart);
20
+
21
+ if (newlineIndex === -1) {
22
+ return {
23
+ contentEnd: markdown.length,
24
+ lineEnd: markdown.length,
25
+ newline: "",
26
+ };
27
+ }
28
+
29
+ const contentEnd =
30
+ newlineIndex > lineStart && markdown[newlineIndex - 1] === "\r"
31
+ ? newlineIndex - 1
32
+ : newlineIndex;
33
+
34
+ return {
35
+ contentEnd,
36
+ lineEnd: newlineIndex + 1,
37
+ newline: markdown.slice(contentEnd, newlineIndex + 1),
38
+ };
39
+ };
40
+
41
+ const getNextBodyStart = (markdown: string, lineStart: number) => {
42
+ let bodyStart = lineStart;
43
+
44
+ while (bodyStart < markdown.length) {
45
+ const lineEnd = getLineEnd(markdown, bodyStart);
46
+ const line = markdown.slice(bodyStart, lineEnd.contentEnd);
47
+
48
+ if (!/^[ \t]*$/.test(line)) {
49
+ break;
50
+ }
51
+
52
+ bodyStart = lineEnd.lineEnd;
53
+ }
54
+
55
+ return bodyStart;
56
+ };
57
+
58
+ export const parseYamlFrontMatter = (yaml: string): unknown => {
59
+ const document = parseDocument(yaml, { prettyErrors: false });
60
+
61
+ if (document.errors.length > 0) {
62
+ throw new Error(document.errors.map((error) => error.message).join("\n"));
63
+ }
64
+
65
+ return document.toJSON();
66
+ };
67
+
68
+ export const extractYamlFrontMatter = (
69
+ markdown: string,
70
+ ): MdKitYamlFrontMatterExtraction => {
71
+ const openingLine = getLineEnd(markdown, 0);
72
+
73
+ if (markdown.slice(0, openingLine.contentEnd) !== delimiter) {
74
+ return { body: markdown, errors: [], frontMatter: null };
75
+ }
76
+
77
+ let lineStart = openingLine.lineEnd;
78
+
79
+ while (lineStart < markdown.length) {
80
+ const lineEnd = getLineEnd(markdown, lineStart);
81
+ const line = markdown.slice(lineStart, lineEnd.contentEnd);
82
+
83
+ if (line !== delimiter) {
84
+ lineStart = lineEnd.lineEnd;
85
+ continue;
86
+ }
87
+
88
+ const bodyStart = getNextBodyStart(markdown, lineEnd.lineEnd);
89
+ const raw = markdown.slice(0, bodyStart);
90
+ const yaml = markdown.slice(openingLine.lineEnd, lineStart);
91
+ const trailingWhitespace = markdown.slice(lineEnd.lineEnd, bodyStart);
92
+
93
+ try {
94
+ const data = parseYamlFrontMatter(yaml);
95
+
96
+ return {
97
+ body: markdown.slice(bodyStart),
98
+ errors: [],
99
+ frontMatter: {
100
+ data,
101
+ raw,
102
+ trailingWhitespace,
103
+ yaml,
104
+ },
105
+ };
106
+ } catch (error) {
107
+ return {
108
+ body: markdown,
109
+ errors: [error instanceof Error ? error.message : String(error)],
110
+ frontMatter: null,
111
+ };
112
+ }
113
+ }
114
+
115
+ return { body: markdown, errors: [], frontMatter: null };
116
+ };
117
+
118
+ export const hasYamlFrontMatter = (markdown: string) =>
119
+ extractYamlFrontMatter(markdown).frontMatter !== null;
120
+
121
+ export const removeYamlFrontMatter = (markdown: string) =>
122
+ extractYamlFrontMatter(markdown).body;
123
+
124
+ export const prependYamlFrontMatter = (
125
+ frontMatter: MdKitYamlFrontMatter | string | null,
126
+ body: string,
127
+ ) => {
128
+ if (!frontMatter) {
129
+ return body;
130
+ }
131
+
132
+ const raw = typeof frontMatter === "string" ? frontMatter : frontMatter.raw;
133
+
134
+ return `${raw}${body}`;
135
+ };
package/src/server.ts ADDED
@@ -0,0 +1,6 @@
1
+ export { createMdKitBackend } from "./transport/backend";
2
+ export type {
3
+ CreateMdKitBackendOptions,
4
+ MdKitBackendStore,
5
+ MdKitCreateCheckpointInput,
6
+ } from "./transport/backend";