@bendyline/docblocks 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-2D7Y4IR3.js → chunk-4437YWLE.js} +123 -67
  3. package/dist/{chunk-5MYAICRE.js → chunk-45T5AFCN.js} +44 -57
  4. package/dist/{chunk-PGR7LK6D.js → chunk-6L3JRDE7.js} +14 -6
  5. package/dist/{chunk-RVQHY3XU.js → chunk-6XHOOBYF.js} +0 -1
  6. package/dist/chunk-73OOROXH.js +8 -0
  7. package/dist/{chunk-SI2LU2RP.js → chunk-AYWKIZLD.js} +1 -1
  8. package/dist/{chunk-GCPBEMMH.js → chunk-BVY7GGNV.js} +10 -21
  9. package/dist/{chunk-AEHCVWU5.js → chunk-DZB666V7.js} +0 -1
  10. package/dist/{chunk-75UUF6C4.js → chunk-HI7QEWL6.js} +18 -10
  11. package/dist/chunk-KQ6756AJ.js +28 -0
  12. package/dist/chunk-LMMOFRGQ.js +148 -0
  13. package/dist/{chunk-FNW556FN.js → chunk-M2PUNKHS.js} +0 -1
  14. package/dist/chunk-M5ZNQGR5.js +19 -0
  15. package/dist/chunk-M62ZGJ5I.js +51 -0
  16. package/dist/chunk-OQCPBOQA.js +168 -0
  17. package/dist/{chunk-EGNDADXB.js → chunk-QI6YKTQV.js} +2 -3
  18. package/dist/chunk-SJBB5OD7.js +227 -0
  19. package/dist/{chunk-EZKJZUNJ.js → chunk-XAMILHQP.js} +0 -1
  20. package/dist/{chunk-J32GU3F3.js → chunk-YM7O3PWO.js} +249 -222
  21. package/dist/{chunk-RQJRQF46.js → chunk-ZO7G53QP.js} +41 -49
  22. package/dist/document/index.d.ts +358 -6
  23. package/dist/document/index.js +24 -4
  24. package/dist/filesystem/electron.d.ts +73 -4
  25. package/dist/filesystem/electron.js +16 -4
  26. package/dist/filesystem/index.d.ts +228 -22
  27. package/dist/filesystem/index.js +133 -20
  28. package/dist/filesystem/indexeddb.d.ts +284 -5
  29. package/dist/filesystem/indexeddb.js +22 -5
  30. package/dist/filesystem/memory.d.ts +152 -4
  31. package/dist/filesystem/memory.js +12 -4
  32. package/dist/filesystem/native.d.ts +136 -4
  33. package/dist/filesystem/native.js +27 -4
  34. package/dist/host/index.d.ts +32 -9
  35. package/dist/host/index.js +26 -30
  36. package/dist/index.d.ts +14 -10
  37. package/dist/index.js +204 -10
  38. package/dist/mcp/index.d.ts +33 -4
  39. package/dist/mcp/index.js +31 -4
  40. package/dist/mcp/zod.d.ts +645 -643
  41. package/dist/mcp/zod.js +700 -885
  42. package/dist/share/index.d.ts +43 -0
  43. package/dist/share/index.js +12 -0
  44. package/dist/{types-CsX6EN2t.d.ts → types-BEg5SQ1_.d.ts} +1 -1
  45. package/dist/{types-eOlt9J7n.d.ts → types-CueK0tvU.d.ts} +34 -11
  46. package/dist/vscode/index.d.ts +225 -3
  47. package/dist/vscode/index.js +389 -3
  48. package/dist/workspace/index.d.ts +141 -4
  49. package/dist/workspace/index.js +28 -3
  50. package/package.json +10 -5
  51. package/dist/chunk-2D7Y4IR3.js.map +0 -1
  52. package/dist/chunk-5MYAICRE.js.map +0 -1
  53. package/dist/chunk-75UUF6C4.js.map +0 -1
  54. package/dist/chunk-AEHCVWU5.js.map +0 -1
  55. package/dist/chunk-AYZ4HN5M.js +0 -20
  56. package/dist/chunk-AYZ4HN5M.js.map +0 -1
  57. package/dist/chunk-EGNDADXB.js.map +0 -1
  58. package/dist/chunk-EZKJZUNJ.js.map +0 -1
  59. package/dist/chunk-FNW556FN.js.map +0 -1
  60. package/dist/chunk-GCPBEMMH.js.map +0 -1
  61. package/dist/chunk-J32GU3F3.js.map +0 -1
  62. package/dist/chunk-PGR7LK6D.js.map +0 -1
  63. package/dist/chunk-RQJRQF46.js.map +0 -1
  64. package/dist/chunk-RVQHY3XU.js.map +0 -1
  65. package/dist/chunk-SI2LU2RP.js.map +0 -1
  66. package/dist/chunk-W5UUUII5.js +0 -142
  67. package/dist/chunk-W5UUUII5.js.map +0 -1
  68. package/dist/document/document-session.d.ts +0 -127
  69. package/dist/document/document-session.d.ts.map +0 -1
  70. package/dist/document/document-session.js +0 -749
  71. package/dist/document/document-session.js.map +0 -1
  72. package/dist/document/filesystem-target.d.ts +0 -17
  73. package/dist/document/filesystem-target.d.ts.map +0 -1
  74. package/dist/document/filesystem-target.js +0 -70
  75. package/dist/document/filesystem-target.js.map +0 -1
  76. package/dist/document/index.d.ts.map +0 -1
  77. package/dist/document/index.js.map +0 -1
  78. package/dist/document/recovery-journal.d.ts +0 -125
  79. package/dist/document/recovery-journal.d.ts.map +0 -1
  80. package/dist/document/recovery-journal.js +0 -385
  81. package/dist/document/recovery-journal.js.map +0 -1
  82. package/dist/document/types.d.ts +0 -89
  83. package/dist/document/types.d.ts.map +0 -1
  84. package/dist/document/types.js +0 -9
  85. package/dist/document/types.js.map +0 -1
  86. package/dist/filesystem/commit-lock.d.ts +0 -3
  87. package/dist/filesystem/commit-lock.d.ts.map +0 -1
  88. package/dist/filesystem/commit-lock.js +0 -20
  89. package/dist/filesystem/commit-lock.js.map +0 -1
  90. package/dist/filesystem/dbk-workspace.d.ts +0 -34
  91. package/dist/filesystem/dbk-workspace.d.ts.map +0 -1
  92. package/dist/filesystem/dbk-workspace.js +0 -85
  93. package/dist/filesystem/dbk-workspace.js.map +0 -1
  94. package/dist/filesystem/electron-provider-v2.d.ts +0 -36
  95. package/dist/filesystem/electron-provider-v2.d.ts.map +0 -1
  96. package/dist/filesystem/electron-provider-v2.js +0 -503
  97. package/dist/filesystem/electron-provider-v2.js.map +0 -1
  98. package/dist/filesystem/electron-provider.d.ts +0 -35
  99. package/dist/filesystem/electron-provider.d.ts.map +0 -1
  100. package/dist/filesystem/electron-provider.js +0 -69
  101. package/dist/filesystem/electron-provider.js.map +0 -1
  102. package/dist/filesystem/electron.d.ts.map +0 -1
  103. package/dist/filesystem/electron.js.map +0 -1
  104. package/dist/filesystem/file-media-provider.d.ts +0 -23
  105. package/dist/filesystem/file-media-provider.d.ts.map +0 -1
  106. package/dist/filesystem/file-media-provider.js +0 -85
  107. package/dist/filesystem/file-media-provider.js.map +0 -1
  108. package/dist/filesystem/filesystem-content-container.d.ts +0 -24
  109. package/dist/filesystem/filesystem-content-container.d.ts.map +0 -1
  110. package/dist/filesystem/filesystem-content-container.js +0 -153
  111. package/dist/filesystem/filesystem-content-container.js.map +0 -1
  112. package/dist/filesystem/fs-error.d.ts +0 -46
  113. package/dist/filesystem/fs-error.d.ts.map +0 -1
  114. package/dist/filesystem/fs-error.js +0 -170
  115. package/dist/filesystem/fs-error.js.map +0 -1
  116. package/dist/filesystem/index.d.ts.map +0 -1
  117. package/dist/filesystem/index.js.map +0 -1
  118. package/dist/filesystem/indexeddb-content-container.d.ts +0 -22
  119. package/dist/filesystem/indexeddb-content-container.d.ts.map +0 -1
  120. package/dist/filesystem/indexeddb-content-container.js +0 -100
  121. package/dist/filesystem/indexeddb-content-container.js.map +0 -1
  122. package/dist/filesystem/indexeddb-provider-v2.d.ts +0 -96
  123. package/dist/filesystem/indexeddb-provider-v2.d.ts.map +0 -1
  124. package/dist/filesystem/indexeddb-provider-v2.js +0 -1241
  125. package/dist/filesystem/indexeddb-provider-v2.js.map +0 -1
  126. package/dist/filesystem/indexeddb-provider.d.ts +0 -39
  127. package/dist/filesystem/indexeddb-provider.d.ts.map +0 -1
  128. package/dist/filesystem/indexeddb-provider.js +0 -145
  129. package/dist/filesystem/indexeddb-provider.js.map +0 -1
  130. package/dist/filesystem/indexeddb-store.d.ts +0 -34
  131. package/dist/filesystem/indexeddb-store.d.ts.map +0 -1
  132. package/dist/filesystem/indexeddb-store.js +0 -165
  133. package/dist/filesystem/indexeddb-store.js.map +0 -1
  134. package/dist/filesystem/indexeddb.d.ts.map +0 -1
  135. package/dist/filesystem/indexeddb.js.map +0 -1
  136. package/dist/filesystem/legacy-v2-adapter.d.ts +0 -62
  137. package/dist/filesystem/legacy-v2-adapter.d.ts.map +0 -1
  138. package/dist/filesystem/legacy-v2-adapter.js +0 -499
  139. package/dist/filesystem/legacy-v2-adapter.js.map +0 -1
  140. package/dist/filesystem/memory-provider-v2.d.ts +0 -92
  141. package/dist/filesystem/memory-provider-v2.d.ts.map +0 -1
  142. package/dist/filesystem/memory-provider-v2.js +0 -615
  143. package/dist/filesystem/memory-provider-v2.js.map +0 -1
  144. package/dist/filesystem/memory-provider.d.ts +0 -54
  145. package/dist/filesystem/memory-provider.d.ts.map +0 -1
  146. package/dist/filesystem/memory-provider.js +0 -132
  147. package/dist/filesystem/memory-provider.js.map +0 -1
  148. package/dist/filesystem/memory.d.ts.map +0 -1
  149. package/dist/filesystem/memory.js.map +0 -1
  150. package/dist/filesystem/move-entry.d.ts +0 -9
  151. package/dist/filesystem/move-entry.d.ts.map +0 -1
  152. package/dist/filesystem/move-entry.js +0 -103
  153. package/dist/filesystem/move-entry.js.map +0 -1
  154. package/dist/filesystem/move-error.d.ts +0 -53
  155. package/dist/filesystem/move-error.d.ts.map +0 -1
  156. package/dist/filesystem/move-error.js +0 -99
  157. package/dist/filesystem/move-error.js.map +0 -1
  158. package/dist/filesystem/native-provider-v2.d.ts +0 -75
  159. package/dist/filesystem/native-provider-v2.d.ts.map +0 -1
  160. package/dist/filesystem/native-provider-v2.js +0 -849
  161. package/dist/filesystem/native-provider-v2.js.map +0 -1
  162. package/dist/filesystem/native-provider.d.ts +0 -53
  163. package/dist/filesystem/native-provider.d.ts.map +0 -1
  164. package/dist/filesystem/native-provider.js +0 -471
  165. package/dist/filesystem/native-provider.js.map +0 -1
  166. package/dist/filesystem/native.d.ts.map +0 -1
  167. package/dist/filesystem/native.js.map +0 -1
  168. package/dist/filesystem/types.d.ts +0 -83
  169. package/dist/filesystem/types.d.ts.map +0 -1
  170. package/dist/filesystem/types.js +0 -16
  171. package/dist/filesystem/types.js.map +0 -1
  172. package/dist/filesystem/v2.d.ts +0 -172
  173. package/dist/filesystem/v2.d.ts.map +0 -1
  174. package/dist/filesystem/v2.js +0 -16
  175. package/dist/filesystem/v2.js.map +0 -1
  176. package/dist/filesystem/workspace-path.d.ts +0 -21
  177. package/dist/filesystem/workspace-path.d.ts.map +0 -1
  178. package/dist/filesystem/workspace-path.js +0 -80
  179. package/dist/filesystem/workspace-path.js.map +0 -1
  180. package/dist/host/filesystem-v2.d.ts +0 -45
  181. package/dist/host/filesystem-v2.d.ts.map +0 -1
  182. package/dist/host/filesystem-v2.js +0 -13
  183. package/dist/host/filesystem-v2.js.map +0 -1
  184. package/dist/host/git.d.ts +0 -200
  185. package/dist/host/git.d.ts.map +0 -1
  186. package/dist/host/git.js +0 -15
  187. package/dist/host/git.js.map +0 -1
  188. package/dist/host/index.d.ts.map +0 -1
  189. package/dist/host/index.js.map +0 -1
  190. package/dist/host/types.d.ts +0 -232
  191. package/dist/host/types.d.ts.map +0 -1
  192. package/dist/host/types.js +0 -10
  193. package/dist/host/types.js.map +0 -1
  194. package/dist/host/wire-policy.d.ts +0 -22
  195. package/dist/host/wire-policy.d.ts.map +0 -1
  196. package/dist/host/wire-policy.js +0 -92
  197. package/dist/host/wire-policy.js.map +0 -1
  198. package/dist/index.d.ts.map +0 -1
  199. package/dist/index.js.map +0 -1
  200. package/dist/mcp/index.d.ts.map +0 -1
  201. package/dist/mcp/index.js.map +0 -1
  202. package/dist/mcp/types.d.ts +0 -466
  203. package/dist/mcp/types.d.ts.map +0 -1
  204. package/dist/mcp/types.js +0 -25
  205. package/dist/mcp/types.js.map +0 -1
  206. package/dist/mcp/wire.d.ts +0 -29
  207. package/dist/mcp/wire.d.ts.map +0 -1
  208. package/dist/mcp/wire.js +0 -1046
  209. package/dist/mcp/wire.js.map +0 -1
  210. package/dist/mcp/zod.d.ts.map +0 -1
  211. package/dist/mcp/zod.js.map +0 -1
  212. package/dist/vscode/index.d.ts.map +0 -1
  213. package/dist/vscode/index.js.map +0 -1
  214. package/dist/vscode/messages.d.ts +0 -204
  215. package/dist/vscode/messages.d.ts.map +0 -1
  216. package/dist/vscode/messages.js +0 -494
  217. package/dist/vscode/messages.js.map +0 -1
  218. package/dist/workspace/index.d.ts.map +0 -1
  219. package/dist/workspace/index.js.map +0 -1
  220. package/dist/workspace/reconcile-electron.d.ts +0 -19
  221. package/dist/workspace/reconcile-electron.d.ts.map +0 -1
  222. package/dist/workspace/reconcile-electron.js +0 -42
  223. package/dist/workspace/reconcile-electron.js.map +0 -1
  224. package/dist/workspace/types.d.ts +0 -69
  225. package/dist/workspace/types.d.ts.map +0 -1
  226. package/dist/workspace/types.js +0 -2
  227. package/dist/workspace/types.js.map +0 -1
  228. package/dist/workspace/workspace-manager.d.ts +0 -47
  229. package/dist/workspace/workspace-manager.d.ts.map +0 -1
  230. package/dist/workspace/workspace-manager.js +0 -126
  231. package/dist/workspace/workspace-manager.js.map +0 -1
  232. package/src/document/document-session.ts +0 -834
  233. package/src/document/filesystem-target.ts +0 -109
  234. package/src/document/index.ts +0 -41
  235. package/src/document/recovery-journal.ts +0 -536
  236. package/src/document/types.ts +0 -116
  237. package/src/filesystem/commit-lock.ts +0 -29
  238. package/src/filesystem/dbk-workspace.ts +0 -135
  239. package/src/filesystem/electron-provider-v2.ts +0 -630
  240. package/src/filesystem/electron-provider.ts +0 -99
  241. package/src/filesystem/electron.ts +0 -3
  242. package/src/filesystem/file-media-provider.ts +0 -103
  243. package/src/filesystem/filesystem-content-container.ts +0 -164
  244. package/src/filesystem/fs-error.ts +0 -234
  245. package/src/filesystem/index.ts +0 -143
  246. package/src/filesystem/indexeddb-content-container.ts +0 -112
  247. package/src/filesystem/indexeddb-provider-v2.ts +0 -1740
  248. package/src/filesystem/indexeddb-provider.ts +0 -171
  249. package/src/filesystem/indexeddb-store.ts +0 -207
  250. package/src/filesystem/indexeddb.ts +0 -12
  251. package/src/filesystem/legacy-v2-adapter.ts +0 -779
  252. package/src/filesystem/memory-provider-v2.ts +0 -896
  253. package/src/filesystem/memory-provider.ts +0 -180
  254. package/src/filesystem/memory.ts +0 -15
  255. package/src/filesystem/move-entry.ts +0 -142
  256. package/src/filesystem/move-error.ts +0 -159
  257. package/src/filesystem/native-provider-v2.ts +0 -1165
  258. package/src/filesystem/native-provider.ts +0 -537
  259. package/src/filesystem/native.ts +0 -16
  260. package/src/filesystem/types.ts +0 -114
  261. package/src/filesystem/v2.ts +0 -231
  262. package/src/filesystem/workspace-path.ts +0 -92
  263. package/src/host/filesystem-v2.ts +0 -104
  264. package/src/host/git.ts +0 -246
  265. package/src/host/index.ts +0 -93
  266. package/src/host/types.ts +0 -274
  267. package/src/host/wire-policy.ts +0 -103
  268. package/src/index.ts +0 -10
  269. package/src/mcp/index.ts +0 -3
  270. package/src/mcp/types.ts +0 -594
  271. package/src/mcp/wire.ts +0 -1280
  272. package/src/mcp/zod.ts +0 -1000
  273. package/src/vscode/index.ts +0 -2
  274. package/src/vscode/messages.ts +0 -704
  275. package/src/workspace/index.ts +0 -17
  276. package/src/workspace/reconcile-electron.ts +0 -65
  277. package/src/workspace/types.ts +0 -67
  278. package/src/workspace/workspace-manager.ts +0 -151
package/src/mcp/wire.ts DELETED
@@ -1,1280 +0,0 @@
1
- import { tryParseWorkspacePath, type WorkspacePath } from '../filesystem/workspace-path.js';
2
- import {
3
- DOCBLOCKS_MCP_WIRE_VERSION,
4
- type AppliedOption,
5
- type ArtifactRef,
6
- type AssetSummary,
7
- type BlockSummary,
8
- type BundleAssetRef,
9
- type BundleAssetContentSource,
10
- type ComparisonChange,
11
- type ComparisonMetric,
12
- type ComparisonResult,
13
- type ConversionFidelity,
14
- type ConversionResult,
15
- type DiagnosticLocation,
16
- type DiagnosticSeverity,
17
- type DiagnosticStage,
18
- type DocumentMetadataSummary,
19
- type DocumentItemSummary,
20
- type DocumentSource,
21
- type DocumentStatistics,
22
- type EngineVersion,
23
- type InspectionResult,
24
- type LinkSummary,
25
- type MaterializationOptions,
26
- type McpDiagnostic,
27
- type McpErrorResult,
28
- type OutlineEntry,
29
- type PreviewItem,
30
- type PreviewResult,
31
- type SourceRange,
32
- type ThemeSummary,
33
- type TableSummary,
34
- type ValidationResult,
35
- type ValidationSummary,
36
- } from './types.js';
37
-
38
- /** Quantitative limits for untrusted MCP payloads. */
39
- export const MCP_WIRE_LIMITS = Object.freeze({
40
- identifierCharacters: 256,
41
- formatCharacters: 64,
42
- mimeTypeCharacters: 256,
43
- labelCharacters: 1_024,
44
- pathCharacters: 4_096,
45
- uriCharacters: 8_192,
46
- messageCharacters: 4_000,
47
- excerptCharacters: 4_096,
48
- documentCharacters: 20 * 1024 * 1024,
49
- artifactBytes: 500 * 1024 * 1024,
50
- bundleAssets: 256,
51
- arrayEntries: 10_000,
52
- previewItems: 1_000,
53
- imageDimension: 32_768,
54
- });
55
-
56
- const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u;
57
- const ENGINE_NAME_PATTERN =
58
- /^(?:[A-Za-z0-9][A-Za-z0-9._:-]*|@[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*)$/u;
59
- const FORMAT_PATTERN = /^[a-z0-9][a-z0-9.+_-]*$/u;
60
- const MIME_TYPE_PATTERN = /^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/iu;
61
- const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
62
- const ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;
63
-
64
- export function parseArtifactRef(value: unknown): ArtifactRef | null {
65
- if (
66
- !isRecord(value) ||
67
- !hasExactKeys(value, [
68
- 'id',
69
- 'uri',
70
- 'format',
71
- 'mimeType',
72
- 'size',
73
- 'sha256',
74
- 'sourceFormat',
75
- 'sourceSha256',
76
- 'suggestedFilename',
77
- 'appliedOptions',
78
- 'engineVersions',
79
- 'createdAt',
80
- 'expiresAt',
81
- ])
82
- ) {
83
- return null;
84
- }
85
-
86
- const {
87
- id,
88
- uri,
89
- format,
90
- mimeType,
91
- size,
92
- sha256,
93
- sourceFormat,
94
- sourceSha256,
95
- suggestedFilename,
96
- createdAt,
97
- expiresAt,
98
- } = value;
99
- const appliedOptions = parseArray(value.appliedOptions, parseAppliedOption);
100
- const engineVersions = parseArray(value.engineVersions, parseEngineVersion);
101
- if (
102
- !isIdentifier(id) ||
103
- !isArtifactUri(uri, id) ||
104
- !isFormat(format) ||
105
- !isMimeType(mimeType) ||
106
- !isBoundedInteger(size, 0, MCP_WIRE_LIMITS.artifactBytes) ||
107
- !isSha256(sha256) ||
108
- !(sourceFormat === null || isFormat(sourceFormat)) ||
109
- !(sourceSha256 === null || isSha256(sourceSha256)) ||
110
- !isSafeFilename(suggestedFilename) ||
111
- appliedOptions === null ||
112
- engineVersions === null ||
113
- hasDuplicateNames(appliedOptions) ||
114
- hasDuplicateNames(engineVersions) ||
115
- !isIsoTimestamp(createdAt) ||
116
- !(expiresAt === null || isIsoTimestamp(expiresAt)) ||
117
- (expiresAt !== null && Date.parse(expiresAt) <= Date.parse(createdAt))
118
- ) {
119
- return null;
120
- }
121
-
122
- return {
123
- id,
124
- uri,
125
- format,
126
- mimeType,
127
- size,
128
- sha256,
129
- sourceFormat,
130
- sourceSha256,
131
- suggestedFilename,
132
- appliedOptions,
133
- engineVersions,
134
- createdAt,
135
- expiresAt,
136
- };
137
- }
138
-
139
- export function parseDocumentSource(value: unknown): DocumentSource | null {
140
- if (!isRecord(value) || typeof value.kind !== 'string') return null;
141
-
142
- switch (value.kind) {
143
- case 'markdown': {
144
- if (!hasExactKeys(value, ['kind', 'markdown', 'name'])) return null;
145
- const { markdown, name } = value;
146
- if (
147
- !isBoundedString(markdown, MCP_WIRE_LIMITS.documentCharacters) ||
148
- !isNullableBoundedString(name, MCP_WIRE_LIMITS.labelCharacters)
149
- ) {
150
- return null;
151
- }
152
- return { kind: 'markdown', markdown, name };
153
- }
154
- case 'file': {
155
- if (!hasExactKeys(value, ['kind', 'rootId', 'path', 'format'])) return null;
156
- const { rootId, path, format } = value;
157
- const parsedPath = parseNonRootWorkspacePath(path);
158
- if (!isIdentifier(rootId) || parsedPath === null || !(format === null || isFormat(format))) {
159
- return null;
160
- }
161
- return { kind: 'file', rootId, path: parsedPath, format };
162
- }
163
- case 'artifact': {
164
- if (!hasExactKeys(value, ['kind', 'uri']) || !isArtifactUri(value.uri)) return null;
165
- return { kind: 'artifact', uri: value.uri };
166
- }
167
- case 'bundle': {
168
- if (!hasExactKeys(value, ['kind', 'markdown', 'assets', 'name'])) return null;
169
- const { markdown, name } = value;
170
- if (
171
- !isBoundedString(markdown, MCP_WIRE_LIMITS.documentCharacters) ||
172
- !isNullableBoundedString(name, MCP_WIRE_LIMITS.labelCharacters)
173
- ) {
174
- return null;
175
- }
176
- const assets = parseArray(value.assets, parseBundleAssetRef, MCP_WIRE_LIMITS.bundleAssets);
177
- if (assets === null || hasDuplicatePaths(assets)) return null;
178
- return { kind: 'bundle', markdown, assets, name };
179
- }
180
- default:
181
- return null;
182
- }
183
- }
184
-
185
- export function parseMcpDiagnostic(value: unknown): McpDiagnostic | null {
186
- if (
187
- !isRecord(value) ||
188
- !hasExactKeys(value, [
189
- 'code',
190
- 'severity',
191
- 'stage',
192
- 'format',
193
- 'count',
194
- 'message',
195
- 'remediation',
196
- 'retryable',
197
- 'location',
198
- ])
199
- ) {
200
- return null;
201
- }
202
-
203
- const { code, severity, stage, format, count, message, remediation, retryable } = value;
204
- const location = parseNullable(value.location, parseDiagnosticLocation);
205
- if (
206
- !isIdentifier(code) ||
207
- !isDiagnosticSeverity(severity) ||
208
- !isDiagnosticStage(stage) ||
209
- !(format === null || isFormat(format)) ||
210
- !isBoundedInteger(count, 1, Number.MAX_SAFE_INTEGER) ||
211
- !isBoundedString(message, MCP_WIRE_LIMITS.messageCharacters, 1) ||
212
- !isNullableBoundedString(remediation, MCP_WIRE_LIMITS.messageCharacters) ||
213
- typeof retryable !== 'boolean' ||
214
- location === undefined
215
- ) {
216
- return null;
217
- }
218
-
219
- return {
220
- code,
221
- severity,
222
- stage,
223
- format,
224
- count,
225
- message,
226
- remediation,
227
- retryable,
228
- location,
229
- };
230
- }
231
-
232
- export function parseMcpErrorResult(value: unknown): McpErrorResult | null {
233
- if (
234
- !isRecord(value) ||
235
- !hasExactKeys(value, ['version', 'kind', 'result', 'error']) ||
236
- value.version !== DOCBLOCKS_MCP_WIRE_VERSION ||
237
- value.kind !== 'error' ||
238
- value.result !== null ||
239
- !isRecord(value.error) ||
240
- !hasExactKeys(value.error, [
241
- 'code',
242
- 'message',
243
- 'stage',
244
- 'format',
245
- 'hint',
246
- 'retryable',
247
- 'operationLoad',
248
- ])
249
- ) {
250
- return null;
251
- }
252
- const { code, message, stage, format, hint, retryable, operationLoad } = value.error;
253
- if (
254
- !isIdentifier(code) ||
255
- !isBoundedString(message, MCP_WIRE_LIMITS.messageCharacters, 1) ||
256
- !(stage === null || isDiagnosticStage(stage)) ||
257
- !(format === null || isFormat(format)) ||
258
- !isNullableBoundedString(hint, MCP_WIRE_LIMITS.messageCharacters) ||
259
- typeof retryable !== 'boolean' ||
260
- !(
261
- operationLoad === null ||
262
- (isRecord(operationLoad) &&
263
- hasExactKeys(operationLoad, ['active', 'capacity']) &&
264
- isBoundedInteger(operationLoad.active, 0, 32) &&
265
- isBoundedInteger(operationLoad.capacity, 1, 32) &&
266
- operationLoad.active <= operationLoad.capacity)
267
- )
268
- ) {
269
- return null;
270
- }
271
- return {
272
- version: DOCBLOCKS_MCP_WIRE_VERSION,
273
- kind: 'error',
274
- result: null,
275
- error: {
276
- code,
277
- message,
278
- stage,
279
- format,
280
- hint,
281
- retryable,
282
- operationLoad:
283
- operationLoad === null
284
- ? null
285
- : {
286
- active: operationLoad.active as number,
287
- capacity: operationLoad.capacity as number,
288
- },
289
- },
290
- };
291
- }
292
-
293
- export function parseConversionResult(value: unknown): ConversionResult | null {
294
- if (
295
- !isRecord(value) ||
296
- !hasExactKeys(value, [
297
- 'version',
298
- 'kind',
299
- 'sourceFormat',
300
- 'targetFormat',
301
- 'artifact',
302
- 'fidelity',
303
- 'appliedThemeId',
304
- 'appliedTransformId',
305
- 'sourceAssets',
306
- 'sourceAssetCount',
307
- 'diagnostics',
308
- ]) ||
309
- value.version !== DOCBLOCKS_MCP_WIRE_VERSION ||
310
- value.kind !== 'conversion' ||
311
- !isFormat(value.sourceFormat) ||
312
- !isFormat(value.targetFormat) ||
313
- !isConversionFidelity(value.fidelity) ||
314
- !isNullableIdentifier(value.appliedThemeId) ||
315
- !isNullableIdentifier(value.appliedTransformId) ||
316
- !isBoundedInteger(value.sourceAssetCount, 0, MCP_WIRE_LIMITS.arrayEntries)
317
- ) {
318
- return null;
319
- }
320
-
321
- const artifact = parseArtifactRef(value.artifact);
322
- const sourceAssets = parseArray(value.sourceAssets, parseAssetSummary);
323
- const diagnostics = parseArray(value.diagnostics, parseMcpDiagnostic);
324
- if (
325
- artifact === null ||
326
- sourceAssets === null ||
327
- diagnostics === null ||
328
- sourceAssets.length !== value.sourceAssetCount ||
329
- hasDuplicatePaths(sourceAssets) ||
330
- artifact.format !== value.targetFormat ||
331
- (artifact.sourceFormat !== null && artifact.sourceFormat !== value.sourceFormat)
332
- ) {
333
- return null;
334
- }
335
-
336
- return {
337
- version: DOCBLOCKS_MCP_WIRE_VERSION,
338
- kind: 'conversion',
339
- sourceFormat: value.sourceFormat,
340
- targetFormat: value.targetFormat,
341
- artifact,
342
- fidelity: value.fidelity,
343
- appliedThemeId: value.appliedThemeId,
344
- appliedTransformId: value.appliedTransformId,
345
- sourceAssets,
346
- sourceAssetCount: value.sourceAssetCount,
347
- diagnostics,
348
- };
349
- }
350
-
351
- export function parseInspectionResult(value: unknown): InspectionResult | null {
352
- if (
353
- !isRecord(value) ||
354
- !hasExactKeys(value, [
355
- 'version',
356
- 'kind',
357
- 'sourceFormat',
358
- 'metadata',
359
- 'statistics',
360
- 'outline',
361
- 'blocks',
362
- 'blockOffset',
363
- 'nextCursor',
364
- 'tables',
365
- 'links',
366
- 'items',
367
- 'assets',
368
- 'theme',
369
- 'truncated',
370
- 'detailsTruncated',
371
- 'diagnostics',
372
- ]) ||
373
- value.version !== DOCBLOCKS_MCP_WIRE_VERSION ||
374
- value.kind !== 'inspection' ||
375
- !isFormat(value.sourceFormat) ||
376
- !isBoundedInteger(value.blockOffset, 0, MCP_WIRE_LIMITS.arrayEntries) ||
377
- !isNullableBoundedString(value.nextCursor, MCP_WIRE_LIMITS.identifierCharacters) ||
378
- typeof value.truncated !== 'boolean' ||
379
- typeof value.detailsTruncated !== 'boolean'
380
- ) {
381
- return null;
382
- }
383
-
384
- const metadata = parseDocumentMetadata(value.metadata);
385
- const statistics = parseDocumentStatistics(value.statistics);
386
- const outline = parseArray(value.outline, parseOutlineEntry);
387
- const blocks = parseArray(value.blocks, parseBlockSummary);
388
- const tables = parseArray(value.tables, parseTableSummary);
389
- const links = parseArray(value.links, parseLinkSummary);
390
- const items = parseArray(value.items, parseDocumentItemSummary);
391
- const assets = parseArray(value.assets, parseAssetSummary);
392
- const theme = parseNullable(value.theme, parseThemeSummary);
393
- const diagnostics = parseArray(value.diagnostics, parseMcpDiagnostic);
394
- if (
395
- metadata === null ||
396
- statistics === null ||
397
- outline === null ||
398
- blocks === null ||
399
- tables === null ||
400
- links === null ||
401
- items === null ||
402
- assets === null ||
403
- theme === undefined ||
404
- diagnostics === null ||
405
- statistics.blockCount < value.blockOffset + blocks.length ||
406
- statistics.assetCount < assets.length ||
407
- (!value.truncated && statistics.blockCount !== value.blockOffset + blocks.length) ||
408
- value.truncated !== (value.nextCursor !== null) ||
409
- statistics.tableCount < tables.length ||
410
- statistics.linkCount < links.length ||
411
- hasDuplicateIds(outline) ||
412
- hasDuplicateIds(blocks) ||
413
- hasDuplicateIndexes(tables) ||
414
- hasDuplicateIndexes(links) ||
415
- hasDuplicateItemIndexes(items) ||
416
- hasDuplicatePaths(assets) ||
417
- outline.some((entry) => entry.blockIndex >= statistics.blockCount) ||
418
- blocks.some(
419
- (block) => block.sourceRange !== null && block.sourceRange.end > statistics.characterCount,
420
- )
421
- ) {
422
- return null;
423
- }
424
-
425
- return {
426
- version: DOCBLOCKS_MCP_WIRE_VERSION,
427
- kind: 'inspection',
428
- sourceFormat: value.sourceFormat,
429
- metadata,
430
- statistics,
431
- outline,
432
- blocks,
433
- blockOffset: value.blockOffset,
434
- nextCursor: value.nextCursor,
435
- tables,
436
- links,
437
- items,
438
- assets,
439
- theme,
440
- truncated: value.truncated,
441
- detailsTruncated: value.detailsTruncated,
442
- diagnostics,
443
- };
444
- }
445
-
446
- export function parseValidationResult(value: unknown): ValidationResult | null {
447
- if (
448
- !isRecord(value) ||
449
- !hasExactKeys(value, [
450
- 'version',
451
- 'kind',
452
- 'sourceFormat',
453
- 'targetFormat',
454
- 'valid',
455
- 'summary',
456
- 'diagnostics',
457
- ]) ||
458
- value.version !== DOCBLOCKS_MCP_WIRE_VERSION ||
459
- value.kind !== 'validation' ||
460
- !isFormat(value.sourceFormat) ||
461
- !(value.targetFormat === null || isFormat(value.targetFormat)) ||
462
- typeof value.valid !== 'boolean'
463
- ) {
464
- return null;
465
- }
466
-
467
- const summary = parseValidationSummary(value.summary);
468
- const diagnostics = parseArray(value.diagnostics, parseMcpDiagnostic);
469
- if (summary === null || diagnostics === null) return null;
470
- const actualSummary = summarizeDiagnostics(diagnostics);
471
- if (
472
- summary.errorCount !== actualSummary.errorCount ||
473
- summary.warningCount !== actualSummary.warningCount ||
474
- summary.infoCount !== actualSummary.infoCount ||
475
- value.valid !== (summary.errorCount === 0)
476
- ) {
477
- return null;
478
- }
479
-
480
- return {
481
- version: DOCBLOCKS_MCP_WIRE_VERSION,
482
- kind: 'validation',
483
- sourceFormat: value.sourceFormat,
484
- targetFormat: value.targetFormat,
485
- valid: value.valid,
486
- summary,
487
- diagnostics,
488
- };
489
- }
490
-
491
- export function parsePreviewResult(value: unknown): PreviewResult | null {
492
- if (
493
- !isRecord(value) ||
494
- !hasExactKeys(value, [
495
- 'version',
496
- 'kind',
497
- 'sourceFormat',
498
- 'previewBasis',
499
- 'totalItems',
500
- 'items',
501
- 'truncated',
502
- 'diagnostics',
503
- ]) ||
504
- value.version !== DOCBLOCKS_MCP_WIRE_VERSION ||
505
- value.kind !== 'preview' ||
506
- !isFormat(value.sourceFormat) ||
507
- !(
508
- value.previewBasis === 'source-render' ||
509
- value.previewBasis === 'reconstructed-import' ||
510
- value.previewBasis === 'native-extracted'
511
- ) ||
512
- !isBoundedInteger(value.totalItems, 0, Number.MAX_SAFE_INTEGER) ||
513
- typeof value.truncated !== 'boolean'
514
- ) {
515
- return null;
516
- }
517
-
518
- const items = parseArray(value.items, parsePreviewItem, MCP_WIRE_LIMITS.previewItems);
519
- const diagnostics = parseArray(value.diagnostics, parseMcpDiagnostic);
520
- if (
521
- items === null ||
522
- diagnostics === null ||
523
- value.totalItems < items.length ||
524
- (!value.truncated && value.totalItems !== items.length) ||
525
- hasDuplicatePreviewIndexes(items) ||
526
- items.some(
527
- (item) =>
528
- item.artifact.sourceFormat !== null && item.artifact.sourceFormat !== value.sourceFormat,
529
- )
530
- ) {
531
- return null;
532
- }
533
-
534
- return {
535
- version: DOCBLOCKS_MCP_WIRE_VERSION,
536
- kind: 'preview',
537
- sourceFormat: value.sourceFormat,
538
- previewBasis: value.previewBasis,
539
- totalItems: value.totalItems,
540
- items,
541
- truncated: value.truncated,
542
- diagnostics,
543
- };
544
- }
545
-
546
- export function parseComparisonResult(value: unknown): ComparisonResult | null {
547
- if (
548
- !isRecord(value) ||
549
- !hasExactKeys(value, [
550
- 'version',
551
- 'kind',
552
- 'leftFormat',
553
- 'rightFormat',
554
- 'equivalent',
555
- 'score',
556
- 'changes',
557
- 'metrics',
558
- 'diagnostics',
559
- ]) ||
560
- value.version !== DOCBLOCKS_MCP_WIRE_VERSION ||
561
- value.kind !== 'comparison' ||
562
- !isFormat(value.leftFormat) ||
563
- !isFormat(value.rightFormat) ||
564
- typeof value.equivalent !== 'boolean' ||
565
- !isBoundedNumber(value.score, 0, 1)
566
- ) {
567
- return null;
568
- }
569
-
570
- const changes = parseArray(value.changes, parseComparisonChange);
571
- const metrics = parseArray(value.metrics, parseComparisonMetric);
572
- const diagnostics = parseArray(value.diagnostics, parseMcpDiagnostic);
573
- if (
574
- changes === null ||
575
- metrics === null ||
576
- diagnostics === null ||
577
- hasDuplicateNames(metrics) ||
578
- (value.equivalent &&
579
- (value.score !== 1 || changes.some((change) => change.status !== 'retained')))
580
- ) {
581
- return null;
582
- }
583
-
584
- return {
585
- version: DOCBLOCKS_MCP_WIRE_VERSION,
586
- kind: 'comparison',
587
- leftFormat: value.leftFormat,
588
- rightFormat: value.rightFormat,
589
- equivalent: value.equivalent,
590
- score: value.score,
591
- changes,
592
- metrics,
593
- diagnostics,
594
- };
595
- }
596
-
597
- export function parseMaterializationOptions(value: unknown): MaterializationOptions | null {
598
- if (
599
- !isRecord(value) ||
600
- !hasExactKeys(value, ['rootId', 'path', 'ifExists', 'expectedSha256']) ||
601
- !isIdentifier(value.rootId)
602
- ) {
603
- return null;
604
- }
605
- const path = parseNonRootWorkspacePath(value.path);
606
- if (path === null) return null;
607
-
608
- if (value.ifExists === 'error' && value.expectedSha256 === null) {
609
- return { rootId: value.rootId, path, ifExists: 'error', expectedSha256: null };
610
- }
611
- if (value.ifExists === 'replace' && isSha256(value.expectedSha256)) {
612
- return {
613
- rootId: value.rootId,
614
- path,
615
- ifExists: 'replace',
616
- expectedSha256: value.expectedSha256,
617
- };
618
- }
619
- return null;
620
- }
621
-
622
- function parseBundleAssetRef(value: unknown): BundleAssetRef | null {
623
- if (
624
- !isRecord(value) ||
625
- !hasExactKeys(value, ['path', 'source', 'mimeType', 'altText', 'credit', 'license'])
626
- ) {
627
- return null;
628
- }
629
- const path = parseNonRootWorkspacePath(value.path);
630
- const source = parseBundleAssetContentSource(value.source);
631
- if (
632
- path === null ||
633
- source === null ||
634
- !(value.mimeType === null || isMimeType(value.mimeType)) ||
635
- !isNullableBoundedString(value.altText, MCP_WIRE_LIMITS.labelCharacters) ||
636
- !isNullableBoundedString(value.credit, MCP_WIRE_LIMITS.labelCharacters) ||
637
- !isNullableBoundedString(value.license, MCP_WIRE_LIMITS.labelCharacters)
638
- ) {
639
- return null;
640
- }
641
- return {
642
- path,
643
- source,
644
- mimeType: value.mimeType,
645
- altText: value.altText,
646
- credit: value.credit,
647
- license: value.license,
648
- };
649
- }
650
-
651
- function parseBundleAssetContentSource(value: unknown): BundleAssetContentSource | null {
652
- if (!isRecord(value) || typeof value.kind !== 'string') return null;
653
- if (value.kind === 'file') {
654
- if (!hasExactKeys(value, ['kind', 'rootId', 'path']) || !isIdentifier(value.rootId)) {
655
- return null;
656
- }
657
- const path = parseNonRootWorkspacePath(value.path);
658
- return path === null ? null : { kind: 'file', rootId: value.rootId, path };
659
- }
660
- if (value.kind === 'artifact') {
661
- return hasExactKeys(value, ['kind', 'uri']) && isArtifactUri(value.uri)
662
- ? { kind: 'artifact', uri: value.uri }
663
- : null;
664
- }
665
- return null;
666
- }
667
-
668
- function parseDiagnosticLocation(value: unknown): DiagnosticLocation | null {
669
- if (!isRecord(value) || typeof value.kind !== 'string') return null;
670
- switch (value.kind) {
671
- case 'source':
672
- return hasExactKeys(value, ['kind', 'line', 'column']) &&
673
- isBoundedInteger(value.line, 1, Number.MAX_SAFE_INTEGER) &&
674
- isBoundedInteger(value.column, 1, Number.MAX_SAFE_INTEGER)
675
- ? { kind: 'source', line: value.line, column: value.column }
676
- : null;
677
- case 'block':
678
- return hasExactKeys(value, ['kind', 'blockId', 'nodeType']) &&
679
- isIdentifier(value.blockId) &&
680
- isIdentifier(value.nodeType)
681
- ? { kind: 'block', blockId: value.blockId, nodeType: value.nodeType }
682
- : null;
683
- case 'asset': {
684
- if (!hasExactKeys(value, ['kind', 'path'])) return null;
685
- const path = parseNonRootWorkspacePath(value.path);
686
- return path === null ? null : { kind: 'asset', path };
687
- }
688
- case 'item':
689
- return hasExactKeys(value, ['kind', 'itemKind', 'index']) &&
690
- isPreviewItemKind(value.itemKind) &&
691
- isBoundedInteger(value.index, 0, Number.MAX_SAFE_INTEGER)
692
- ? { kind: 'item', itemKind: value.itemKind, index: value.index }
693
- : null;
694
- default:
695
- return null;
696
- }
697
- }
698
-
699
- function parseAppliedOption(value: unknown): AppliedOption | null {
700
- if (
701
- !isRecord(value) ||
702
- !hasExactKeys(value, ['name', 'value']) ||
703
- !isIdentifier(value.name) ||
704
- !isAppliedOptionValue(value.value)
705
- ) {
706
- return null;
707
- }
708
- return { name: value.name, value: value.value };
709
- }
710
-
711
- function parseEngineVersion(value: unknown): EngineVersion | null {
712
- if (
713
- !isRecord(value) ||
714
- !hasExactKeys(value, ['name', 'version']) ||
715
- !isEngineName(value.name) ||
716
- !isBoundedString(value.version, MCP_WIRE_LIMITS.identifierCharacters, 1)
717
- ) {
718
- return null;
719
- }
720
- return { name: value.name, version: value.version };
721
- }
722
-
723
- function parseDocumentMetadata(value: unknown): DocumentMetadataSummary | null {
724
- if (
725
- !isRecord(value) ||
726
- !hasExactKeys(value, ['title', 'author', 'description']) ||
727
- !isNullableBoundedString(value.title, MCP_WIRE_LIMITS.labelCharacters) ||
728
- !isNullableBoundedString(value.author, MCP_WIRE_LIMITS.labelCharacters) ||
729
- !isNullableBoundedString(value.description, MCP_WIRE_LIMITS.messageCharacters)
730
- ) {
731
- return null;
732
- }
733
- return { title: value.title, author: value.author, description: value.description };
734
- }
735
-
736
- function parseDocumentStatistics(value: unknown): DocumentStatistics | null {
737
- if (
738
- !isRecord(value) ||
739
- !hasExactKeys(value, [
740
- 'blockCount',
741
- 'wordCount',
742
- 'characterCount',
743
- 'tableCount',
744
- 'linkCount',
745
- 'assetCount',
746
- 'pageCount',
747
- 'slideCount',
748
- 'sheetCount',
749
- ]) ||
750
- !isNonNegativeInteger(value.blockCount) ||
751
- !isNonNegativeInteger(value.wordCount) ||
752
- !isNonNegativeInteger(value.characterCount) ||
753
- !isNonNegativeInteger(value.tableCount) ||
754
- !isNonNegativeInteger(value.linkCount) ||
755
- !isNonNegativeInteger(value.assetCount) ||
756
- !isNullableNonNegativeInteger(value.pageCount) ||
757
- !isNullableNonNegativeInteger(value.slideCount) ||
758
- !isNullableNonNegativeInteger(value.sheetCount)
759
- ) {
760
- return null;
761
- }
762
- return {
763
- blockCount: value.blockCount,
764
- wordCount: value.wordCount,
765
- characterCount: value.characterCount,
766
- tableCount: value.tableCount,
767
- linkCount: value.linkCount,
768
- assetCount: value.assetCount,
769
- pageCount: value.pageCount,
770
- slideCount: value.slideCount,
771
- sheetCount: value.sheetCount,
772
- };
773
- }
774
-
775
- function parseOutlineEntry(value: unknown): OutlineEntry | null {
776
- if (
777
- !isRecord(value) ||
778
- !hasExactKeys(value, ['id', 'title', 'level', 'blockIndex']) ||
779
- !isIdentifier(value.id) ||
780
- !isBoundedString(value.title, MCP_WIRE_LIMITS.labelCharacters, 1) ||
781
- !isBoundedInteger(value.level, 1, 6) ||
782
- !isNonNegativeInteger(value.blockIndex)
783
- ) {
784
- return null;
785
- }
786
- return { id: value.id, title: value.title, level: value.level, blockIndex: value.blockIndex };
787
- }
788
-
789
- function parseBlockSummary(value: unknown): BlockSummary | null {
790
- if (
791
- !isRecord(value) ||
792
- !hasExactKeys(value, ['id', 'type', 'text', 'wordCount', 'templateId', 'sourceRange']) ||
793
- !isIdentifier(value.id) ||
794
- !isIdentifier(value.type) ||
795
- !isBoundedString(value.text, MCP_WIRE_LIMITS.excerptCharacters) ||
796
- !isNonNegativeInteger(value.wordCount) ||
797
- !isNullableIdentifier(value.templateId)
798
- ) {
799
- return null;
800
- }
801
- const sourceRange = parseNullable(value.sourceRange, parseSourceRange);
802
- if (sourceRange === undefined) return null;
803
- return {
804
- id: value.id,
805
- type: value.type,
806
- text: value.text,
807
- wordCount: value.wordCount,
808
- templateId: value.templateId,
809
- sourceRange,
810
- };
811
- }
812
-
813
- function parseSourceRange(value: unknown): SourceRange | null {
814
- if (
815
- !isRecord(value) ||
816
- !hasExactKeys(value, ['start', 'end']) ||
817
- !isNonNegativeInteger(value.start) ||
818
- !isNonNegativeInteger(value.end) ||
819
- value.end < value.start
820
- ) {
821
- return null;
822
- }
823
- return { start: value.start, end: value.end };
824
- }
825
-
826
- function parseTableSummary(value: unknown): TableSummary | null {
827
- if (
828
- !isRecord(value) ||
829
- !hasExactKeys(value, ['index', 'rowCount', 'columnCount', 'headers']) ||
830
- !isNonNegativeInteger(value.index) ||
831
- !isNonNegativeInteger(value.rowCount) ||
832
- !isNonNegativeInteger(value.columnCount)
833
- ) {
834
- return null;
835
- }
836
- const headers = parseArray(value.headers, (entry) =>
837
- isBoundedString(entry, MCP_WIRE_LIMITS.labelCharacters) ? entry : null,
838
- );
839
- if (headers === null || headers.length > value.columnCount) return null;
840
- return {
841
- index: value.index,
842
- rowCount: value.rowCount,
843
- columnCount: value.columnCount,
844
- headers,
845
- };
846
- }
847
-
848
- function parseLinkSummary(value: unknown): LinkSummary | null {
849
- if (
850
- !isRecord(value) ||
851
- !hasExactKeys(value, ['index', 'text', 'target']) ||
852
- !isNonNegativeInteger(value.index) ||
853
- !isBoundedString(value.text, MCP_WIRE_LIMITS.labelCharacters) ||
854
- !isBoundedString(value.target, MCP_WIRE_LIMITS.uriCharacters, 1)
855
- ) {
856
- return null;
857
- }
858
- return { index: value.index, text: value.text, target: value.target };
859
- }
860
-
861
- function parseDocumentItemSummary(value: unknown): DocumentItemSummary | null {
862
- if (
863
- !isRecord(value) ||
864
- !hasExactKeys(value, ['kind', 'index', 'label', 'blockIds']) ||
865
- !isPreviewItemKind(value.kind) ||
866
- !isNonNegativeInteger(value.index) ||
867
- !isNullableBoundedString(value.label, MCP_WIRE_LIMITS.labelCharacters)
868
- ) {
869
- return null;
870
- }
871
- const blockIds = parseArray(value.blockIds, (entry) => (isIdentifier(entry) ? entry : null));
872
- if (blockIds === null || new Set(blockIds).size !== blockIds.length) return null;
873
- return { kind: value.kind, index: value.index, label: value.label, blockIds };
874
- }
875
-
876
- function parseAssetSummary(value: unknown): AssetSummary | null {
877
- if (
878
- !isRecord(value) ||
879
- !hasExactKeys(value, ['path', 'mimeType', 'size', 'sha256', 'altText', 'credit', 'license'])
880
- ) {
881
- return null;
882
- }
883
- const path = parseNonRootWorkspacePath(value.path);
884
- if (
885
- path === null ||
886
- !isMimeType(value.mimeType) ||
887
- !isBoundedInteger(value.size, 0, MCP_WIRE_LIMITS.artifactBytes) ||
888
- !(value.sha256 === null || isSha256(value.sha256)) ||
889
- !isNullableBoundedString(value.altText, MCP_WIRE_LIMITS.labelCharacters) ||
890
- !isNullableBoundedString(value.credit, MCP_WIRE_LIMITS.labelCharacters) ||
891
- !isNullableBoundedString(value.license, MCP_WIRE_LIMITS.labelCharacters)
892
- ) {
893
- return null;
894
- }
895
- return {
896
- path,
897
- mimeType: value.mimeType,
898
- size: value.size,
899
- sha256: value.sha256,
900
- altText: value.altText,
901
- credit: value.credit,
902
- license: value.license,
903
- };
904
- }
905
-
906
- function parseThemeSummary(value: unknown): ThemeSummary | null {
907
- if (
908
- !isRecord(value) ||
909
- !hasExactKeys(value, ['id', 'name', 'source', 'layouts']) ||
910
- !isIdentifier(value.id) ||
911
- !isBoundedString(value.name, MCP_WIRE_LIMITS.labelCharacters, 1) ||
912
- !(value.source === 'built-in' || value.source === 'document' || value.source === 'inferred')
913
- ) {
914
- return null;
915
- }
916
- const layouts = parseStringArray(value.layouts, MCP_WIRE_LIMITS.labelCharacters);
917
- if (layouts === null || new Set(layouts).size !== layouts.length) return null;
918
- return { id: value.id, name: value.name, source: value.source, layouts };
919
- }
920
-
921
- function parseValidationSummary(value: unknown): ValidationSummary | null {
922
- if (
923
- !isRecord(value) ||
924
- !hasExactKeys(value, ['errorCount', 'warningCount', 'infoCount']) ||
925
- !isNonNegativeInteger(value.errorCount) ||
926
- !isNonNegativeInteger(value.warningCount) ||
927
- !isNonNegativeInteger(value.infoCount)
928
- ) {
929
- return null;
930
- }
931
- return {
932
- errorCount: value.errorCount,
933
- warningCount: value.warningCount,
934
- infoCount: value.infoCount,
935
- };
936
- }
937
-
938
- function parsePreviewItem(value: unknown): PreviewItem | null {
939
- if (
940
- !isRecord(value) ||
941
- !hasExactKeys(value, ['kind', 'index', 'label', 'artifact', 'width', 'height']) ||
942
- !isPreviewItemKind(value.kind) ||
943
- !isNonNegativeInteger(value.index) ||
944
- !isNullableBoundedString(value.label, MCP_WIRE_LIMITS.labelCharacters) ||
945
- !isBoundedInteger(value.width, 1, MCP_WIRE_LIMITS.imageDimension) ||
946
- !isBoundedInteger(value.height, 1, MCP_WIRE_LIMITS.imageDimension)
947
- ) {
948
- return null;
949
- }
950
- const artifact = parseArtifactRef(value.artifact);
951
- if (artifact === null || !artifact.mimeType.startsWith('image/')) return null;
952
- return {
953
- kind: value.kind,
954
- index: value.index,
955
- label: value.label,
956
- artifact,
957
- width: value.width,
958
- height: value.height,
959
- };
960
- }
961
-
962
- function parseComparisonChange(value: unknown): ComparisonChange | null {
963
- if (
964
- !isRecord(value) ||
965
- !hasExactKeys(value, ['category', 'status', 'path', 'message']) ||
966
- !isComparisonCategory(value.category) ||
967
- !isComparisonStatus(value.status) ||
968
- !isNullableBoundedString(value.path, MCP_WIRE_LIMITS.pathCharacters) ||
969
- !isBoundedString(value.message, MCP_WIRE_LIMITS.messageCharacters, 1)
970
- ) {
971
- return null;
972
- }
973
- return {
974
- category: value.category,
975
- status: value.status,
976
- path: value.path,
977
- message: value.message,
978
- };
979
- }
980
-
981
- function parseComparisonMetric(value: unknown): ComparisonMetric | null {
982
- if (
983
- !isRecord(value) ||
984
- !hasExactKeys(value, ['name', 'leftValue', 'rightValue', 'similarity']) ||
985
- !isIdentifier(value.name) ||
986
- !isFiniteNumber(value.leftValue) ||
987
- !isFiniteNumber(value.rightValue) ||
988
- !isBoundedNumber(value.similarity, 0, 1)
989
- ) {
990
- return null;
991
- }
992
- return {
993
- name: value.name,
994
- leftValue: value.leftValue,
995
- rightValue: value.rightValue,
996
- similarity: value.similarity,
997
- };
998
- }
999
-
1000
- function summarizeDiagnostics(diagnostics: readonly McpDiagnostic[]): ValidationSummary {
1001
- let errorCount = 0;
1002
- let warningCount = 0;
1003
- let infoCount = 0;
1004
- for (const diagnostic of diagnostics) {
1005
- if (diagnostic.severity === 'error') errorCount += diagnostic.count;
1006
- else if (diagnostic.severity === 'warning') warningCount += diagnostic.count;
1007
- else infoCount += diagnostic.count;
1008
- }
1009
- return { errorCount, warningCount, infoCount };
1010
- }
1011
-
1012
- function parseArray<T>(
1013
- value: unknown,
1014
- parser: (entry: unknown) => T | null,
1015
- maximumEntries: number = MCP_WIRE_LIMITS.arrayEntries,
1016
- ): T[] | null {
1017
- if (!Array.isArray(value) || value.length > maximumEntries) return null;
1018
- const parsed: T[] = [];
1019
- for (const entry of value) {
1020
- const result = parser(entry);
1021
- if (result === null) return null;
1022
- parsed.push(result);
1023
- }
1024
- return parsed;
1025
- }
1026
-
1027
- function parseStringArray(value: unknown, maximumCharacters: number): string[] | null {
1028
- if (!Array.isArray(value) || value.length > MCP_WIRE_LIMITS.arrayEntries) return null;
1029
- const parsed: string[] = [];
1030
- for (const entry of value) {
1031
- if (!isBoundedString(entry, maximumCharacters, 1)) return null;
1032
- parsed.push(entry);
1033
- }
1034
- return parsed;
1035
- }
1036
-
1037
- function parseNullable<T>(
1038
- value: unknown,
1039
- parser: (entry: unknown) => T | null,
1040
- ): T | null | undefined {
1041
- if (value === null) return null;
1042
- return parser(value) ?? undefined;
1043
- }
1044
-
1045
- function parseNonRootWorkspacePath(value: unknown): WorkspacePath | null {
1046
- if (
1047
- !isBoundedString(value, MCP_WIRE_LIMITS.pathCharacters, 1) ||
1048
- value.includes('\\') ||
1049
- value.startsWith('/') ||
1050
- value.endsWith('/') ||
1051
- value.includes('//')
1052
- ) {
1053
- return null;
1054
- }
1055
- const parsed = tryParseWorkspacePath(value);
1056
- return parsed !== null && parsed !== '' && parsed === value ? parsed : null;
1057
- }
1058
-
1059
- function isArtifactUri(value: unknown, expectedId?: string): value is string {
1060
- if (!isBoundedString(value, MCP_WIRE_LIMITS.uriCharacters, 1)) return false;
1061
- try {
1062
- const url = new URL(value);
1063
- if (
1064
- url.protocol !== 'docblocks:' ||
1065
- url.hostname !== 'artifacts' ||
1066
- url.username !== '' ||
1067
- url.password !== '' ||
1068
- url.port !== '' ||
1069
- url.search !== '' ||
1070
- url.hash !== ''
1071
- ) {
1072
- return false;
1073
- }
1074
- const id = url.pathname.slice(1);
1075
- return (
1076
- !id.includes('/') &&
1077
- isIdentifier(id) &&
1078
- value === `docblocks://artifacts/${id}` &&
1079
- (expectedId === undefined || id === expectedId)
1080
- );
1081
- } catch {
1082
- return false;
1083
- }
1084
- }
1085
-
1086
- function isSafeFilename(value: unknown): value is string {
1087
- return (
1088
- isBoundedString(value, MCP_WIRE_LIMITS.labelCharacters, 1) &&
1089
- value !== '.' &&
1090
- value !== '..' &&
1091
- !value.includes('/') &&
1092
- !value.includes('\\')
1093
- );
1094
- }
1095
-
1096
- function isIsoTimestamp(value: unknown): value is string {
1097
- if (typeof value !== 'string' || !ISO_TIMESTAMP_PATTERN.test(value)) return false;
1098
- const timestamp = Date.parse(value);
1099
- return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
1100
- }
1101
-
1102
- function isIdentifier(value: unknown): value is string {
1103
- return (
1104
- isBoundedString(value, MCP_WIRE_LIMITS.identifierCharacters, 1) &&
1105
- IDENTIFIER_PATTERN.test(value)
1106
- );
1107
- }
1108
-
1109
- function isEngineName(value: unknown): value is string {
1110
- return (
1111
- isBoundedString(value, MCP_WIRE_LIMITS.identifierCharacters, 1) &&
1112
- ENGINE_NAME_PATTERN.test(value)
1113
- );
1114
- }
1115
-
1116
- function isNullableIdentifier(value: unknown): value is string | null {
1117
- return value === null || isIdentifier(value);
1118
- }
1119
-
1120
- function isFormat(value: unknown): value is string {
1121
- return isBoundedString(value, MCP_WIRE_LIMITS.formatCharacters, 1) && FORMAT_PATTERN.test(value);
1122
- }
1123
-
1124
- function isMimeType(value: unknown): value is string {
1125
- return (
1126
- isBoundedString(value, MCP_WIRE_LIMITS.mimeTypeCharacters, 3) && MIME_TYPE_PATTERN.test(value)
1127
- );
1128
- }
1129
-
1130
- function isSha256(value: unknown): value is string {
1131
- return typeof value === 'string' && SHA256_PATTERN.test(value);
1132
- }
1133
-
1134
- function isAppliedOptionValue(value: unknown): value is string | number | boolean | null {
1135
- return (
1136
- value === null ||
1137
- typeof value === 'boolean' ||
1138
- isBoundedString(value, MCP_WIRE_LIMITS.messageCharacters) ||
1139
- isFiniteNumber(value)
1140
- );
1141
- }
1142
-
1143
- function isDiagnosticSeverity(value: unknown): value is DiagnosticSeverity {
1144
- return value === 'info' || value === 'warning' || value === 'error';
1145
- }
1146
-
1147
- function isDiagnosticStage(value: unknown): value is DiagnosticStage {
1148
- return (
1149
- value === 'resolve' ||
1150
- value === 'import' ||
1151
- value === 'parse' ||
1152
- value === 'inspect' ||
1153
- value === 'validate' ||
1154
- value === 'transform' ||
1155
- value === 'convert' ||
1156
- value === 'render' ||
1157
- value === 'export' ||
1158
- value === 'materialize' ||
1159
- value === 'compare'
1160
- );
1161
- }
1162
-
1163
- function isConversionFidelity(value: unknown): value is ConversionFidelity {
1164
- return (
1165
- value === 'semantic' ||
1166
- value === 'editable-native' ||
1167
- value === 'rendered-fidelity' ||
1168
- value === 'hybrid'
1169
- );
1170
- }
1171
-
1172
- function isPreviewItemKind(value: unknown): value is PreviewItem['kind'] {
1173
- return value === 'page' || value === 'slide' || value === 'sheet' || value === 'frame';
1174
- }
1175
-
1176
- function isComparisonCategory(value: unknown): value is ComparisonChange['category'] {
1177
- return (
1178
- value === 'text' ||
1179
- value === 'structure' ||
1180
- value === 'table' ||
1181
- value === 'media' ||
1182
- value === 'theme' ||
1183
- value === 'layout' ||
1184
- value === 'metadata' ||
1185
- value === 'timing' ||
1186
- value === 'accessibility'
1187
- );
1188
- }
1189
-
1190
- function isComparisonStatus(value: unknown): value is ComparisonChange['status'] {
1191
- return (
1192
- value === 'retained' ||
1193
- value === 'changed' ||
1194
- value === 'degraded' ||
1195
- value === 'omitted' ||
1196
- value === 'added'
1197
- );
1198
- }
1199
-
1200
- function hasDuplicateNames(values: readonly { readonly name: string }[]): boolean {
1201
- return new Set(values.map((value) => value.name)).size !== values.length;
1202
- }
1203
-
1204
- function hasDuplicateIds(values: readonly { readonly id: string }[]): boolean {
1205
- return new Set(values.map((value) => value.id)).size !== values.length;
1206
- }
1207
-
1208
- function hasDuplicatePaths(values: readonly { readonly path: WorkspacePath }[]): boolean {
1209
- return new Set(values.map((value) => value.path)).size !== values.length;
1210
- }
1211
-
1212
- function hasDuplicateIndexes(values: readonly { readonly index: number }[]): boolean {
1213
- return new Set(values.map((value) => value.index)).size !== values.length;
1214
- }
1215
-
1216
- function hasDuplicateItemIndexes(values: readonly DocumentItemSummary[]): boolean {
1217
- return new Set(values.map((value) => `${value.kind}:${value.index}`)).size !== values.length;
1218
- }
1219
-
1220
- function hasDuplicatePreviewIndexes(values: readonly PreviewItem[]): boolean {
1221
- const keys = values.map((value) => `${value.kind}:${value.index}`);
1222
- return new Set(keys).size !== keys.length;
1223
- }
1224
-
1225
- function isRecord(value: unknown): value is Record<string, unknown> {
1226
- return typeof value === 'object' && value !== null && !Array.isArray(value);
1227
- }
1228
-
1229
- function hasExactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
1230
- const actual = Object.keys(value);
1231
- return (
1232
- actual.length === keys.length &&
1233
- keys.every((key) => Object.prototype.hasOwnProperty.call(value, key))
1234
- );
1235
- }
1236
-
1237
- function isBoundedString(
1238
- value: unknown,
1239
- maximumCharacters: number,
1240
- minimumCharacters = 0,
1241
- ): value is string {
1242
- return (
1243
- typeof value === 'string' &&
1244
- value.length >= minimumCharacters &&
1245
- value.length <= maximumCharacters &&
1246
- ![...value].some((character) => {
1247
- const codePoint = character.codePointAt(0) ?? 0;
1248
- return codePoint === 0 || codePoint === 0x7f;
1249
- })
1250
- );
1251
- }
1252
-
1253
- function isNullableBoundedString(
1254
- value: unknown,
1255
- maximumCharacters: number,
1256
- ): value is string | null {
1257
- return value === null || isBoundedString(value, maximumCharacters);
1258
- }
1259
-
1260
- function isFiniteNumber(value: unknown): value is number {
1261
- return typeof value === 'number' && Number.isFinite(value);
1262
- }
1263
-
1264
- function isBoundedNumber(value: unknown, minimum: number, maximum: number): value is number {
1265
- return isFiniteNumber(value) && value >= minimum && value <= maximum;
1266
- }
1267
-
1268
- function isBoundedInteger(value: unknown, minimum: number, maximum: number): value is number {
1269
- return (
1270
- Number.isSafeInteger(value) && typeof value === 'number' && value >= minimum && value <= maximum
1271
- );
1272
- }
1273
-
1274
- function isNonNegativeInteger(value: unknown): value is number {
1275
- return isBoundedInteger(value, 0, Number.MAX_SAFE_INTEGER);
1276
- }
1277
-
1278
- function isNullableNonNegativeInteger(value: unknown): value is number | null {
1279
- return value === null || isNonNegativeInteger(value);
1280
- }