@bendyline/docblocks 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/dist/{chunk-J32GU3F3.js → chunk-6IYMTYMQ.js} +249 -221
  2. package/dist/chunk-6IYMTYMQ.js.map +1 -0
  3. package/dist/{chunk-75UUF6C4.js → chunk-B56KWCSE.js} +2 -2
  4. package/dist/{chunk-RVQHY3XU.js → chunk-BNKV7MTO.js} +1 -1
  5. package/dist/{chunk-RVQHY3XU.js.map → chunk-BNKV7MTO.js.map} +1 -1
  6. package/dist/chunk-BXMF5YQJ.js +149 -0
  7. package/dist/chunk-BXMF5YQJ.js.map +1 -0
  8. package/dist/{chunk-PGR7LK6D.js → chunk-COYQXQK3.js} +14 -5
  9. package/dist/chunk-COYQXQK3.js.map +1 -0
  10. package/dist/{chunk-RQJRQF46.js → chunk-DN7A33LL.js} +42 -49
  11. package/dist/chunk-DN7A33LL.js.map +1 -0
  12. package/dist/chunk-FA7UNOXK.js +20 -0
  13. package/dist/chunk-FA7UNOXK.js.map +1 -0
  14. package/dist/chunk-GM5YXU4L.js +29 -0
  15. package/dist/chunk-GM5YXU4L.js.map +1 -0
  16. package/dist/{chunk-SI2LU2RP.js → chunk-JS6XFWQK.js} +2 -1
  17. package/dist/chunk-JS6XFWQK.js.map +1 -0
  18. package/dist/{chunk-EGNDADXB.js → chunk-MNSZ7EZN.js} +2 -2
  19. package/dist/chunk-P3DVZQ6S.js +228 -0
  20. package/dist/chunk-P3DVZQ6S.js.map +1 -0
  21. package/dist/chunk-P4WATND4.js +52 -0
  22. package/dist/chunk-P4WATND4.js.map +1 -0
  23. package/dist/chunk-RVPGQBWR.js +169 -0
  24. package/dist/chunk-RVPGQBWR.js.map +1 -0
  25. package/dist/chunk-U722UUFN.js +9 -0
  26. package/dist/chunk-U722UUFN.js.map +1 -0
  27. package/dist/{chunk-GCPBEMMH.js → chunk-VTCSF7VS.js} +11 -21
  28. package/dist/chunk-VTCSF7VS.js.map +1 -0
  29. package/dist/{chunk-2D7Y4IR3.js → chunk-WJKYY5QR.js} +123 -66
  30. package/dist/chunk-WJKYY5QR.js.map +1 -0
  31. package/dist/{chunk-5MYAICRE.js → chunk-XUML3MKV.js} +44 -56
  32. package/dist/chunk-XUML3MKV.js.map +1 -0
  33. package/dist/document/index.d.ts +358 -6
  34. package/dist/document/index.js +24 -3
  35. package/dist/document/index.js.map +1 -1
  36. package/dist/filesystem/electron.d.ts +73 -4
  37. package/dist/filesystem/electron.js +16 -3
  38. package/dist/filesystem/electron.js.map +1 -1
  39. package/dist/filesystem/index.d.ts +228 -22
  40. package/dist/filesystem/index.js +133 -19
  41. package/dist/filesystem/index.js.map +1 -1
  42. package/dist/filesystem/indexeddb.d.ts +284 -5
  43. package/dist/filesystem/indexeddb.js +22 -4
  44. package/dist/filesystem/indexeddb.js.map +1 -1
  45. package/dist/filesystem/memory.d.ts +152 -4
  46. package/dist/filesystem/memory.js +12 -3
  47. package/dist/filesystem/memory.js.map +1 -1
  48. package/dist/filesystem/native.d.ts +136 -4
  49. package/dist/filesystem/native.js +27 -3
  50. package/dist/filesystem/native.js.map +1 -1
  51. package/dist/host/index.d.ts +32 -9
  52. package/dist/host/index.js +26 -29
  53. package/dist/host/index.js.map +1 -1
  54. package/dist/index.d.ts +14 -10
  55. package/dist/index.js +204 -9
  56. package/dist/index.js.map +1 -1
  57. package/dist/mcp/index.d.ts +33 -4
  58. package/dist/mcp/index.js +31 -3
  59. package/dist/mcp/index.js.map +1 -1
  60. package/dist/mcp/zod.d.ts +637 -635
  61. package/dist/mcp/zod.js +700 -884
  62. package/dist/mcp/zod.js.map +1 -1
  63. package/dist/share/index.d.ts +43 -0
  64. package/dist/share/index.js +13 -0
  65. package/dist/share/index.js.map +1 -0
  66. package/dist/{types-CsX6EN2t.d.ts → types-BEg5SQ1_.d.ts} +1 -1
  67. package/dist/{types-eOlt9J7n.d.ts → types-CueK0tvU.d.ts} +34 -11
  68. package/dist/vscode/index.d.ts +220 -3
  69. package/dist/vscode/index.js +382 -2
  70. package/dist/vscode/index.js.map +1 -1
  71. package/dist/workspace/index.d.ts +141 -4
  72. package/dist/workspace/index.js +28 -2
  73. package/dist/workspace/index.js.map +1 -1
  74. package/package.json +9 -5
  75. package/dist/chunk-2D7Y4IR3.js.map +0 -1
  76. package/dist/chunk-5MYAICRE.js.map +0 -1
  77. package/dist/chunk-AYZ4HN5M.js +0 -20
  78. package/dist/chunk-AYZ4HN5M.js.map +0 -1
  79. package/dist/chunk-GCPBEMMH.js.map +0 -1
  80. package/dist/chunk-J32GU3F3.js.map +0 -1
  81. package/dist/chunk-PGR7LK6D.js.map +0 -1
  82. package/dist/chunk-RQJRQF46.js.map +0 -1
  83. package/dist/chunk-SI2LU2RP.js.map +0 -1
  84. package/dist/chunk-W5UUUII5.js +0 -142
  85. package/dist/chunk-W5UUUII5.js.map +0 -1
  86. package/dist/document/document-session.d.ts +0 -127
  87. package/dist/document/document-session.d.ts.map +0 -1
  88. package/dist/document/document-session.js +0 -749
  89. package/dist/document/document-session.js.map +0 -1
  90. package/dist/document/filesystem-target.d.ts +0 -17
  91. package/dist/document/filesystem-target.d.ts.map +0 -1
  92. package/dist/document/filesystem-target.js +0 -70
  93. package/dist/document/filesystem-target.js.map +0 -1
  94. package/dist/document/index.d.ts.map +0 -1
  95. package/dist/document/recovery-journal.d.ts +0 -125
  96. package/dist/document/recovery-journal.d.ts.map +0 -1
  97. package/dist/document/recovery-journal.js +0 -385
  98. package/dist/document/recovery-journal.js.map +0 -1
  99. package/dist/document/types.d.ts +0 -89
  100. package/dist/document/types.d.ts.map +0 -1
  101. package/dist/document/types.js +0 -9
  102. package/dist/document/types.js.map +0 -1
  103. package/dist/filesystem/commit-lock.d.ts +0 -3
  104. package/dist/filesystem/commit-lock.d.ts.map +0 -1
  105. package/dist/filesystem/commit-lock.js +0 -20
  106. package/dist/filesystem/commit-lock.js.map +0 -1
  107. package/dist/filesystem/dbk-workspace.d.ts +0 -34
  108. package/dist/filesystem/dbk-workspace.d.ts.map +0 -1
  109. package/dist/filesystem/dbk-workspace.js +0 -85
  110. package/dist/filesystem/dbk-workspace.js.map +0 -1
  111. package/dist/filesystem/electron-provider-v2.d.ts +0 -36
  112. package/dist/filesystem/electron-provider-v2.d.ts.map +0 -1
  113. package/dist/filesystem/electron-provider-v2.js +0 -503
  114. package/dist/filesystem/electron-provider-v2.js.map +0 -1
  115. package/dist/filesystem/electron-provider.d.ts +0 -35
  116. package/dist/filesystem/electron-provider.d.ts.map +0 -1
  117. package/dist/filesystem/electron-provider.js +0 -69
  118. package/dist/filesystem/electron-provider.js.map +0 -1
  119. package/dist/filesystem/electron.d.ts.map +0 -1
  120. package/dist/filesystem/file-media-provider.d.ts +0 -23
  121. package/dist/filesystem/file-media-provider.d.ts.map +0 -1
  122. package/dist/filesystem/file-media-provider.js +0 -85
  123. package/dist/filesystem/file-media-provider.js.map +0 -1
  124. package/dist/filesystem/filesystem-content-container.d.ts +0 -24
  125. package/dist/filesystem/filesystem-content-container.d.ts.map +0 -1
  126. package/dist/filesystem/filesystem-content-container.js +0 -153
  127. package/dist/filesystem/filesystem-content-container.js.map +0 -1
  128. package/dist/filesystem/fs-error.d.ts +0 -46
  129. package/dist/filesystem/fs-error.d.ts.map +0 -1
  130. package/dist/filesystem/fs-error.js +0 -170
  131. package/dist/filesystem/fs-error.js.map +0 -1
  132. package/dist/filesystem/index.d.ts.map +0 -1
  133. package/dist/filesystem/indexeddb-content-container.d.ts +0 -22
  134. package/dist/filesystem/indexeddb-content-container.d.ts.map +0 -1
  135. package/dist/filesystem/indexeddb-content-container.js +0 -100
  136. package/dist/filesystem/indexeddb-content-container.js.map +0 -1
  137. package/dist/filesystem/indexeddb-provider-v2.d.ts +0 -96
  138. package/dist/filesystem/indexeddb-provider-v2.d.ts.map +0 -1
  139. package/dist/filesystem/indexeddb-provider-v2.js +0 -1241
  140. package/dist/filesystem/indexeddb-provider-v2.js.map +0 -1
  141. package/dist/filesystem/indexeddb-provider.d.ts +0 -39
  142. package/dist/filesystem/indexeddb-provider.d.ts.map +0 -1
  143. package/dist/filesystem/indexeddb-provider.js +0 -145
  144. package/dist/filesystem/indexeddb-provider.js.map +0 -1
  145. package/dist/filesystem/indexeddb-store.d.ts +0 -34
  146. package/dist/filesystem/indexeddb-store.d.ts.map +0 -1
  147. package/dist/filesystem/indexeddb-store.js +0 -165
  148. package/dist/filesystem/indexeddb-store.js.map +0 -1
  149. package/dist/filesystem/indexeddb.d.ts.map +0 -1
  150. package/dist/filesystem/legacy-v2-adapter.d.ts +0 -62
  151. package/dist/filesystem/legacy-v2-adapter.d.ts.map +0 -1
  152. package/dist/filesystem/legacy-v2-adapter.js +0 -499
  153. package/dist/filesystem/legacy-v2-adapter.js.map +0 -1
  154. package/dist/filesystem/memory-provider-v2.d.ts +0 -92
  155. package/dist/filesystem/memory-provider-v2.d.ts.map +0 -1
  156. package/dist/filesystem/memory-provider-v2.js +0 -615
  157. package/dist/filesystem/memory-provider-v2.js.map +0 -1
  158. package/dist/filesystem/memory-provider.d.ts +0 -54
  159. package/dist/filesystem/memory-provider.d.ts.map +0 -1
  160. package/dist/filesystem/memory-provider.js +0 -132
  161. package/dist/filesystem/memory-provider.js.map +0 -1
  162. package/dist/filesystem/memory.d.ts.map +0 -1
  163. package/dist/filesystem/move-entry.d.ts +0 -9
  164. package/dist/filesystem/move-entry.d.ts.map +0 -1
  165. package/dist/filesystem/move-entry.js +0 -103
  166. package/dist/filesystem/move-entry.js.map +0 -1
  167. package/dist/filesystem/move-error.d.ts +0 -53
  168. package/dist/filesystem/move-error.d.ts.map +0 -1
  169. package/dist/filesystem/move-error.js +0 -99
  170. package/dist/filesystem/move-error.js.map +0 -1
  171. package/dist/filesystem/native-provider-v2.d.ts +0 -75
  172. package/dist/filesystem/native-provider-v2.d.ts.map +0 -1
  173. package/dist/filesystem/native-provider-v2.js +0 -849
  174. package/dist/filesystem/native-provider-v2.js.map +0 -1
  175. package/dist/filesystem/native-provider.d.ts +0 -53
  176. package/dist/filesystem/native-provider.d.ts.map +0 -1
  177. package/dist/filesystem/native-provider.js +0 -471
  178. package/dist/filesystem/native-provider.js.map +0 -1
  179. package/dist/filesystem/native.d.ts.map +0 -1
  180. package/dist/filesystem/types.d.ts +0 -83
  181. package/dist/filesystem/types.d.ts.map +0 -1
  182. package/dist/filesystem/types.js +0 -16
  183. package/dist/filesystem/types.js.map +0 -1
  184. package/dist/filesystem/v2.d.ts +0 -172
  185. package/dist/filesystem/v2.d.ts.map +0 -1
  186. package/dist/filesystem/v2.js +0 -16
  187. package/dist/filesystem/v2.js.map +0 -1
  188. package/dist/filesystem/workspace-path.d.ts +0 -21
  189. package/dist/filesystem/workspace-path.d.ts.map +0 -1
  190. package/dist/filesystem/workspace-path.js +0 -80
  191. package/dist/filesystem/workspace-path.js.map +0 -1
  192. package/dist/host/filesystem-v2.d.ts +0 -45
  193. package/dist/host/filesystem-v2.d.ts.map +0 -1
  194. package/dist/host/filesystem-v2.js +0 -13
  195. package/dist/host/filesystem-v2.js.map +0 -1
  196. package/dist/host/git.d.ts +0 -200
  197. package/dist/host/git.d.ts.map +0 -1
  198. package/dist/host/git.js +0 -15
  199. package/dist/host/git.js.map +0 -1
  200. package/dist/host/index.d.ts.map +0 -1
  201. package/dist/host/types.d.ts +0 -232
  202. package/dist/host/types.d.ts.map +0 -1
  203. package/dist/host/types.js +0 -10
  204. package/dist/host/types.js.map +0 -1
  205. package/dist/host/wire-policy.d.ts +0 -22
  206. package/dist/host/wire-policy.d.ts.map +0 -1
  207. package/dist/host/wire-policy.js +0 -92
  208. package/dist/host/wire-policy.js.map +0 -1
  209. package/dist/index.d.ts.map +0 -1
  210. package/dist/mcp/index.d.ts.map +0 -1
  211. package/dist/mcp/types.d.ts +0 -466
  212. package/dist/mcp/types.d.ts.map +0 -1
  213. package/dist/mcp/types.js +0 -25
  214. package/dist/mcp/types.js.map +0 -1
  215. package/dist/mcp/wire.d.ts +0 -29
  216. package/dist/mcp/wire.d.ts.map +0 -1
  217. package/dist/mcp/wire.js +0 -1046
  218. package/dist/mcp/wire.js.map +0 -1
  219. package/dist/mcp/zod.d.ts.map +0 -1
  220. package/dist/vscode/index.d.ts.map +0 -1
  221. package/dist/vscode/messages.d.ts +0 -204
  222. package/dist/vscode/messages.d.ts.map +0 -1
  223. package/dist/vscode/messages.js +0 -494
  224. package/dist/vscode/messages.js.map +0 -1
  225. package/dist/workspace/index.d.ts.map +0 -1
  226. package/dist/workspace/reconcile-electron.d.ts +0 -19
  227. package/dist/workspace/reconcile-electron.d.ts.map +0 -1
  228. package/dist/workspace/reconcile-electron.js +0 -42
  229. package/dist/workspace/reconcile-electron.js.map +0 -1
  230. package/dist/workspace/types.d.ts +0 -69
  231. package/dist/workspace/types.d.ts.map +0 -1
  232. package/dist/workspace/types.js +0 -2
  233. package/dist/workspace/types.js.map +0 -1
  234. package/dist/workspace/workspace-manager.d.ts +0 -47
  235. package/dist/workspace/workspace-manager.d.ts.map +0 -1
  236. package/dist/workspace/workspace-manager.js +0 -126
  237. package/dist/workspace/workspace-manager.js.map +0 -1
  238. package/src/document/document-session.ts +0 -834
  239. package/src/document/filesystem-target.ts +0 -109
  240. package/src/document/index.ts +0 -41
  241. package/src/document/recovery-journal.ts +0 -536
  242. package/src/document/types.ts +0 -116
  243. package/src/filesystem/commit-lock.ts +0 -29
  244. package/src/filesystem/dbk-workspace.ts +0 -135
  245. package/src/filesystem/electron-provider-v2.ts +0 -630
  246. package/src/filesystem/electron-provider.ts +0 -99
  247. package/src/filesystem/electron.ts +0 -3
  248. package/src/filesystem/file-media-provider.ts +0 -103
  249. package/src/filesystem/filesystem-content-container.ts +0 -164
  250. package/src/filesystem/fs-error.ts +0 -234
  251. package/src/filesystem/index.ts +0 -143
  252. package/src/filesystem/indexeddb-content-container.ts +0 -112
  253. package/src/filesystem/indexeddb-provider-v2.ts +0 -1740
  254. package/src/filesystem/indexeddb-provider.ts +0 -171
  255. package/src/filesystem/indexeddb-store.ts +0 -207
  256. package/src/filesystem/indexeddb.ts +0 -12
  257. package/src/filesystem/legacy-v2-adapter.ts +0 -779
  258. package/src/filesystem/memory-provider-v2.ts +0 -896
  259. package/src/filesystem/memory-provider.ts +0 -180
  260. package/src/filesystem/memory.ts +0 -15
  261. package/src/filesystem/move-entry.ts +0 -142
  262. package/src/filesystem/move-error.ts +0 -159
  263. package/src/filesystem/native-provider-v2.ts +0 -1165
  264. package/src/filesystem/native-provider.ts +0 -537
  265. package/src/filesystem/native.ts +0 -16
  266. package/src/filesystem/types.ts +0 -114
  267. package/src/filesystem/v2.ts +0 -231
  268. package/src/filesystem/workspace-path.ts +0 -92
  269. package/src/host/filesystem-v2.ts +0 -104
  270. package/src/host/git.ts +0 -246
  271. package/src/host/index.ts +0 -93
  272. package/src/host/types.ts +0 -274
  273. package/src/host/wire-policy.ts +0 -103
  274. package/src/index.ts +0 -10
  275. package/src/mcp/index.ts +0 -3
  276. package/src/mcp/types.ts +0 -594
  277. package/src/mcp/wire.ts +0 -1280
  278. package/src/mcp/zod.ts +0 -1000
  279. package/src/vscode/index.ts +0 -2
  280. package/src/vscode/messages.ts +0 -704
  281. package/src/workspace/index.ts +0 -17
  282. package/src/workspace/reconcile-electron.ts +0 -65
  283. package/src/workspace/types.ts +0 -67
  284. package/src/workspace/workspace-manager.ts +0 -151
  285. /package/dist/{chunk-75UUF6C4.js.map → chunk-B56KWCSE.js.map} +0 -0
  286. /package/dist/{chunk-EGNDADXB.js.map → chunk-MNSZ7EZN.js.map} +0 -0
@@ -1,704 +0,0 @@
1
- /**
2
- * Typed message protocol between the extension host and webview.
3
- */
4
-
5
- import { HOST_WIRE_LIMITS, isBoundedString } from '../host/wire-policy.js';
6
-
7
- const MAX_REQUEST_ID = 2_147_483_647;
8
-
9
- export type DocumentSessionMessageStatus =
10
- | 'idle'
11
- | 'saved'
12
- | 'dirty'
13
- | 'saving'
14
- | 'error'
15
- | 'conflict'
16
- | 'closed';
17
-
18
- export type DocumentConflictChoice = 'use-local' | 'use-external';
19
-
20
- /** Host-observed details that help a user distinguish two text branches. */
21
- export interface DocumentConflictDetailsMessage {
22
- /** VS Code document version on which the DocBlocks draft was based. */
23
- localBaseDocumentVersion: number;
24
- /** Current VS Code document version observed outside the DocBlocks edit pipeline. */
25
- externalDocumentVersion: number;
26
- /** UTF-8 encoded sizes of the complete text snapshots. */
27
- localBytes: number;
28
- externalBytes: number | null;
29
- /** Times are extension-host observations, not filesystem-authoritative mtimes. */
30
- localEditedAt: number | null;
31
- externalObservedAt: number | null;
32
- /** Whether VS Code reported the external document snapshot as not yet saved. */
33
- externalIsDirty: boolean | null;
34
- }
35
-
36
- export const DOCBLOCKS_ACCENT_COLORS = [
37
- 'brown',
38
- 'green',
39
- 'blue',
40
- 'purple',
41
- 'maroon',
42
- 'orange',
43
- 'gray',
44
- ] as const;
45
-
46
- export type DocBlocksAccentColor = (typeof DOCBLOCKS_ACCENT_COLORS)[number];
47
-
48
- export interface VscodeEditorSettings {
49
- autoSave: boolean;
50
- accentColor: DocBlocksAccentColor;
51
- }
52
-
53
- /**
54
- * An opaque, one-shot authority to write one host-owned export target.
55
- * The label is presentation-only and must never be interpreted as a path.
56
- */
57
- export interface ExportTargetGrantMessage {
58
- grantId: string;
59
- displayLabel: string;
60
- }
61
-
62
- /** Messages sent from the extension host to the webview. */
63
- export type ExtensionToWebviewMessage =
64
- | {
65
- type: 'setContent';
66
- content: string;
67
- documentVersion: number;
68
- fileName: string;
69
- sessionId: string;
70
- sessionRevision: number;
71
- acknowledgedClientRevision: number;
72
- }
73
- | {
74
- type: 'editAcknowledged';
75
- sessionId: string;
76
- clientRevision: number;
77
- sessionRevision: number;
78
- accepted: boolean;
79
- message: string | null;
80
- }
81
- | {
82
- type: 'sessionState';
83
- sessionId: string;
84
- status: DocumentSessionMessageStatus;
85
- revision: number;
86
- persistedRevision: number;
87
- acknowledgedClientRevision: number;
88
- documentVersion: number;
89
- error: string | null;
90
- conflict: DocumentConflictDetailsMessage | null;
91
- }
92
- | {
93
- type: 'saveResult';
94
- sessionId: string;
95
- requestId: number;
96
- ok: boolean;
97
- revision: number;
98
- persistedRevision: number;
99
- documentVersion: number;
100
- message: string | null;
101
- }
102
- | { type: 'themeChange'; theme: 'light' | 'dark' }
103
- | { type: 'editorSettings'; settings: VscodeEditorSettings }
104
- | { type: 'mediaResolved'; requestId: number; url: string }
105
- | { type: 'mediaListed'; requestId: number; entries: MediaEntryMessage[] }
106
- | { type: 'mediaAdded'; requestId: number; path: string }
107
- | { type: 'mediaRemoved'; requestId: number }
108
- | { type: 'mediaError'; requestId: number; message: string }
109
- | { type: 'exportSaved'; requestId: number; target: ExportTargetGrantMessage | null }
110
- | { type: 'exportError'; requestId: number; message: string }
111
- | { type: 'exportTargetResolved'; requestId: number; target: ExportTargetGrantMessage | null }
112
- | { type: 'exportTargetPicked'; requestId: number; target: ExportTargetGrantMessage | null }
113
- | { type: 'workspaceFileRead'; requestId: number; dataBase64: string | null }
114
- | { type: 'workspaceFileError'; requestId: number; message: string };
115
-
116
- /** Messages sent from the webview to the extension host. */
117
- export type WebviewToExtensionMessage =
118
- | { type: 'ready' }
119
- | { type: 'setAutoSave'; enabled: boolean }
120
- | { type: 'setAccentColor'; accentColor: DocBlocksAccentColor }
121
- | {
122
- type: 'edit';
123
- content: string;
124
- sessionId: string;
125
- clientRevision: number;
126
- baseDocumentVersion: number;
127
- }
128
- | {
129
- type: 'save';
130
- sessionId: string;
131
- requestId: number;
132
- clientRevision: number;
133
- baseDocumentVersion: number;
134
- }
135
- | { type: 'resolveConflict'; sessionId: string; choice: DocumentConflictChoice }
136
- | { type: 'resolveMedia'; requestId: number; ref: string }
137
- | { type: 'listMedia'; requestId: number }
138
- | { type: 'addMedia'; requestId: number; name: string; dataBase64: string; mimeType: string }
139
- | { type: 'removeMedia'; requestId: number; ref: string }
140
- | {
141
- type: 'saveExport';
142
- requestId: number;
143
- filename: string;
144
- dataBase64: string;
145
- mimeType: string;
146
- grantId: string | null;
147
- }
148
- | { type: 'resolveExportTarget'; requestId: number; filename: string }
149
- | {
150
- type: 'pickExportTarget';
151
- requestId: number;
152
- filename: string;
153
- currentGrantId: string | null;
154
- }
155
- | { type: 'readWorkspaceFile'; requestId: number; path: string };
156
-
157
- export interface MediaEntryMessage {
158
- name: string;
159
- mimeType: string;
160
- size: number;
161
- }
162
-
163
- /**
164
- * The webview boundary is untrusted at runtime. Keep malformed or stale
165
- * messages out of the document session instead of relying on a TypeScript
166
- * assertion in the extension host.
167
- */
168
- export function parseWebviewToExtensionMessage(value: unknown): WebviewToExtensionMessage | null {
169
- if (!isRecord(value) || typeof value.type !== 'string') return null;
170
-
171
- switch (value.type) {
172
- case 'ready':
173
- return hasOnlyKeys(value, ['type']) ? { type: 'ready' } : null;
174
- case 'setAutoSave':
175
- return hasOnlyKeys(value, ['type', 'enabled']) && typeof value.enabled === 'boolean'
176
- ? { type: 'setAutoSave', enabled: value.enabled }
177
- : null;
178
- case 'setAccentColor':
179
- return hasOnlyKeys(value, ['type', 'accentColor']) &&
180
- isDocBlocksAccentColor(value.accentColor)
181
- ? { type: 'setAccentColor', accentColor: value.accentColor }
182
- : null;
183
- case 'edit':
184
- return hasOnlyKeys(value, [
185
- 'type',
186
- 'content',
187
- 'sessionId',
188
- 'clientRevision',
189
- 'baseDocumentVersion',
190
- ]) &&
191
- hasBoundedString(value, 'content', HOST_WIRE_LIMITS.documentCharacters) &&
192
- isSessionEnvelope(value)
193
- ? {
194
- type: 'edit',
195
- content: value.content,
196
- sessionId: value.sessionId,
197
- clientRevision: value.clientRevision,
198
- baseDocumentVersion: value.baseDocumentVersion,
199
- }
200
- : null;
201
- case 'save':
202
- return hasOnlyKeys(value, [
203
- 'type',
204
- 'sessionId',
205
- 'requestId',
206
- 'clientRevision',
207
- 'baseDocumentVersion',
208
- ]) &&
209
- isSessionEnvelope(value) &&
210
- hasRequestId(value)
211
- ? {
212
- type: 'save',
213
- sessionId: value.sessionId,
214
- requestId: value.requestId,
215
- clientRevision: value.clientRevision,
216
- baseDocumentVersion: value.baseDocumentVersion,
217
- }
218
- : null;
219
- case 'resolveConflict':
220
- return hasOnlyKeys(value, ['type', 'sessionId', 'choice']) &&
221
- hasBoundedString(value, 'sessionId', HOST_WIRE_LIMITS.identifierCharacters, 1) &&
222
- (value.choice === 'use-local' || value.choice === 'use-external')
223
- ? { type: 'resolveConflict', sessionId: value.sessionId, choice: value.choice }
224
- : null;
225
- case 'resolveMedia':
226
- return hasOnlyKeys(value, ['type', 'requestId', 'ref']) &&
227
- hasRequestId(value) &&
228
- hasBoundedString(value, 'ref', HOST_WIRE_LIMITS.pathCharacters, 1)
229
- ? { type: 'resolveMedia', requestId: value.requestId, ref: value.ref }
230
- : null;
231
- case 'listMedia':
232
- return hasOnlyKeys(value, ['type', 'requestId']) && hasRequestId(value)
233
- ? { type: 'listMedia', requestId: value.requestId }
234
- : null;
235
- case 'addMedia':
236
- return hasOnlyKeys(value, ['type', 'requestId', 'name', 'dataBase64', 'mimeType']) &&
237
- hasRequestId(value) &&
238
- hasBoundedString(value, 'name', HOST_WIRE_LIMITS.pathCharacters, 1) &&
239
- hasBoundedString(value, 'dataBase64', HOST_WIRE_LIMITS.base64Characters) &&
240
- hasMimeType(value, 'mimeType')
241
- ? {
242
- type: 'addMedia',
243
- requestId: value.requestId,
244
- name: value.name,
245
- dataBase64: value.dataBase64,
246
- mimeType: value.mimeType,
247
- }
248
- : null;
249
- case 'removeMedia':
250
- return hasOnlyKeys(value, ['type', 'requestId', 'ref']) &&
251
- hasRequestId(value) &&
252
- hasBoundedString(value, 'ref', HOST_WIRE_LIMITS.pathCharacters, 1)
253
- ? { type: 'removeMedia', requestId: value.requestId, ref: value.ref }
254
- : null;
255
- case 'saveExport':
256
- return hasOnlyKeys(value, [
257
- 'type',
258
- 'requestId',
259
- 'filename',
260
- 'dataBase64',
261
- 'mimeType',
262
- 'grantId',
263
- ]) &&
264
- hasRequestId(value) &&
265
- hasSafeFilename(value, 'filename') &&
266
- hasBoundedString(value, 'dataBase64', HOST_WIRE_LIMITS.base64Characters) &&
267
- hasMimeType(value, 'mimeType') &&
268
- hasNullableBoundedString(value, 'grantId', HOST_WIRE_LIMITS.identifierCharacters, 1)
269
- ? {
270
- type: 'saveExport',
271
- requestId: value.requestId,
272
- filename: value.filename,
273
- dataBase64: value.dataBase64,
274
- mimeType: value.mimeType,
275
- grantId: value.grantId,
276
- }
277
- : null;
278
- case 'resolveExportTarget':
279
- return hasOnlyKeys(value, ['type', 'requestId', 'filename']) &&
280
- hasRequestId(value) &&
281
- hasSafeFilename(value, 'filename')
282
- ? { type: 'resolveExportTarget', requestId: value.requestId, filename: value.filename }
283
- : null;
284
- case 'pickExportTarget':
285
- return hasOnlyKeys(value, ['type', 'requestId', 'filename', 'currentGrantId']) &&
286
- hasRequestId(value) &&
287
- hasSafeFilename(value, 'filename') &&
288
- hasNullableBoundedString(value, 'currentGrantId', HOST_WIRE_LIMITS.identifierCharacters, 1)
289
- ? {
290
- type: 'pickExportTarget',
291
- requestId: value.requestId,
292
- filename: value.filename,
293
- currentGrantId: value.currentGrantId,
294
- }
295
- : null;
296
- case 'readWorkspaceFile':
297
- return hasOnlyKeys(value, ['type', 'requestId', 'path']) &&
298
- hasRequestId(value) &&
299
- hasBoundedString(value, 'path', HOST_WIRE_LIMITS.pathCharacters, 1)
300
- ? { type: 'readWorkspaceFile', requestId: value.requestId, path: value.path }
301
- : null;
302
- default:
303
- return null;
304
- }
305
- }
306
-
307
- /**
308
- * Validate host responses before they influence webview state. This protects
309
- * the browser side from protocol drift and from messages injected by another
310
- * script in the webview document; a TypeScript MessageEvent annotation is not
311
- * runtime validation.
312
- */
313
- export function parseExtensionToWebviewMessage(value: unknown): ExtensionToWebviewMessage | null {
314
- if (!isRecord(value) || typeof value.type !== 'string') return null;
315
-
316
- switch (value.type) {
317
- case 'setContent':
318
- return hasOnlyKeys(value, [
319
- 'type',
320
- 'content',
321
- 'documentVersion',
322
- 'fileName',
323
- 'sessionId',
324
- 'sessionRevision',
325
- 'acknowledgedClientRevision',
326
- ]) &&
327
- hasBoundedString(value, 'content', HOST_WIRE_LIMITS.documentCharacters) &&
328
- hasNonNegativeInteger(value, 'documentVersion') &&
329
- hasBoundedString(value, 'fileName', HOST_WIRE_LIMITS.pathCharacters, 1) &&
330
- hasBoundedString(value, 'sessionId', HOST_WIRE_LIMITS.identifierCharacters, 1) &&
331
- hasNonNegativeInteger(value, 'sessionRevision') &&
332
- hasNonNegativeInteger(value, 'acknowledgedClientRevision')
333
- ? {
334
- type: 'setContent',
335
- content: value.content,
336
- documentVersion: value.documentVersion,
337
- fileName: value.fileName,
338
- sessionId: value.sessionId,
339
- sessionRevision: value.sessionRevision,
340
- acknowledgedClientRevision: value.acknowledgedClientRevision,
341
- }
342
- : null;
343
- case 'editAcknowledged':
344
- return hasOnlyKeys(value, [
345
- 'type',
346
- 'sessionId',
347
- 'clientRevision',
348
- 'sessionRevision',
349
- 'accepted',
350
- 'message',
351
- ]) &&
352
- hasBoundedString(value, 'sessionId', HOST_WIRE_LIMITS.identifierCharacters, 1) &&
353
- hasNonNegativeInteger(value, 'clientRevision') &&
354
- hasNonNegativeInteger(value, 'sessionRevision') &&
355
- typeof value.accepted === 'boolean' &&
356
- hasNullableBoundedString(value, 'message', HOST_WIRE_LIMITS.messageCharacters)
357
- ? {
358
- type: 'editAcknowledged',
359
- sessionId: value.sessionId,
360
- clientRevision: value.clientRevision,
361
- sessionRevision: value.sessionRevision,
362
- accepted: value.accepted,
363
- message: value.message,
364
- }
365
- : null;
366
- case 'sessionState': {
367
- if (
368
- !hasOnlyKeys(value, [
369
- 'type',
370
- 'sessionId',
371
- 'status',
372
- 'revision',
373
- 'persistedRevision',
374
- 'acknowledgedClientRevision',
375
- 'documentVersion',
376
- 'error',
377
- 'conflict',
378
- ]) ||
379
- !hasBoundedString(value, 'sessionId', HOST_WIRE_LIMITS.identifierCharacters, 1) ||
380
- !isDocumentSessionMessageStatus(value.status) ||
381
- !hasNonNegativeInteger(value, 'revision') ||
382
- !hasNonNegativeInteger(value, 'persistedRevision') ||
383
- value.persistedRevision > value.revision ||
384
- !hasNonNegativeInteger(value, 'acknowledgedClientRevision') ||
385
- !hasNonNegativeInteger(value, 'documentVersion') ||
386
- !hasNullableBoundedString(value, 'error', HOST_WIRE_LIMITS.messageCharacters)
387
- ) {
388
- return null;
389
- }
390
- const conflict = parseDocumentConflictDetails(value.conflict);
391
- if (
392
- conflict === undefined ||
393
- (value.status === 'conflict' && conflict === null) ||
394
- (value.status !== 'conflict' && conflict !== null)
395
- ) {
396
- return null;
397
- }
398
- return {
399
- type: 'sessionState',
400
- sessionId: value.sessionId,
401
- status: value.status,
402
- revision: value.revision,
403
- persistedRevision: value.persistedRevision,
404
- acknowledgedClientRevision: value.acknowledgedClientRevision,
405
- documentVersion: value.documentVersion,
406
- error: value.error,
407
- conflict,
408
- };
409
- }
410
- case 'saveResult':
411
- return hasOnlyKeys(value, [
412
- 'type',
413
- 'sessionId',
414
- 'requestId',
415
- 'ok',
416
- 'revision',
417
- 'persistedRevision',
418
- 'documentVersion',
419
- 'message',
420
- ]) &&
421
- hasBoundedString(value, 'sessionId', HOST_WIRE_LIMITS.identifierCharacters, 1) &&
422
- hasRequestId(value) &&
423
- typeof value.ok === 'boolean' &&
424
- hasNonNegativeInteger(value, 'revision') &&
425
- hasNonNegativeInteger(value, 'persistedRevision') &&
426
- value.persistedRevision <= value.revision &&
427
- hasNonNegativeInteger(value, 'documentVersion') &&
428
- hasNullableBoundedString(value, 'message', HOST_WIRE_LIMITS.messageCharacters)
429
- ? {
430
- type: 'saveResult',
431
- sessionId: value.sessionId,
432
- requestId: value.requestId,
433
- ok: value.ok,
434
- revision: value.revision,
435
- persistedRevision: value.persistedRevision,
436
- documentVersion: value.documentVersion,
437
- message: value.message,
438
- }
439
- : null;
440
- case 'themeChange':
441
- return hasOnlyKeys(value, ['type', 'theme']) &&
442
- (value.theme === 'light' || value.theme === 'dark')
443
- ? { type: 'themeChange', theme: value.theme }
444
- : null;
445
- case 'editorSettings': {
446
- if (!hasOnlyKeys(value, ['type', 'settings'])) return null;
447
- const settings = parseVscodeEditorSettings(value.settings);
448
- return settings ? { type: 'editorSettings', settings } : null;
449
- }
450
- case 'mediaResolved':
451
- return hasOnlyKeys(value, ['type', 'requestId', 'url']) &&
452
- hasRequestId(value) &&
453
- isBoundedMediaDataUrl(value.url)
454
- ? { type: 'mediaResolved', requestId: value.requestId, url: value.url }
455
- : null;
456
- case 'mediaListed': {
457
- if (!hasOnlyKeys(value, ['type', 'requestId', 'entries']) || !hasRequestId(value)) {
458
- return null;
459
- }
460
- const entries = parseMediaEntries(value.entries);
461
- return entries ? { type: 'mediaListed', requestId: value.requestId, entries } : null;
462
- }
463
- case 'mediaAdded':
464
- return hasOnlyKeys(value, ['type', 'requestId', 'path']) &&
465
- hasRequestId(value) &&
466
- hasBoundedString(value, 'path', HOST_WIRE_LIMITS.pathCharacters, 1)
467
- ? { type: 'mediaAdded', requestId: value.requestId, path: value.path }
468
- : null;
469
- case 'mediaRemoved':
470
- return hasOnlyKeys(value, ['type', 'requestId']) && hasRequestId(value)
471
- ? { type: 'mediaRemoved', requestId: value.requestId }
472
- : null;
473
- case 'mediaError':
474
- case 'exportError':
475
- case 'workspaceFileError':
476
- return hasOnlyKeys(value, ['type', 'requestId', 'message']) &&
477
- hasRequestId(value) &&
478
- hasBoundedString(value, 'message', HOST_WIRE_LIMITS.messageCharacters, 1)
479
- ? { type: value.type, requestId: value.requestId, message: value.message }
480
- : null;
481
- case 'exportSaved':
482
- case 'exportTargetResolved':
483
- case 'exportTargetPicked': {
484
- if (!hasOnlyKeys(value, ['type', 'requestId', 'target']) || !hasRequestId(value)) {
485
- return null;
486
- }
487
- const target = parseExportTargetGrant(value.target);
488
- return target !== undefined ? { type: value.type, requestId: value.requestId, target } : null;
489
- }
490
- case 'workspaceFileRead':
491
- return hasOnlyKeys(value, ['type', 'requestId', 'dataBase64']) &&
492
- hasRequestId(value) &&
493
- hasNullableBoundedString(value, 'dataBase64', HOST_WIRE_LIMITS.base64Characters)
494
- ? { type: 'workspaceFileRead', requestId: value.requestId, dataBase64: value.dataBase64 }
495
- : null;
496
- default:
497
- return null;
498
- }
499
- }
500
-
501
- function parseExportTargetGrant(value: unknown): ExportTargetGrantMessage | null | undefined {
502
- if (value === null) return null;
503
- if (
504
- !isRecord(value) ||
505
- !hasOnlyKeys(value, ['grantId', 'displayLabel']) ||
506
- !hasBoundedString(value, 'grantId', HOST_WIRE_LIMITS.identifierCharacters, 1) ||
507
- !hasBoundedString(value, 'displayLabel', HOST_WIRE_LIMITS.pathCharacters, 1)
508
- ) {
509
- return undefined;
510
- }
511
- return { grantId: value.grantId, displayLabel: value.displayLabel };
512
- }
513
-
514
- function parseDocumentConflictDetails(
515
- value: unknown,
516
- ): DocumentConflictDetailsMessage | null | undefined {
517
- if (value === null) return null;
518
- if (
519
- !isRecord(value) ||
520
- !hasOnlyKeys(value, [
521
- 'localBaseDocumentVersion',
522
- 'externalDocumentVersion',
523
- 'localBytes',
524
- 'externalBytes',
525
- 'localEditedAt',
526
- 'externalObservedAt',
527
- 'externalIsDirty',
528
- ]) ||
529
- !hasNonNegativeInteger(value, 'localBaseDocumentVersion') ||
530
- !hasNonNegativeInteger(value, 'externalDocumentVersion') ||
531
- !hasNonNegativeInteger(value, 'localBytes') ||
532
- !isNullableNonNegativeInteger(value.externalBytes) ||
533
- !isNullableNonNegativeInteger(value.localEditedAt) ||
534
- !isNullableNonNegativeInteger(value.externalObservedAt) ||
535
- (value.externalIsDirty !== null && typeof value.externalIsDirty !== 'boolean')
536
- ) {
537
- return undefined;
538
- }
539
- return {
540
- localBaseDocumentVersion: value.localBaseDocumentVersion,
541
- externalDocumentVersion: value.externalDocumentVersion,
542
- localBytes: value.localBytes,
543
- externalBytes: value.externalBytes,
544
- localEditedAt: value.localEditedAt,
545
- externalObservedAt: value.externalObservedAt,
546
- externalIsDirty: value.externalIsDirty,
547
- };
548
- }
549
-
550
- function parseVscodeEditorSettings(value: unknown): VscodeEditorSettings | null {
551
- if (
552
- !isRecord(value) ||
553
- !hasOnlyKeys(value, ['autoSave', 'accentColor']) ||
554
- typeof value.autoSave !== 'boolean' ||
555
- !isDocBlocksAccentColor(value.accentColor)
556
- ) {
557
- return null;
558
- }
559
- return { autoSave: value.autoSave, accentColor: value.accentColor };
560
- }
561
-
562
- export function isDocBlocksAccentColor(value: unknown): value is DocBlocksAccentColor {
563
- return DOCBLOCKS_ACCENT_COLORS.some((color) => color === value);
564
- }
565
-
566
- function parseMediaEntries(value: unknown): MediaEntryMessage[] | null {
567
- if (!Array.isArray(value) || value.length > HOST_WIRE_LIMITS.arrayEntries) return null;
568
- const entries: MediaEntryMessage[] = [];
569
- for (const entry of value) {
570
- if (
571
- !isRecord(entry) ||
572
- !hasOnlyKeys(entry, ['name', 'mimeType', 'size']) ||
573
- !hasBoundedString(entry, 'name', HOST_WIRE_LIMITS.pathCharacters, 1) ||
574
- !hasMimeType(entry, 'mimeType') ||
575
- !hasNonNegativeInteger(entry, 'size') ||
576
- entry.size > HOST_WIRE_LIMITS.binaryBytes
577
- ) {
578
- return null;
579
- }
580
- entries.push({ name: entry.name, mimeType: entry.mimeType, size: entry.size });
581
- }
582
- return entries;
583
- }
584
-
585
- function isBoundedMediaDataUrl(value: unknown): value is string {
586
- if (!isBoundedString(value, HOST_WIRE_LIMITS.base64Characters + 512, 1)) return false;
587
- if (!value.startsWith('data:')) return false;
588
- const separator = value.indexOf(';base64,');
589
- return separator > 5 && separator <= HOST_WIRE_LIMITS.identifierCharacters;
590
- }
591
-
592
- function isDocumentSessionMessageStatus(value: unknown): value is DocumentSessionMessageStatus {
593
- return (
594
- value === 'idle' ||
595
- value === 'saved' ||
596
- value === 'dirty' ||
597
- value === 'saving' ||
598
- value === 'error' ||
599
- value === 'conflict' ||
600
- value === 'closed'
601
- );
602
- }
603
-
604
- function isSessionEnvelope(value: Record<string, unknown>): value is Record<string, unknown> & {
605
- sessionId: string;
606
- clientRevision: number;
607
- baseDocumentVersion: number;
608
- } {
609
- return (
610
- hasBoundedString(value, 'sessionId', HOST_WIRE_LIMITS.identifierCharacters, 1) &&
611
- hasNonNegativeInteger(value, 'clientRevision') &&
612
- hasNonNegativeInteger(value, 'baseDocumentVersion')
613
- );
614
- }
615
-
616
- function hasRequestId(
617
- value: Record<string, unknown>,
618
- ): value is Record<string, unknown> & { requestId: number } {
619
- return hasNonNegativeInteger(value, 'requestId') && value.requestId <= MAX_REQUEST_ID;
620
- }
621
-
622
- function hasBoundedString<K extends string>(
623
- value: Record<string, unknown>,
624
- key: K,
625
- maximumCharacters: number,
626
- minimumCharacters = 0,
627
- ): value is Record<string, unknown> & Record<K, string> {
628
- return isBoundedString(value[key], maximumCharacters, minimumCharacters);
629
- }
630
-
631
- function hasNullableBoundedString<K extends string>(
632
- value: Record<string, unknown>,
633
- key: K,
634
- maximumCharacters: number,
635
- minimumCharacters = 0,
636
- ): value is Record<string, unknown> & Record<K, string | null> {
637
- return value[key] === null || isBoundedString(value[key], maximumCharacters, minimumCharacters);
638
- }
639
-
640
- function hasNonNegativeInteger<K extends string>(
641
- value: Record<string, unknown>,
642
- key: K,
643
- ): value is Record<string, unknown> & Record<K, number> {
644
- const entry = value[key];
645
- return typeof entry === 'number' && Number.isSafeInteger(entry) && entry >= 0;
646
- }
647
-
648
- function isNullableNonNegativeInteger(value: unknown): value is number | null {
649
- return value === null || (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0);
650
- }
651
-
652
- function isRecord(value: unknown): value is Record<string, unknown> {
653
- return typeof value === 'object' && value !== null && !Array.isArray(value);
654
- }
655
-
656
- function hasOnlyKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
657
- const expected = new Set(keys);
658
- const actual = Object.keys(value);
659
- return actual.length === expected.size && actual.every((key) => expected.has(key));
660
- }
661
-
662
- function hasSafeFilename<K extends string>(
663
- value: Record<string, unknown>,
664
- key: K,
665
- ): value is Record<string, unknown> & Record<K, string> {
666
- return isSafeExportFilename(value[key]);
667
- }
668
-
669
- export function isSafeExportFilename(value: unknown): value is string {
670
- return (
671
- isBoundedString(value, 255, 1) &&
672
- value.trim() === value &&
673
- !/[\\/:*?"<>|]/.test(value) &&
674
- !hasControlCharacter(value) &&
675
- value !== '.' &&
676
- value !== '..' &&
677
- !value.endsWith('.') &&
678
- !value.endsWith(' ')
679
- );
680
- }
681
-
682
- function hasControlCharacter(value: string): boolean {
683
- for (let index = 0; index < value.length; index += 1) {
684
- const code = value.charCodeAt(index);
685
- if (code <= 0x1f || code === 0x7f) return true;
686
- }
687
- return false;
688
- }
689
-
690
- function hasMimeType<K extends string>(
691
- value: Record<string, unknown>,
692
- key: K,
693
- ): value is Record<string, unknown> & Record<K, string> {
694
- return isSafeMimeType(value[key]);
695
- }
696
-
697
- export function isSafeMimeType(value: unknown): value is string {
698
- return (
699
- isBoundedString(value, HOST_WIRE_LIMITS.identifierCharacters, 1) &&
700
- /^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+(?:\s*;\s*[A-Za-z0-9!#$&^_.+-]+=[^;\r\n]+)*$/.test(
701
- value,
702
- )
703
- );
704
- }