@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
@@ -1,99 +0,0 @@
1
- /**
2
- * ElectronFileSystemProvider — implements FileSystemProvider by delegating
3
- * to the Electron desktop host's fs IPC bridge. Every operation is scoped
4
- * to an opaque workspace id that the main process resolves against its
5
- * registered workspace authority.
6
- *
7
- * This file has no Electron dependency — it is a pure IPC client that
8
- * relies on the `docBlocksHost` global installed by the preload script.
9
- */
10
-
11
- import type { FileCommitResult, FileSystemProvider, FileSystemEntry, FileMeta } from './types.js';
12
- import { maybeGetDocBlocksHost } from '../host/index.js';
13
- import type { DocBlocksHostFsAPI } from '../host/types.js';
14
- import { ElectronFileSystemProviderV2 } from './electron-provider-v2.js';
15
-
16
- export { isElectronHost } from '../host/index.js';
17
-
18
- function getHostFs(): DocBlocksHostFsAPI {
19
- const host = maybeGetDocBlocksHost();
20
- if (!host) {
21
- throw new Error(
22
- 'ElectronFileSystemProvider: docBlocksHost is not available — not running under Electron?',
23
- );
24
- }
25
- return host.fs;
26
- }
27
-
28
- export class ElectronFileSystemProvider implements FileSystemProvider {
29
- readonly id: string;
30
- readonly label: string;
31
- readonly v2: ElectronFileSystemProviderV2;
32
-
33
- private readonly rootPath: string;
34
-
35
- constructor(id: string, label: string, rootPath: string) {
36
- this.id = id;
37
- this.label = label;
38
- this.rootPath = rootPath;
39
- this.v2 = new ElectronFileSystemProviderV2(id, label, rootPath);
40
- }
41
-
42
- /** Absolute path this provider is rooted at. */
43
- getRootPath(): string {
44
- return this.rootPath;
45
- }
46
-
47
- readFile(path: string): Promise<string | null> {
48
- return getHostFs().readFile(this.id, path);
49
- }
50
-
51
- writeFile(path: string, content: string): Promise<void> {
52
- return getHostFs().writeFile(this.id, path, content);
53
- }
54
-
55
- commitFile(
56
- path: string,
57
- content: string,
58
- expectedContent: string | null,
59
- ): Promise<FileCommitResult> {
60
- return getHostFs().commitFile(this.id, path, content, expectedContent);
61
- }
62
-
63
- delete(path: string): Promise<void> {
64
- return getHostFs().delete(this.id, path);
65
- }
66
-
67
- rename(oldPath: string, newPath: string): Promise<void> {
68
- return getHostFs().rename(this.id, oldPath, newPath);
69
- }
70
-
71
- readDirectory(path: string): Promise<FileSystemEntry[]> {
72
- return getHostFs().readDirectory(this.id, path);
73
- }
74
-
75
- exists(path: string): Promise<boolean> {
76
- return getHostFs().exists(this.id, path);
77
- }
78
-
79
- createDirectory(path: string): Promise<void> {
80
- return getHostFs().createDirectory(this.id, path);
81
- }
82
-
83
- stat(path: string): Promise<FileMeta | null> {
84
- return getHostFs().stat(this.id, path);
85
- }
86
-
87
- readBinary(path: string): Promise<ArrayBuffer | null> {
88
- return getHostFs().readBinary(this.id, path);
89
- }
90
-
91
- writeBinary(path: string, data: ArrayBuffer | Uint8Array): Promise<void> {
92
- return getHostFs().writeBinary(this.id, path, data);
93
- }
94
-
95
- /** Subscribe to external change notifications under this root. */
96
- watch(onChange: (changedPath: string) => void): () => void {
97
- return getHostFs().watch(this.id, onChange);
98
- }
99
- }
@@ -1,3 +0,0 @@
1
- /** Electron renderer filesystem provider entry point. */
2
- export { ElectronFileSystemProvider, isElectronHost } from './electron-provider.js';
3
- export { ElectronFileSystemProviderV2 } from './electron-provider-v2.js';
@@ -1,103 +0,0 @@
1
- /**
2
- * createFileMediaProvider — per-file media storage following the pandoc /
3
- * Word convention: a markdown file `notes.md` gets a sibling folder
4
- * `notes_files/` that holds its images, audio, and video.
5
- *
6
- * Given:
7
- * • `container` — a ContentContainer scoped to the markdown file's
8
- * parent directory (so `readFile('notes_files/image.png')` maps to the
9
- * parent-relative path)
10
- * • `markdownBasename` — e.g. `"notes.md"`
11
- *
12
- * Returns a MediaProvider that:
13
- * • Writes new media under `{basename}_files/{name}` in the parent dir
14
- * • Returns the folder-qualified path (`notes_files/image.png`) from
15
- * addMedia so the markdown stays portable outside DocBlocks
16
- * • Resolves both bare (`image.png`) and folder-qualified
17
- * (`notes_files/image.png`) references — so legacy markdown and
18
- * exports from other tools both work
19
- */
20
-
21
- import type { MediaProvider, MediaEntry } from '@bendyline/squisq/schemas';
22
- import type { ContentContainer } from '@bendyline/squisq/storage';
23
-
24
- function stripExt(name: string): string {
25
- return name.replace(/\.[^.]+$/, '');
26
- }
27
-
28
- export function createFileMediaProvider(
29
- container: ContentContainer,
30
- markdownBasename: string,
31
- ): MediaProvider {
32
- const folder = stripExt(markdownBasename) + '_files';
33
- const prefix = folder + '/';
34
- const blobUrlCache = new Map<string, string>();
35
-
36
- function toKey(ref: string): string {
37
- const clean = ref.replace(/^\/+/, '');
38
- return clean.startsWith(prefix) ? clean : prefix + clean;
39
- }
40
-
41
- return {
42
- async resolveUrl(ref: string): Promise<string> {
43
- const key = toKey(ref);
44
- const cached = blobUrlCache.get(key);
45
- if (cached) return cached;
46
-
47
- const data = await container.readFile(key);
48
- if (!data) return ref;
49
-
50
- const entries = await container.listFiles();
51
- const entry = entries.find((e) => e.path === key);
52
- const mimeType = entry?.mimeType ?? 'application/octet-stream';
53
-
54
- const url = URL.createObjectURL(new Blob([data], { type: mimeType }));
55
- blobUrlCache.set(key, url);
56
- return url;
57
- },
58
-
59
- async listMedia(): Promise<MediaEntry[]> {
60
- const entries = await container.listFiles(prefix);
61
- return entries
62
- .filter((e) => !e.path.toLowerCase().endsWith('.md'))
63
- .map((e) => ({
64
- name: e.path,
65
- mimeType: e.mimeType,
66
- size: e.size,
67
- }));
68
- },
69
-
70
- async addMedia(
71
- name: string,
72
- data: ArrayBuffer | Blob | Uint8Array,
73
- mimeType: string,
74
- ): Promise<string> {
75
- const key = toKey(name);
76
- const cached = blobUrlCache.get(key);
77
- if (cached) {
78
- URL.revokeObjectURL(cached);
79
- blobUrlCache.delete(key);
80
- }
81
- const buffer = data instanceof Blob ? new Uint8Array(await data.arrayBuffer()) : data;
82
- await container.writeFile(key, buffer, mimeType);
83
- return key;
84
- },
85
-
86
- async removeMedia(ref: string): Promise<void> {
87
- const key = toKey(ref);
88
- const cached = blobUrlCache.get(key);
89
- if (cached) {
90
- URL.revokeObjectURL(cached);
91
- blobUrlCache.delete(key);
92
- }
93
- await container.removeFile(key);
94
- },
95
-
96
- dispose(): void {
97
- for (const url of blobUrlCache.values()) {
98
- URL.revokeObjectURL(url);
99
- }
100
- blobUrlCache.clear();
101
- },
102
- };
103
- }
@@ -1,164 +0,0 @@
1
- /**
2
- * FileSystemContentContainer — a ContentContainer backed by any
3
- * FileSystemProvider, scoped to a sub-path (e.g., ".docblocks/media/").
4
- *
5
- * Used by the Electron desktop app so media lives inside the workspace
6
- * folder (visible as regular files) rather than in a separate IndexedDB
7
- * origin that only the app can see.
8
- */
9
-
10
- import type { ContentContainer, ContentEntry } from '@bendyline/squisq/storage';
11
- import { findDocumentPath } from '@bendyline/squisq/storage';
12
- import { FsError } from './fs-error.js';
13
- import { getFileSystemProviderV2, type FileSystemProvider } from './types.js';
14
- import { parseWorkspacePath, workspacePathContains, workspacePathJoin } from './workspace-path.js';
15
-
16
- const EXTENSION_MIME_MAP: Record<string, string> = {
17
- '.md': 'text/markdown',
18
- '.txt': 'text/plain',
19
- '.json': 'application/json',
20
- '.jpg': 'image/jpeg',
21
- '.jpeg': 'image/jpeg',
22
- '.png': 'image/png',
23
- '.gif': 'image/gif',
24
- '.svg': 'image/svg+xml',
25
- '.webp': 'image/webp',
26
- '.avif': 'image/avif',
27
- '.mp4': 'video/mp4',
28
- '.webm': 'video/webm',
29
- '.mp3': 'audio/mpeg',
30
- '.wav': 'audio/wav',
31
- '.ogg': 'audio/ogg',
32
- };
33
-
34
- function guessMimeType(path: string): string {
35
- const dot = path.lastIndexOf('.');
36
- if (dot === -1) return 'application/octet-stream';
37
- const ext = path.slice(dot).toLowerCase();
38
- return EXTENSION_MIME_MAP[ext] ?? 'application/octet-stream';
39
- }
40
-
41
- function joinPrefix(prefix: string, p: string): string {
42
- return workspacePathJoin(parseWorkspacePath(prefix), p);
43
- }
44
-
45
- export class FileSystemContentContainer implements ContentContainer {
46
- private readonly prefix: string;
47
-
48
- constructor(
49
- private readonly provider: FileSystemProvider,
50
- prefix = '.docblocks/media',
51
- ) {
52
- this.prefix = parseWorkspacePath(prefix);
53
- }
54
-
55
- async readFile(path: string): Promise<ArrayBuffer | null> {
56
- const full = joinPrefix(this.prefix, path);
57
- const providerV2 = getFileSystemProviderV2(this.provider);
58
- if (providerV2) return (await providerV2.readFile(parseWorkspacePath(full)))?.data ?? null;
59
- const binary = await this.provider.readBinary(full);
60
- if (binary) return binary;
61
- // IndexedDB-backed workspaces store text via `writeFile(string)` and
62
- // binary via `writeBinary(ArrayBuffer)` under separate keys, so a
63
- // markdown file written through the text API is invisible to
64
- // `readBinary`. Fall back to text-then-UTF-8 so consumers like the
65
- // recursive HTML export can resolve sibling `.md` links uniformly
66
- // across browser and native workspaces.
67
- const text = await this.provider.readFile(full);
68
- if (text === null) return null;
69
- return new TextEncoder().encode(text).buffer as ArrayBuffer;
70
- }
71
-
72
- async writeFile(path: string, data: ArrayBuffer | Uint8Array, _mimeType?: string): Promise<void> {
73
- const full = joinPrefix(this.prefix, path);
74
- const providerV2 = getFileSystemProviderV2(this.provider);
75
- if (providerV2) {
76
- await providerV2.writeFile(parseWorkspacePath(full), data, {
77
- mode: 'upsert',
78
- createParents: true,
79
- });
80
- return;
81
- }
82
- await this.provider.writeBinary(full, data);
83
- }
84
-
85
- async removeFile(path: string): Promise<void> {
86
- const full = joinPrefix(this.prefix, path);
87
- const providerV2 = getFileSystemProviderV2(this.provider);
88
- if (providerV2) {
89
- await providerV2.remove(parseWorkspacePath(full), { missing: 'ignore' });
90
- return;
91
- }
92
- await this.provider.delete(full);
93
- }
94
-
95
- async listFiles(prefix?: string): Promise<ContentEntry[]> {
96
- const entries: ContentEntry[] = [];
97
- const providerV2 = getFileSystemProviderV2(this.provider);
98
- const root = parseWorkspacePath(this.prefix);
99
- type ListedEntry = {
100
- readonly kind: 'file' | 'directory';
101
- readonly path: string;
102
- readonly size?: number | null;
103
- };
104
- const walk = async (dir: string) => {
105
- let children: readonly ListedEntry[];
106
- try {
107
- children = providerV2
108
- ? await providerV2.readDirectory(parseWorkspacePath(dir))
109
- : await this.provider.readDirectory(dir);
110
- } catch (error: unknown) {
111
- // An absent container root means an empty container. Every other
112
- // storage fault (including a subtree disappearing mid-snapshot)
113
- // remains observable to backup/export callers.
114
- if (dir === root && error instanceof FsError && error.code === 'not-found') return;
115
- throw error;
116
- }
117
- for (const child of children) {
118
- if (child.kind === 'directory') {
119
- await walk(child.path);
120
- } else {
121
- const childPath = parseWorkspacePath(child.path);
122
- const prefixPath = parseWorkspacePath(this.prefix);
123
- if (!workspacePathContains(prefixPath, childPath)) continue;
124
- const rel = childPath === prefixPath ? '' : childPath.slice(prefixPath.length + 1);
125
- if (prefix && !rel.startsWith(prefix)) continue;
126
- const size = child.size ?? (await this.provider.stat(child.path))?.size ?? 0;
127
- entries.push({
128
- path: rel,
129
- mimeType: guessMimeType(rel),
130
- size,
131
- });
132
- }
133
- }
134
- };
135
- await walk(root);
136
- return entries;
137
- }
138
-
139
- async exists(path: string): Promise<boolean> {
140
- const full = joinPrefix(this.prefix, path);
141
- const providerV2 = getFileSystemProviderV2(this.provider);
142
- return providerV2
143
- ? (await providerV2.stat(parseWorkspacePath(full))) !== null
144
- : this.provider.exists(full);
145
- }
146
-
147
- async getDocumentPath(): Promise<string | null> {
148
- return findDocumentPath(await this.listFiles());
149
- }
150
-
151
- async readDocument(): Promise<string | null> {
152
- const docPath = await this.getDocumentPath();
153
- if (!docPath) return null;
154
- const data = await this.readFile(docPath);
155
- if (!data) return null;
156
- return new TextDecoder().decode(data);
157
- }
158
-
159
- async writeDocument(markdown: string, filename?: string): Promise<void> {
160
- const name = filename ?? 'index.md';
161
- const data = new TextEncoder().encode(markdown);
162
- await this.writeFile(name, data, 'text/markdown');
163
- }
164
- }
@@ -1,234 +0,0 @@
1
- /** Stable error codes shared by every filesystem transport and backend. */
2
- export type FsErrorCode =
3
- | 'invalid-path'
4
- | 'path-escape'
5
- | 'not-found'
6
- | 'already-exists'
7
- | 'type-mismatch'
8
- | 'not-empty'
9
- | 'permission-denied'
10
- | 'conflict'
11
- | 'quota-exceeded'
12
- | 'not-supported'
13
- | 'busy'
14
- | 'disposed'
15
- | 'closed'
16
- | 'aborted'
17
- | 'io';
18
-
19
- export type FsOperation =
20
- | 'parse-path'
21
- | 'read'
22
- | 'stat'
23
- | 'list'
24
- | 'write'
25
- | 'create-directory'
26
- | 'remove'
27
- | 'move'
28
- | 'snapshot'
29
- | 'watch'
30
- | 'dispose';
31
-
32
- /** Plain-data form safe to send over IPC, postMessage, or JSON. */
33
- export interface SerializedFsError {
34
- readonly name: 'FsError';
35
- readonly code: FsErrorCode;
36
- readonly message: string;
37
- readonly operation: FsOperation | null;
38
- readonly path: string | null;
39
- readonly destinationPath: string | null;
40
- readonly retryable: boolean;
41
- }
42
-
43
- export interface FsErrorContext {
44
- readonly operation?: FsOperation;
45
- readonly path?: string;
46
- readonly destinationPath?: string;
47
- readonly retryable?: boolean;
48
- readonly defaultCode?: FsErrorCode;
49
- }
50
-
51
- const FS_ERROR_CODES: ReadonlySet<string> = new Set<FsErrorCode>([
52
- 'invalid-path',
53
- 'path-escape',
54
- 'not-found',
55
- 'already-exists',
56
- 'type-mismatch',
57
- 'not-empty',
58
- 'permission-denied',
59
- 'conflict',
60
- 'quota-exceeded',
61
- 'not-supported',
62
- 'busy',
63
- 'disposed',
64
- 'closed',
65
- 'aborted',
66
- 'io',
67
- ]);
68
-
69
- const FS_OPERATIONS: ReadonlySet<string> = new Set<FsOperation>([
70
- 'parse-path',
71
- 'read',
72
- 'stat',
73
- 'list',
74
- 'write',
75
- 'create-directory',
76
- 'remove',
77
- 'move',
78
- 'snapshot',
79
- 'watch',
80
- 'dispose',
81
- ]);
82
-
83
- /** Error with a stable code and a deliberately serializable public shape. */
84
- export class FsError extends Error {
85
- public readonly code: FsErrorCode;
86
- public readonly operation: FsOperation | null;
87
- public readonly path: string | null;
88
- public readonly destinationPath: string | null;
89
- public readonly retryable: boolean;
90
-
91
- public constructor(code: FsErrorCode, message: string, context: FsErrorContext = {}) {
92
- super(message);
93
- this.name = 'FsError';
94
- this.code = code;
95
- this.operation = context.operation ?? null;
96
- this.path = context.path ?? null;
97
- this.destinationPath = context.destinationPath ?? null;
98
- this.retryable = context.retryable ?? (code === 'io' || code === 'aborted' || code === 'busy');
99
- }
100
-
101
- public toJSON(): SerializedFsError {
102
- return {
103
- name: 'FsError',
104
- code: this.code,
105
- message: this.message,
106
- operation: this.operation,
107
- path: this.path,
108
- destinationPath: this.destinationPath,
109
- retryable: this.retryable,
110
- };
111
- }
112
- }
113
-
114
- export function mapDomExceptionToFsErrorCode(name: string): FsErrorCode {
115
- switch (name) {
116
- case 'NotFoundError':
117
- return 'not-found';
118
- case 'ConstraintError':
119
- return 'already-exists';
120
- case 'TypeMismatchError':
121
- return 'type-mismatch';
122
- case 'InvalidModificationError':
123
- return 'not-empty';
124
- case 'NotAllowedError':
125
- case 'SecurityError':
126
- case 'NoModificationAllowedError':
127
- return 'permission-denied';
128
- case 'QuotaExceededError':
129
- return 'quota-exceeded';
130
- case 'NotSupportedError':
131
- return 'not-supported';
132
- case 'InvalidStateError':
133
- return 'disposed';
134
- case 'AbortError':
135
- return 'aborted';
136
- default:
137
- return 'io';
138
- }
139
- }
140
-
141
- export function mapNodeErrorCodeToFsErrorCode(code: string): FsErrorCode {
142
- switch (code) {
143
- case 'ENOENT':
144
- return 'not-found';
145
- case 'ENOTDIR':
146
- return 'type-mismatch';
147
- case 'EEXIST':
148
- return 'already-exists';
149
- case 'EISDIR':
150
- return 'type-mismatch';
151
- case 'ENOTEMPTY':
152
- return 'not-empty';
153
- case 'EACCES':
154
- case 'EPERM':
155
- return 'permission-denied';
156
- case 'ENOSPC':
157
- case 'EDQUOT':
158
- return 'quota-exceeded';
159
- case 'EBUSY':
160
- return 'busy';
161
- case 'EXDEV':
162
- case 'ENOTSUP':
163
- case 'EOPNOTSUPP':
164
- return 'not-supported';
165
- default:
166
- return 'io';
167
- }
168
- }
169
-
170
- export function isSerializedFsError(value: unknown): value is SerializedFsError {
171
- if (!value || typeof value !== 'object') return false;
172
- const candidate = value as Partial<Record<keyof SerializedFsError, unknown>>;
173
- return (
174
- candidate.name === 'FsError' &&
175
- typeof candidate.code === 'string' &&
176
- FS_ERROR_CODES.has(candidate.code) &&
177
- typeof candidate.message === 'string' &&
178
- (candidate.operation === null ||
179
- (typeof candidate.operation === 'string' && FS_OPERATIONS.has(candidate.operation))) &&
180
- (candidate.path === null || typeof candidate.path === 'string') &&
181
- (candidate.destinationPath === null || typeof candidate.destinationPath === 'string') &&
182
- typeof candidate.retryable === 'boolean'
183
- );
184
- }
185
-
186
- export function deserializeFsError(value: SerializedFsError): FsError {
187
- return new FsError(value.code, value.message, {
188
- operation: value.operation ?? undefined,
189
- path: value.path ?? undefined,
190
- destinationPath: value.destinationPath ?? undefined,
191
- retryable: value.retryable,
192
- });
193
- }
194
-
195
- export function fsErrorFromUnknown(error: unknown, context: FsErrorContext = {}): FsError {
196
- if (error instanceof FsError) return error;
197
- if (isSerializedFsError(error)) return deserializeFsError(error);
198
-
199
- const name = getErrorStringField(error, 'name');
200
- const nodeCode = getErrorStringField(error, 'code');
201
- const message = getErrorStringField(error, 'message') ?? String(error);
202
- const code =
203
- context.defaultCode ??
204
- (nodeCode
205
- ? mapNodeErrorCodeToFsErrorCode(nodeCode)
206
- : name
207
- ? mapDomExceptionToFsErrorCode(name)
208
- : 'io');
209
- return new FsError(code, message, context);
210
- }
211
-
212
- export function serializeFsError(error: unknown, context: FsErrorContext = {}): SerializedFsError {
213
- return fsErrorFromUnknown(error, context).toJSON();
214
- }
215
-
216
- /**
217
- * True when a storage backend rejected an operation because the origin or
218
- * device is out of space. Covers typed `FsError`s (`'quota-exceeded'`, from
219
- * DOMException `QuotaExceededError` or Node `ENOSPC`/`EDQUOT`), serialized
220
- * transport copies, and raw DOMExceptions from backends that do not wrap
221
- * their errors (e.g. the File System Access API) — including cross-realm
222
- * instances where `instanceof DOMException` would fail.
223
- */
224
- export function isQuotaExceededError(error: unknown): boolean {
225
- if (error instanceof FsError) return error.code === 'quota-exceeded';
226
- if (isSerializedFsError(error)) return error.code === 'quota-exceeded';
227
- return getErrorStringField(error, 'name') === 'QuotaExceededError';
228
- }
229
-
230
- function getErrorStringField(error: unknown, key: 'name' | 'message' | 'code'): string | null {
231
- if (!error || typeof error !== 'object') return null;
232
- const value = (error as Record<string, unknown>)[key];
233
- return typeof value === 'string' ? value : null;
234
- }