@bendyline/docblocks 1.1.2 → 2.0.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 (270) hide show
  1. package/README.md +76 -11
  2. package/dist/chunk-2D7Y4IR3.js +1293 -0
  3. package/dist/chunk-2D7Y4IR3.js.map +1 -0
  4. package/dist/chunk-5MYAICRE.js +957 -0
  5. package/dist/chunk-5MYAICRE.js.map +1 -0
  6. package/dist/chunk-75UUF6C4.js +802 -0
  7. package/dist/chunk-75UUF6C4.js.map +1 -0
  8. package/dist/chunk-AEHCVWU5.js +25 -0
  9. package/dist/chunk-AEHCVWU5.js.map +1 -0
  10. package/dist/chunk-AYZ4HN5M.js +20 -0
  11. package/dist/chunk-AYZ4HN5M.js.map +1 -0
  12. package/dist/chunk-EGNDADXB.js +111 -0
  13. package/dist/chunk-EGNDADXB.js.map +1 -0
  14. package/dist/chunk-EZKJZUNJ.js +13 -0
  15. package/dist/chunk-EZKJZUNJ.js.map +1 -0
  16. package/dist/chunk-FNW556FN.js +75 -0
  17. package/dist/chunk-FNW556FN.js.map +1 -0
  18. package/dist/chunk-GCPBEMMH.js +563 -0
  19. package/dist/chunk-GCPBEMMH.js.map +1 -0
  20. package/dist/chunk-J32GU3F3.js +1664 -0
  21. package/dist/chunk-J32GU3F3.js.map +1 -0
  22. package/dist/chunk-PGR7LK6D.js +1062 -0
  23. package/dist/chunk-PGR7LK6D.js.map +1 -0
  24. package/dist/chunk-RQJRQF46.js +787 -0
  25. package/dist/chunk-RQJRQF46.js.map +1 -0
  26. package/dist/{chunk-ME76RUMR.js → chunk-RVQHY3XU.js} +15 -1
  27. package/dist/chunk-RVQHY3XU.js.map +1 -0
  28. package/dist/chunk-SI2LU2RP.js +224 -0
  29. package/dist/chunk-SI2LU2RP.js.map +1 -0
  30. package/dist/chunk-W5UUUII5.js +142 -0
  31. package/dist/chunk-W5UUUII5.js.map +1 -0
  32. package/dist/document/document-session.d.ts +127 -0
  33. package/dist/document/document-session.d.ts.map +1 -0
  34. package/dist/document/document-session.js +749 -0
  35. package/dist/document/document-session.js.map +1 -0
  36. package/dist/document/filesystem-target.d.ts +17 -0
  37. package/dist/document/filesystem-target.d.ts.map +1 -0
  38. package/dist/document/filesystem-target.js +70 -0
  39. package/dist/document/filesystem-target.js.map +1 -0
  40. package/dist/document/index.d.ts +6 -0
  41. package/dist/document/index.d.ts.map +1 -0
  42. package/dist/document/index.js +4 -0
  43. package/dist/document/index.js.map +1 -0
  44. package/dist/document/recovery-journal.d.ts +125 -0
  45. package/dist/document/recovery-journal.d.ts.map +1 -0
  46. package/dist/document/recovery-journal.js +385 -0
  47. package/dist/document/recovery-journal.js.map +1 -0
  48. package/dist/document/types.d.ts +89 -0
  49. package/dist/document/types.d.ts.map +1 -0
  50. package/dist/document/types.js +9 -0
  51. package/dist/document/types.js.map +1 -0
  52. package/dist/filesystem/commit-lock.d.ts +3 -0
  53. package/dist/filesystem/commit-lock.d.ts.map +1 -0
  54. package/dist/filesystem/commit-lock.js +20 -0
  55. package/dist/filesystem/commit-lock.js.map +1 -0
  56. package/dist/filesystem/dbk-workspace.d.ts +34 -0
  57. package/dist/filesystem/dbk-workspace.d.ts.map +1 -0
  58. package/dist/filesystem/dbk-workspace.js +85 -0
  59. package/dist/filesystem/dbk-workspace.js.map +1 -0
  60. package/dist/filesystem/electron-provider-v2.d.ts +36 -0
  61. package/dist/filesystem/electron-provider-v2.d.ts.map +1 -0
  62. package/dist/filesystem/electron-provider-v2.js +503 -0
  63. package/dist/filesystem/electron-provider-v2.js.map +1 -0
  64. package/dist/filesystem/electron-provider.d.ts +6 -3
  65. package/dist/filesystem/electron-provider.d.ts.map +1 -1
  66. package/dist/filesystem/electron-provider.js +18 -13
  67. package/dist/filesystem/electron-provider.js.map +1 -1
  68. package/dist/filesystem/electron.d.ts +4 -0
  69. package/dist/filesystem/electron.d.ts.map +1 -0
  70. package/dist/filesystem/electron.js +4 -0
  71. package/dist/filesystem/electron.js.map +1 -0
  72. package/dist/filesystem/filesystem-content-container.d.ts +1 -1
  73. package/dist/filesystem/filesystem-content-container.d.ts.map +1 -1
  74. package/dist/filesystem/filesystem-content-container.js +50 -13
  75. package/dist/filesystem/filesystem-content-container.js.map +1 -1
  76. package/dist/filesystem/fs-error.d.ts +46 -0
  77. package/dist/filesystem/fs-error.d.ts.map +1 -0
  78. package/dist/filesystem/fs-error.js +170 -0
  79. package/dist/filesystem/fs-error.js.map +1 -0
  80. package/dist/filesystem/index.d.ts +15 -1
  81. package/dist/filesystem/index.d.ts.map +1 -1
  82. package/dist/filesystem/index.js +13 -0
  83. package/dist/filesystem/index.js.map +1 -1
  84. package/dist/filesystem/indexeddb-content-container.d.ts +3 -1
  85. package/dist/filesystem/indexeddb-content-container.d.ts.map +1 -1
  86. package/dist/filesystem/indexeddb-content-container.js +18 -11
  87. package/dist/filesystem/indexeddb-content-container.js.map +1 -1
  88. package/dist/filesystem/indexeddb-provider-v2.d.ts +96 -0
  89. package/dist/filesystem/indexeddb-provider-v2.d.ts.map +1 -0
  90. package/dist/filesystem/indexeddb-provider-v2.js +1241 -0
  91. package/dist/filesystem/indexeddb-provider-v2.js.map +1 -0
  92. package/dist/filesystem/indexeddb-provider.d.ts +21 -14
  93. package/dist/filesystem/indexeddb-provider.d.ts.map +1 -1
  94. package/dist/filesystem/indexeddb-provider.js +111 -261
  95. package/dist/filesystem/indexeddb-provider.js.map +1 -1
  96. package/dist/filesystem/indexeddb-store.d.ts +34 -0
  97. package/dist/filesystem/indexeddb-store.d.ts.map +1 -0
  98. package/dist/filesystem/indexeddb-store.js +165 -0
  99. package/dist/filesystem/indexeddb-store.js.map +1 -0
  100. package/dist/filesystem/indexeddb.d.ts +5 -0
  101. package/dist/filesystem/indexeddb.d.ts.map +1 -0
  102. package/dist/filesystem/indexeddb.js +5 -0
  103. package/dist/filesystem/indexeddb.js.map +1 -0
  104. package/dist/filesystem/legacy-v2-adapter.d.ts +62 -0
  105. package/dist/filesystem/legacy-v2-adapter.d.ts.map +1 -0
  106. package/dist/filesystem/legacy-v2-adapter.js +499 -0
  107. package/dist/filesystem/legacy-v2-adapter.js.map +1 -0
  108. package/dist/filesystem/memory-provider-v2.d.ts +92 -0
  109. package/dist/filesystem/memory-provider-v2.d.ts.map +1 -0
  110. package/dist/filesystem/memory-provider-v2.js +615 -0
  111. package/dist/filesystem/memory-provider-v2.js.map +1 -0
  112. package/dist/filesystem/memory-provider.d.ts +54 -0
  113. package/dist/filesystem/memory-provider.d.ts.map +1 -0
  114. package/dist/filesystem/memory-provider.js +132 -0
  115. package/dist/filesystem/memory-provider.js.map +1 -0
  116. package/dist/filesystem/memory.d.ts +4 -0
  117. package/dist/filesystem/memory.d.ts.map +1 -0
  118. package/dist/filesystem/memory.js +4 -0
  119. package/dist/filesystem/memory.js.map +1 -0
  120. package/dist/filesystem/move-entry.d.ts +9 -0
  121. package/dist/filesystem/move-entry.d.ts.map +1 -0
  122. package/dist/filesystem/move-entry.js +103 -0
  123. package/dist/filesystem/move-entry.js.map +1 -0
  124. package/dist/filesystem/move-error.d.ts +53 -0
  125. package/dist/filesystem/move-error.d.ts.map +1 -0
  126. package/dist/filesystem/move-error.js +99 -0
  127. package/dist/filesystem/move-error.js.map +1 -0
  128. package/dist/filesystem/native-provider-v2.d.ts +75 -0
  129. package/dist/filesystem/native-provider-v2.d.ts.map +1 -0
  130. package/dist/filesystem/native-provider-v2.js +849 -0
  131. package/dist/filesystem/native-provider-v2.js.map +1 -0
  132. package/dist/filesystem/native-provider.d.ts +9 -1
  133. package/dist/filesystem/native-provider.d.ts.map +1 -1
  134. package/dist/filesystem/native-provider.js +172 -37
  135. package/dist/filesystem/native-provider.js.map +1 -1
  136. package/dist/filesystem/native.d.ts +4 -0
  137. package/dist/filesystem/native.d.ts.map +1 -0
  138. package/dist/filesystem/native.js +4 -0
  139. package/dist/filesystem/native.js.map +1 -0
  140. package/dist/filesystem/types.d.ts +37 -4
  141. package/dist/filesystem/types.d.ts.map +1 -1
  142. package/dist/filesystem/types.js +11 -3
  143. package/dist/filesystem/types.js.map +1 -1
  144. package/dist/filesystem/v2.d.ts +172 -0
  145. package/dist/filesystem/v2.d.ts.map +1 -0
  146. package/dist/filesystem/v2.js +16 -0
  147. package/dist/filesystem/v2.js.map +1 -0
  148. package/dist/filesystem/workspace-path.d.ts +21 -0
  149. package/dist/filesystem/workspace-path.d.ts.map +1 -0
  150. package/dist/filesystem/workspace-path.js +80 -0
  151. package/dist/filesystem/workspace-path.js.map +1 -0
  152. package/dist/host/filesystem-v2.d.ts +45 -0
  153. package/dist/host/filesystem-v2.d.ts.map +1 -0
  154. package/dist/host/filesystem-v2.js +13 -0
  155. package/dist/host/filesystem-v2.js.map +1 -0
  156. package/dist/host/git.d.ts +200 -0
  157. package/dist/host/git.d.ts.map +1 -0
  158. package/dist/host/git.js +15 -0
  159. package/dist/host/git.js.map +1 -0
  160. package/dist/host/index.d.ts +4 -1
  161. package/dist/host/index.d.ts.map +1 -1
  162. package/dist/host/index.js +2 -0
  163. package/dist/host/index.js.map +1 -1
  164. package/dist/host/types.d.ts +114 -31
  165. package/dist/host/types.d.ts.map +1 -1
  166. package/dist/host/wire-policy.d.ts +22 -0
  167. package/dist/host/wire-policy.d.ts.map +1 -0
  168. package/dist/host/wire-policy.js +92 -0
  169. package/dist/host/wire-policy.js.map +1 -0
  170. package/dist/index.d.ts +2 -1
  171. package/dist/index.d.ts.map +1 -1
  172. package/dist/index.js +2 -1
  173. package/dist/index.js.map +1 -1
  174. package/dist/mcp/index.d.ts +4 -0
  175. package/dist/mcp/index.d.ts.map +1 -0
  176. package/dist/mcp/index.js +4 -0
  177. package/dist/mcp/index.js.map +1 -0
  178. package/dist/mcp/types.d.ts +466 -0
  179. package/dist/mcp/types.d.ts.map +1 -0
  180. package/dist/mcp/types.js +25 -0
  181. package/dist/mcp/types.js.map +1 -0
  182. package/dist/mcp/wire.d.ts +29 -0
  183. package/dist/mcp/wire.d.ts.map +1 -0
  184. package/dist/mcp/wire.js +1046 -0
  185. package/dist/mcp/wire.js.map +1 -0
  186. package/dist/mcp/zod.d.ts +5763 -0
  187. package/dist/mcp/zod.d.ts.map +1 -0
  188. package/dist/mcp/zod.js +908 -0
  189. package/dist/mcp/zod.js.map +1 -0
  190. package/dist/types-CX893hgH.d.ts +468 -0
  191. package/dist/types-CsX6EN2t.d.ts +301 -0
  192. package/dist/types-eOlt9J7n.d.ts +478 -0
  193. package/dist/vscode/index.d.ts +3 -0
  194. package/dist/vscode/index.d.ts.map +1 -0
  195. package/dist/vscode/index.js +3 -0
  196. package/dist/vscode/index.js.map +1 -0
  197. package/dist/vscode/messages.d.ts +204 -0
  198. package/dist/vscode/messages.d.ts.map +1 -0
  199. package/dist/vscode/messages.js +494 -0
  200. package/dist/vscode/messages.js.map +1 -0
  201. package/dist/workspace/index.d.ts +3 -2
  202. package/dist/workspace/index.d.ts.map +1 -1
  203. package/dist/workspace/index.js +2 -1
  204. package/dist/workspace/index.js.map +1 -1
  205. package/dist/workspace/reconcile-electron.d.ts +19 -0
  206. package/dist/workspace/reconcile-electron.d.ts.map +1 -0
  207. package/dist/workspace/reconcile-electron.js +42 -0
  208. package/dist/workspace/reconcile-electron.js.map +1 -0
  209. package/dist/workspace/types.d.ts +41 -1
  210. package/dist/workspace/types.d.ts.map +1 -1
  211. package/dist/workspace/types.js +0 -4
  212. package/dist/workspace/types.js.map +1 -1
  213. package/dist/workspace/workspace-manager.d.ts +17 -5
  214. package/dist/workspace/workspace-manager.d.ts.map +1 -1
  215. package/dist/workspace/workspace-manager.js +55 -11
  216. package/dist/workspace/workspace-manager.js.map +1 -1
  217. package/dist/workspace-path-CWVrcuPL.d.ts +21 -0
  218. package/package.json +43 -2
  219. package/src/document/document-session.ts +834 -0
  220. package/src/document/filesystem-target.ts +109 -0
  221. package/src/document/index.ts +41 -0
  222. package/src/document/recovery-journal.ts +536 -0
  223. package/src/document/types.ts +116 -0
  224. package/src/filesystem/commit-lock.ts +29 -0
  225. package/src/filesystem/dbk-workspace.ts +135 -0
  226. package/src/filesystem/electron-provider-v2.ts +630 -0
  227. package/src/filesystem/electron-provider.ts +25 -14
  228. package/src/filesystem/electron.ts +3 -0
  229. package/src/filesystem/filesystem-content-container.ts +53 -15
  230. package/src/filesystem/fs-error.ts +234 -0
  231. package/src/filesystem/index.ts +119 -0
  232. package/src/filesystem/indexeddb-content-container.ts +20 -12
  233. package/src/filesystem/indexeddb-provider-v2.ts +1740 -0
  234. package/src/filesystem/indexeddb-provider.ts +126 -300
  235. package/src/filesystem/indexeddb-store.ts +207 -0
  236. package/src/filesystem/indexeddb.ts +12 -0
  237. package/src/filesystem/legacy-v2-adapter.ts +779 -0
  238. package/src/filesystem/memory-provider-v2.ts +896 -0
  239. package/src/filesystem/memory-provider.ts +180 -0
  240. package/src/filesystem/memory.ts +15 -0
  241. package/src/filesystem/move-entry.ts +142 -0
  242. package/src/filesystem/move-error.ts +159 -0
  243. package/src/filesystem/native-provider-v2.ts +1165 -0
  244. package/src/filesystem/native-provider.ts +194 -41
  245. package/src/filesystem/native.ts +16 -0
  246. package/src/filesystem/types.ts +49 -4
  247. package/src/filesystem/v2.ts +231 -0
  248. package/src/filesystem/workspace-path.ts +92 -0
  249. package/src/host/filesystem-v2.ts +104 -0
  250. package/src/host/git.ts +246 -0
  251. package/src/host/index.ts +46 -0
  252. package/src/host/types.ts +155 -34
  253. package/src/host/wire-policy.ts +103 -0
  254. package/src/index.ts +2 -1
  255. package/src/mcp/index.ts +3 -0
  256. package/src/mcp/types.ts +594 -0
  257. package/src/mcp/wire.ts +1280 -0
  258. package/src/mcp/zod.ts +1000 -0
  259. package/src/vscode/index.ts +2 -0
  260. package/src/vscode/messages.ts +704 -0
  261. package/src/workspace/index.ts +8 -1
  262. package/src/workspace/reconcile-electron.ts +65 -0
  263. package/src/workspace/types.ts +39 -1
  264. package/src/workspace/workspace-manager.ts +71 -10
  265. package/dist/chunk-GUNM43XZ.js +0 -63
  266. package/dist/chunk-GUNM43XZ.js.map +0 -1
  267. package/dist/chunk-IM7LCQJ6.js +0 -850
  268. package/dist/chunk-IM7LCQJ6.js.map +0 -1
  269. package/dist/chunk-ME76RUMR.js.map +0 -1
  270. package/dist/index-pgBHBQ9G.d.ts +0 -213
@@ -0,0 +1,1293 @@
1
+ import {
2
+ withFileCommitLock
3
+ } from "./chunk-AEHCVWU5.js";
4
+ import {
5
+ parseFileSystemVersion
6
+ } from "./chunk-AYZ4HN5M.js";
7
+ import {
8
+ FsError,
9
+ WORKSPACE_ROOT,
10
+ fsErrorFromUnknown,
11
+ parseWorkspacePath,
12
+ workspacePathBasename,
13
+ workspacePathContains,
14
+ workspacePathDirname,
15
+ workspacePathJoin
16
+ } from "./chunk-SI2LU2RP.js";
17
+
18
+ // src/filesystem/native-provider-v2.ts
19
+ var NativeFileSystemMoveRecoveryError = class extends FsError {
20
+ constructor(sourcePath, destinationPath, primaryError, recoveryErrors, state) {
21
+ super(
22
+ "io",
23
+ `Native move recovery is incomplete (source: ${state.source}, destination: ${state.destination}).`,
24
+ {
25
+ operation: "move",
26
+ path: sourcePath,
27
+ destinationPath,
28
+ retryable: false
29
+ }
30
+ );
31
+ this.name = "NativeFileSystemMoveRecoveryError";
32
+ this.primaryError = primaryError;
33
+ this.recoveryErrors = Object.freeze([...recoveryErrors]);
34
+ this.state = Object.freeze({ ...state });
35
+ }
36
+ };
37
+ var EPOCH = (/* @__PURE__ */ new Date(0)).toISOString();
38
+ var NATIVE_V2_CAPABILITIES = Object.freeze({
39
+ writeAtomicity: "none",
40
+ moveAtomicity: "none",
41
+ snapshotAtomicity: "none",
42
+ conditionalWrite: "process",
43
+ recursiveRemove: true,
44
+ watch: false,
45
+ caseSensitivity: "platform",
46
+ symlinkPolicy: "unsupported",
47
+ durability: "best-effort"
48
+ });
49
+ var NativeFileSystemProviderV2 = class {
50
+ constructor(id, root) {
51
+ this.capabilities = NATIVE_V2_CAPABILITIES;
52
+ this.subscriptions = /* @__PURE__ */ new Set();
53
+ this.inFlight = /* @__PURE__ */ new Set();
54
+ this.disposed = false;
55
+ this.disposePromise = null;
56
+ this.id = id;
57
+ this.label = root.name;
58
+ this.root = root;
59
+ this.lockKey = `native:${id}:workspace`;
60
+ }
61
+ async stat(path) {
62
+ const canonical = this.canonical(path);
63
+ return this.execute("stat", canonical, null, async () => {
64
+ const scanned = await this.tryScanMetadata(canonical, "stat");
65
+ return scanned ? withoutData(scanned.root) : null;
66
+ });
67
+ }
68
+ async readFile(path) {
69
+ const canonical = this.canonical(path);
70
+ return this.execute("read", canonical, null, async () => {
71
+ const handle = await this.getHandle(canonical);
72
+ if (!handle) return null;
73
+ if (handle.kind !== "file") {
74
+ throw this.error("type-mismatch", "read", canonical, "Expected a file, found a directory.");
75
+ }
76
+ const scanned = await this.scanSnapshotHandle(canonical, handle, "read");
77
+ if (scanned.root.kind !== "file") {
78
+ throw this.error("io", "read", canonical, "Native file handle changed kind during read.");
79
+ }
80
+ return Object.freeze({
81
+ entry: withoutData(scanned.root),
82
+ data: scanned.root.data.slice(0)
83
+ });
84
+ });
85
+ }
86
+ async readDirectory(path) {
87
+ const canonical = this.canonical(path);
88
+ return this.execute("list", canonical, null, async () => {
89
+ const handle = await this.getHandle(canonical);
90
+ if (!handle) throw this.error("not-found", "list", canonical, "Directory does not exist.");
91
+ if (handle.kind !== "directory") {
92
+ throw this.error("type-mismatch", "list", canonical, "Expected a directory, found a file.");
93
+ }
94
+ const children = [];
95
+ for (const [name, childHandle] of await listDirectory(
96
+ handle,
97
+ "list",
98
+ canonical
99
+ )) {
100
+ const childPath = workspacePathJoin(canonical, name);
101
+ children.push(
102
+ withoutData((await this.scanMetadataHandle(childPath, childHandle, "list")).root)
103
+ );
104
+ }
105
+ children.sort(compareSnapshots);
106
+ return Object.freeze(children);
107
+ });
108
+ }
109
+ async writeFile(path, data, options = {}) {
110
+ const canonical = this.canonicalNonRoot(path, "write");
111
+ const ownedData = copyBytes(data);
112
+ return this.mutate("write", canonical, null, async () => {
113
+ const mode = options.mode ?? "upsert";
114
+ if (mode !== "upsert" && mode !== "create" && mode !== "replace") {
115
+ throw this.error("invalid-path", "write", canonical, "Invalid write mode.");
116
+ }
117
+ const existing = await this.tryScanMetadata(canonical, "write");
118
+ if (existing?.root.kind === "directory") {
119
+ throw this.error(
120
+ "type-mismatch",
121
+ "write",
122
+ canonical,
123
+ "Cannot replace a directory with a file."
124
+ );
125
+ }
126
+ if (mode === "create" && existing) {
127
+ throw this.error("already-exists", "write", canonical, "File already exists.");
128
+ }
129
+ if (mode === "replace" && !existing) {
130
+ throw this.error("not-found", "write", canonical, "File does not exist.");
131
+ }
132
+ this.assertExpectedVersion(existing?.root, options.expectedVersion, "write", canonical);
133
+ const missingParents = await this.planParents(
134
+ canonical,
135
+ options.createParents ?? false,
136
+ "write"
137
+ );
138
+ const createdParents = await this.createDirectories(missingParents);
139
+ let writeCompleted = false;
140
+ try {
141
+ await this.writeBytes(canonical, ownedData);
142
+ writeCompleted = true;
143
+ const written = await this.requireSnapshot(canonical, "write");
144
+ if (written.root.kind !== "file" || !equalBytes(written.root.data, ownedData)) {
145
+ throw this.error(
146
+ "conflict",
147
+ "write",
148
+ canonical,
149
+ "Stored bytes changed before the write could be verified."
150
+ );
151
+ }
152
+ return withoutData(written.root);
153
+ } catch (error) {
154
+ if (!existing && !writeCompleted) {
155
+ await this.removeEmptyFileIfPresent(canonical).catch(() => void 0);
156
+ }
157
+ await this.rollbackDirectories(createdParents);
158
+ throw error;
159
+ }
160
+ });
161
+ }
162
+ async createDirectory(path, options = {}) {
163
+ const canonical = this.canonical(path);
164
+ return this.mutate("create-directory", canonical, null, async () => {
165
+ const mode = options.mode ?? "ensure";
166
+ if (mode !== "ensure" && mode !== "create") {
167
+ throw this.error("invalid-path", "create-directory", canonical, "Invalid directory mode.");
168
+ }
169
+ const existing = await this.tryScanMetadata(canonical, "create-directory");
170
+ if (existing) {
171
+ if (existing.root.kind !== "directory") {
172
+ throw this.error(
173
+ "type-mismatch",
174
+ "create-directory",
175
+ canonical,
176
+ "Cannot replace a file with a directory."
177
+ );
178
+ }
179
+ if (mode === "create") {
180
+ throw this.error(
181
+ "already-exists",
182
+ "create-directory",
183
+ canonical,
184
+ "Directory already exists."
185
+ );
186
+ }
187
+ return withoutData(existing.root);
188
+ }
189
+ const missingParents = await this.planParents(
190
+ canonical,
191
+ options.createParents ?? false,
192
+ "create-directory"
193
+ );
194
+ const planned = [...missingParents, canonical];
195
+ const created = await this.createDirectories(planned);
196
+ try {
197
+ const scanned = await this.requireMetadata(canonical, "create-directory");
198
+ if (scanned.root.kind !== "directory") {
199
+ throw this.error(
200
+ "io",
201
+ "create-directory",
202
+ canonical,
203
+ "The native backend did not create a directory."
204
+ );
205
+ }
206
+ return withoutData(scanned.root);
207
+ } catch (error) {
208
+ await this.rollbackDirectories(created);
209
+ throw error;
210
+ }
211
+ });
212
+ }
213
+ async remove(path, options = {}) {
214
+ const canonical = this.canonicalNonRoot(path, "remove");
215
+ return this.mutate("remove", canonical, null, async () => {
216
+ const existing = await this.tryScanMetadata(canonical, "remove");
217
+ if (!existing) {
218
+ if ((options.missing ?? "error") === "ignore") {
219
+ return Object.freeze({
220
+ removed: false,
221
+ version: await this.currentTreeVersion("remove")
222
+ });
223
+ }
224
+ throw this.error("not-found", "remove", canonical, "Entry does not exist.");
225
+ }
226
+ this.assertExpectedVersion(existing.root, options.expectedVersion, "remove", canonical);
227
+ if (existing.root.kind === "directory" && !options.recursive) {
228
+ const handle = await this.getHandle(canonical);
229
+ if (!handle || handle.kind !== "directory") {
230
+ throw this.error("conflict", "remove", canonical, "Directory changed before removal.");
231
+ }
232
+ if ((await listDirectory(handle, "remove", canonical)).length > 0) {
233
+ throw this.error("not-empty", "remove", canonical, "Directory is not empty.");
234
+ }
235
+ }
236
+ await this.removeEntry(canonical, existing.root.kind === "directory" && !!options.recursive);
237
+ if (await this.getHandle(canonical)) {
238
+ throw this.error("conflict", "remove", canonical, "Entry was recreated during removal.");
239
+ }
240
+ return Object.freeze({
241
+ removed: true,
242
+ version: await this.currentTreeVersion("remove")
243
+ });
244
+ });
245
+ }
246
+ async move(oldPath, newPath, options = {}) {
247
+ const sourcePath = this.canonicalNonRoot(oldPath, "move");
248
+ const destinationPath = this.canonicalNonRoot(newPath, "move");
249
+ return this.mutate("move", sourcePath, destinationPath, async () => {
250
+ const sourceMetadata = await this.tryScanMetadata(sourcePath, "move");
251
+ if (!sourceMetadata) {
252
+ throw this.error("not-found", "move", sourcePath, "Source does not exist.");
253
+ }
254
+ this.assertExpectedVersion(sourceMetadata.root, options.expectedVersion, "move", sourcePath);
255
+ if (sourcePath === destinationPath) return withoutData(sourceMetadata.root);
256
+ if (sourceMetadata.root.kind === "directory" && workspacePathContains(sourcePath, destinationPath, false)) {
257
+ throw new FsError("invalid-path", "Cannot move a directory into itself.", {
258
+ operation: "move",
259
+ path: sourcePath,
260
+ destinationPath
261
+ });
262
+ }
263
+ if (await this.getHandle(destinationPath)) {
264
+ throw new FsError("already-exists", "Destination already exists.", {
265
+ operation: "move",
266
+ path: sourcePath,
267
+ destinationPath
268
+ });
269
+ }
270
+ const missingParents = await this.planParents(
271
+ destinationPath,
272
+ options.createParents ?? false,
273
+ "move"
274
+ );
275
+ const source = await this.tryScanSnapshot(sourcePath, "move");
276
+ if (!source || source.root.kind !== sourceMetadata.root.kind || source.root.version !== sourceMetadata.root.version) {
277
+ throw new FsError("conflict", "Source changed before the move could be copied.", {
278
+ operation: "move",
279
+ path: sourcePath,
280
+ destinationPath
281
+ });
282
+ }
283
+ const createdParents = await this.createDirectories(missingParents);
284
+ let destinationVersion = null;
285
+ let sourceDeleted = false;
286
+ try {
287
+ await this.writeTree(source, sourcePath, destinationPath);
288
+ const copied = await this.requireSnapshot(destinationPath, "move");
289
+ destinationVersion = copied.root.version;
290
+ if (!equalTreeContents(source, sourcePath, copied, destinationPath)) {
291
+ throw new FsError("conflict", "Destination changed while the move was copied.", {
292
+ operation: "move",
293
+ path: sourcePath,
294
+ destinationPath
295
+ });
296
+ }
297
+ const sourceBeforeDelete = await this.tryScanMetadata(sourcePath, "move");
298
+ if (!sourceBeforeDelete || sourceBeforeDelete.root.kind !== source.root.kind || sourceBeforeDelete.root.version !== source.root.version) {
299
+ throw new FsError("conflict", "Source changed while the move was copied.", {
300
+ operation: "move",
301
+ path: sourcePath,
302
+ destinationPath
303
+ });
304
+ }
305
+ await this.removeEntry(sourcePath, source.root.kind === "directory");
306
+ sourceDeleted = true;
307
+ const [sourceAfter, destinationAfter] = await Promise.all([
308
+ this.tryScanMetadata(sourcePath, "move"),
309
+ this.tryScanMetadata(destinationPath, "move")
310
+ ]);
311
+ if (sourceAfter || !destinationAfter || destinationAfter.root.kind !== source.root.kind || destinationAfter.root.version !== destinationVersion) {
312
+ throw new FsError("conflict", "Move state changed before it could be verified.", {
313
+ operation: "move",
314
+ path: sourcePath,
315
+ destinationPath
316
+ });
317
+ }
318
+ return withoutData(destinationAfter.root);
319
+ } catch (error) {
320
+ return await this.rollbackMoveAndThrow(
321
+ sourcePath,
322
+ destinationPath,
323
+ source,
324
+ createdParents,
325
+ destinationVersion,
326
+ sourceDeleted,
327
+ error
328
+ );
329
+ }
330
+ });
331
+ }
332
+ async snapshot() {
333
+ return this.mutate("snapshot", WORKSPACE_ROOT, null, async () => {
334
+ const scanned = await this.scanSnapshotHandle(WORKSPACE_ROOT, this.root, "snapshot");
335
+ const entries = [...scanned.entries].sort((left, right) => compareText(left.path, right.path)).map(copySnapshotEntry);
336
+ return Object.freeze({ version: scanned.root.version, entries: Object.freeze(entries) });
337
+ });
338
+ }
339
+ watch(_listener, options = {}) {
340
+ this.assertOpen("watch");
341
+ const error = new FsError("not-supported", "Native filesystem watching is unavailable.", {
342
+ operation: "watch"
343
+ });
344
+ let closed = false;
345
+ let disposePromise = null;
346
+ const ready = Promise.reject(error);
347
+ void ready.catch((caught) => {
348
+ try {
349
+ options.onError?.(caught);
350
+ } catch {
351
+ }
352
+ });
353
+ const subscription = {
354
+ get closed() {
355
+ return closed;
356
+ },
357
+ ready,
358
+ dispose: () => {
359
+ if (disposePromise) return disposePromise;
360
+ closed = true;
361
+ this.subscriptions.delete(subscription);
362
+ disposePromise = Promise.resolve();
363
+ return disposePromise;
364
+ }
365
+ };
366
+ this.subscriptions.add(subscription);
367
+ return subscription;
368
+ }
369
+ dispose() {
370
+ if (this.disposePromise) return this.disposePromise;
371
+ this.disposed = true;
372
+ this.disposePromise = (async () => {
373
+ await Promise.allSettled([...this.inFlight]);
374
+ await Promise.all([...this.subscriptions].map((subscription) => subscription.dispose()));
375
+ })();
376
+ return this.disposePromise;
377
+ }
378
+ canonical(path) {
379
+ return parseWorkspacePath(String(path));
380
+ }
381
+ canonicalNonRoot(path, operation) {
382
+ const canonical = this.canonical(path);
383
+ if (!canonical) {
384
+ throw this.error("invalid-path", operation, canonical, "Workspace root cannot be mutated.");
385
+ }
386
+ return canonical;
387
+ }
388
+ execute(operation, path, destinationPath, work) {
389
+ this.assertOpen(operation);
390
+ const promise = (async () => {
391
+ try {
392
+ return await work();
393
+ } catch (error) {
394
+ throw this.translateError(error, operation, path, destinationPath);
395
+ }
396
+ })();
397
+ this.inFlight.add(promise);
398
+ void promise.then(
399
+ () => this.inFlight.delete(promise),
400
+ () => this.inFlight.delete(promise)
401
+ );
402
+ return promise;
403
+ }
404
+ mutate(operation, path, destinationPath, work) {
405
+ return this.execute(
406
+ operation,
407
+ path,
408
+ destinationPath,
409
+ () => withFileCommitLock(this.lockKey, work)
410
+ );
411
+ }
412
+ assertOpen(operation) {
413
+ if (!this.disposed) return;
414
+ throw new FsError("disposed", "Filesystem provider is disposed.", { operation });
415
+ }
416
+ translateError(error, operation, path, destinationPath) {
417
+ if (error instanceof FsError) return error;
418
+ return fsErrorFromUnknown(error, {
419
+ operation,
420
+ path: path ?? void 0,
421
+ destinationPath: destinationPath ?? void 0
422
+ });
423
+ }
424
+ error(code, operation, path, message) {
425
+ return new FsError(code, message, { operation, path });
426
+ }
427
+ assertExpectedVersion(entry, expected, operation, path) {
428
+ if (expected === void 0) return;
429
+ if (expected === null ? !entry : entry?.version === expected) return;
430
+ throw this.error(
431
+ "conflict",
432
+ operation,
433
+ path,
434
+ "Entry version does not match the expected version."
435
+ );
436
+ }
437
+ async resolveDirectory(path) {
438
+ if (!path) return this.root;
439
+ let current = this.root;
440
+ for (const segment of path.split("/")) {
441
+ try {
442
+ current = await current.getDirectoryHandle(segment);
443
+ } catch (error) {
444
+ if (errorName(error) === "NotFoundError") return null;
445
+ throw error;
446
+ }
447
+ }
448
+ return current;
449
+ }
450
+ async getHandle(path) {
451
+ if (!path) return this.root;
452
+ const parent = await this.resolveDirectory(workspacePathDirname(path));
453
+ if (!parent) return null;
454
+ const name = workspacePathBasename(path);
455
+ try {
456
+ return await parent.getFileHandle(name);
457
+ } catch (error) {
458
+ if (!isMissingOrKindError(error)) throw error;
459
+ }
460
+ try {
461
+ return await parent.getDirectoryHandle(name);
462
+ } catch (error) {
463
+ if (!isMissingOrKindError(error)) throw error;
464
+ return null;
465
+ }
466
+ }
467
+ async tryScanMetadata(path, operation) {
468
+ const handle = await this.getHandle(path);
469
+ return handle ? this.scanMetadataHandle(path, handle, operation) : null;
470
+ }
471
+ async tryScanSnapshot(path, operation) {
472
+ const handle = await this.getHandle(path);
473
+ return handle ? this.scanSnapshotHandle(path, handle, operation) : null;
474
+ }
475
+ async requireMetadata(path, operation) {
476
+ const scanned = await this.tryScanMetadata(path, operation);
477
+ if (!scanned) throw this.error("not-found", operation, path, "Entry does not exist.");
478
+ return scanned;
479
+ }
480
+ async requireSnapshot(path, operation) {
481
+ const scanned = await this.tryScanSnapshot(path, operation);
482
+ if (!scanned) throw this.error("not-found", operation, path, "Entry does not exist.");
483
+ return scanned;
484
+ }
485
+ scanMetadataHandle(path, handle, operation) {
486
+ return this.scanHandle(
487
+ path,
488
+ handle,
489
+ operation,
490
+ (filePath, fileHandle) => this.scanFileMetadata(filePath, fileHandle, operation)
491
+ );
492
+ }
493
+ scanSnapshotHandle(path, handle, operation) {
494
+ return this.scanHandle(
495
+ path,
496
+ handle,
497
+ operation,
498
+ (filePath, fileHandle) => this.scanFileSnapshot(filePath, fileHandle, operation)
499
+ );
500
+ }
501
+ async scanHandle(path, handle, operation, scanFile) {
502
+ if (handle.kind === "file") {
503
+ const entry = await scanFile(path, handle);
504
+ return { root: entry, entries: Object.freeze([entry]) };
505
+ }
506
+ const children = [];
507
+ for (const [name, childHandle] of await listDirectory(
508
+ handle,
509
+ operation,
510
+ path
511
+ )) {
512
+ children.push(
513
+ await this.scanHandle(workspacePathJoin(path, name), childHandle, operation, scanFile)
514
+ );
515
+ }
516
+ const childRoots = children.map((child) => child.root).sort(compareSnapshots);
517
+ const version = await metadataVersion(
518
+ "directory",
519
+ new TextEncoder().encode(
520
+ childRoots.map((child) => `${child.kind}\0${child.name.length}:${child.name}\0${child.version}`).join("\n")
521
+ ),
522
+ operation,
523
+ path
524
+ );
525
+ const directory = Object.freeze({
526
+ kind: "directory",
527
+ path,
528
+ name: workspacePathBasename(path),
529
+ size: null,
530
+ version,
531
+ lastModified: newestLastModified(childRoots)
532
+ });
533
+ return {
534
+ root: directory,
535
+ entries: Object.freeze([directory, ...children.flatMap((child) => child.entries)])
536
+ };
537
+ }
538
+ async scanFileMetadata(path, handle, operation) {
539
+ const file = await this.getFileSnapshot(handle, path, operation);
540
+ return this.fileMetadata(file, path, operation);
541
+ }
542
+ async scanFileSnapshot(path, handle, operation) {
543
+ const file = await this.getFileSnapshot(handle, path, operation);
544
+ const metadata = await this.fileMetadata(file, path, operation);
545
+ let data;
546
+ try {
547
+ data = copyBytes(await file.arrayBuffer());
548
+ } catch (error) {
549
+ throw this.translateError(error, operation, path, null);
550
+ }
551
+ if (metadata.size !== data.byteLength) {
552
+ throw this.error("io", operation, path, "Native file metadata is inconsistent.");
553
+ }
554
+ return Object.freeze({ ...metadata, data });
555
+ }
556
+ async getFileSnapshot(handle, path, operation) {
557
+ try {
558
+ return await handle.getFile();
559
+ } catch (error) {
560
+ throw this.translateError(error, operation, path, null);
561
+ }
562
+ }
563
+ async fileMetadata(file, path, operation) {
564
+ if (!Number.isSafeInteger(file.size) || file.size < 0 || !Number.isFinite(file.lastModified)) {
565
+ throw this.error("io", operation, path, "Native file metadata is inconsistent.");
566
+ }
567
+ let lastModified;
568
+ try {
569
+ lastModified = new Date(file.lastModified).toISOString();
570
+ } catch {
571
+ throw this.error("io", operation, path, "Native file metadata is inconsistent.");
572
+ }
573
+ return Object.freeze({
574
+ kind: "file",
575
+ path,
576
+ name: workspacePathBasename(path),
577
+ size: file.size,
578
+ version: await metadataVersion(
579
+ "file",
580
+ new TextEncoder().encode(`${file.size}\0${file.lastModified}`),
581
+ operation,
582
+ path
583
+ ),
584
+ lastModified
585
+ });
586
+ }
587
+ async planParents(path, createParents, operation) {
588
+ const missing = [];
589
+ for (const parent of parentChain(path)) {
590
+ const handle = await this.getHandle(parent);
591
+ if (handle?.kind === "file") {
592
+ throw this.error("type-mismatch", operation, parent, "A parent path is a file.");
593
+ }
594
+ if (!handle) {
595
+ if (!createParents) {
596
+ throw this.error("not-found", operation, parent, "Parent directory does not exist.");
597
+ }
598
+ missing.push(parent);
599
+ }
600
+ }
601
+ return missing;
602
+ }
603
+ async createDirectories(paths) {
604
+ const created = [];
605
+ try {
606
+ for (const path of paths) {
607
+ if (!path) continue;
608
+ const parent = await this.resolveDirectory(workspacePathDirname(path));
609
+ if (!parent) {
610
+ throw new DOMException("Parent directory does not exist.", "NotFoundError");
611
+ }
612
+ await parent.getDirectoryHandle(workspacePathBasename(path), { create: true });
613
+ created.push(path);
614
+ }
615
+ return created;
616
+ } catch (error) {
617
+ await this.rollbackDirectories(created);
618
+ throw error;
619
+ }
620
+ }
621
+ async rollbackDirectories(paths) {
622
+ for (const path of [...paths].reverse()) {
623
+ try {
624
+ await this.removeEntry(path, false);
625
+ } catch {
626
+ }
627
+ }
628
+ }
629
+ async writeBytes(path, data) {
630
+ const parent = await this.resolveDirectory(workspacePathDirname(path));
631
+ if (!parent) throw new DOMException("Parent directory does not exist.", "NotFoundError");
632
+ const fileHandle = await parent.getFileHandle(workspacePathBasename(path), { create: true });
633
+ const writable = await fileHandle.createWritable();
634
+ await writeAndClose(writable, data);
635
+ }
636
+ async removeEntry(path, recursive) {
637
+ const parent = await this.resolveDirectory(workspacePathDirname(path));
638
+ if (!parent) throw new DOMException("Parent directory does not exist.", "NotFoundError");
639
+ await parent.removeEntry(workspacePathBasename(path), { recursive });
640
+ }
641
+ async removeEmptyFileIfPresent(path) {
642
+ const current = await this.tryScanMetadata(path, "write");
643
+ if (current?.root.kind !== "file" || current.root.size !== 0) return;
644
+ await this.removeEntry(path, false);
645
+ }
646
+ async currentTreeVersion(operation) {
647
+ return (await this.scanMetadataHandle(WORKSPACE_ROOT, this.root, operation)).root.version;
648
+ }
649
+ async writeTree(source, sourcePath, destinationPath) {
650
+ const remapped = source.entries.map((entry) => {
651
+ const suffix = entry.path.slice(sourcePath.length);
652
+ return [entry, parseWorkspacePath(`${destinationPath}${suffix}`)];
653
+ });
654
+ const directories = remapped.filter(
655
+ (item) => item[0].kind === "directory"
656
+ ).sort((left, right) => pathDepth(left[1]) - pathDepth(right[1]));
657
+ for (const [, path] of directories) {
658
+ const parent = await this.resolveDirectory(workspacePathDirname(path));
659
+ if (!parent) throw new DOMException("Parent directory does not exist.", "NotFoundError");
660
+ await parent.getDirectoryHandle(workspacePathBasename(path), { create: true });
661
+ }
662
+ for (const [entry, path] of remapped) {
663
+ if (entry.kind === "file") await this.writeBytes(path, entry.data);
664
+ }
665
+ }
666
+ async rollbackMoveAndThrow(sourcePath, destinationPath, source, createdParents, destinationVersion, sourceDeleted, primary) {
667
+ const primaryError = this.translateError(primary, "move", sourcePath, destinationPath);
668
+ const recoveryErrors = [];
669
+ const sourceCurrent = await this.probeScan(sourcePath);
670
+ if (sourceCurrent === null && sourceDeleted) {
671
+ try {
672
+ await this.writeTree(source, sourcePath, sourcePath);
673
+ const restored = await this.probeSnapshot(sourcePath);
674
+ if (!restored || !equalTreeContents(source, sourcePath, restored, sourcePath)) {
675
+ throw new FsError("conflict", "Restored source does not match its captured state.", {
676
+ operation: "move",
677
+ path: sourcePath,
678
+ destinationPath
679
+ });
680
+ }
681
+ } catch (error) {
682
+ recoveryErrors.push(this.translateError(error, "move", sourcePath, destinationPath));
683
+ }
684
+ }
685
+ const destinationCurrent = await this.probeScan(destinationPath);
686
+ if (destinationCurrent) {
687
+ if (destinationVersion && destinationCurrent.root.version !== destinationVersion) {
688
+ recoveryErrors.push(
689
+ new FsError("conflict", "Destination changed; rollback will not delete it.", {
690
+ operation: "move",
691
+ path: sourcePath,
692
+ destinationPath
693
+ })
694
+ );
695
+ } else {
696
+ try {
697
+ await this.removeEntry(destinationPath, destinationCurrent.root.kind === "directory");
698
+ } catch (error) {
699
+ recoveryErrors.push(this.translateError(error, "move", sourcePath, destinationPath));
700
+ }
701
+ }
702
+ }
703
+ await this.rollbackDirectories(createdParents);
704
+ const state = {
705
+ source: await this.probePresence(sourcePath),
706
+ destination: await this.probePresence(destinationPath)
707
+ };
708
+ if (recoveryErrors.length === 0 && state.source === "present" && state.destination === "missing") {
709
+ throw primaryError;
710
+ }
711
+ throw new NativeFileSystemMoveRecoveryError(
712
+ sourcePath,
713
+ destinationPath,
714
+ primaryError,
715
+ recoveryErrors,
716
+ state
717
+ );
718
+ }
719
+ async probeScan(path) {
720
+ try {
721
+ return await this.tryScanMetadata(path, "move");
722
+ } catch {
723
+ return void 0;
724
+ }
725
+ }
726
+ async probeSnapshot(path) {
727
+ try {
728
+ return await this.tryScanSnapshot(path, "move");
729
+ } catch {
730
+ return void 0;
731
+ }
732
+ }
733
+ async probePresence(path) {
734
+ try {
735
+ return await this.getHandle(path) ? "present" : "missing";
736
+ } catch {
737
+ return "unknown";
738
+ }
739
+ }
740
+ };
741
+ async function listDirectory(directory, operation, path) {
742
+ const entries = [];
743
+ try {
744
+ for await (const entry of directory) {
745
+ const [name, handle] = entry;
746
+ if (typeof name !== "string" || !handle || handle.kind !== "file" && handle.kind !== "directory") {
747
+ throw new FsError("io", "Native directory returned an invalid entry.", {
748
+ operation,
749
+ path
750
+ });
751
+ }
752
+ entries.push([name, handle]);
753
+ }
754
+ } catch (error) {
755
+ throw fsErrorFromUnknown(error, { operation, path });
756
+ }
757
+ entries.sort(([left], [right]) => compareText(left, right));
758
+ return entries;
759
+ }
760
+ async function writeAndClose(writable, data) {
761
+ try {
762
+ await writable.write(data);
763
+ await writable.close();
764
+ } catch (error) {
765
+ await writable.abort(error).catch(() => void 0);
766
+ throw error;
767
+ }
768
+ }
769
+ async function metadataVersion(kind, metadata, operation, path) {
770
+ const prefix = new TextEncoder().encode(`${kind}\0`);
771
+ const input = new Uint8Array(prefix.byteLength + metadata.byteLength);
772
+ input.set(prefix);
773
+ input.set(metadata, prefix.byteLength);
774
+ let digest;
775
+ try {
776
+ digest = await globalThis.crypto.subtle.digest("SHA-256", input.buffer);
777
+ } catch (error) {
778
+ throw fsErrorFromUnknown(error, {
779
+ operation,
780
+ path,
781
+ defaultCode: "not-supported"
782
+ });
783
+ }
784
+ const hex = [...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join("");
785
+ return parseFileSystemVersion(`sha256:${hex}`);
786
+ }
787
+ function parentChain(path) {
788
+ const parents = [];
789
+ let current = workspacePathDirname(path);
790
+ while (current) {
791
+ parents.unshift(current);
792
+ current = workspacePathDirname(current);
793
+ }
794
+ return parents;
795
+ }
796
+ function pathDepth(path) {
797
+ return path ? path.split("/").length : 0;
798
+ }
799
+ function errorName(error) {
800
+ if (!error || typeof error !== "object") return null;
801
+ const name = error.name;
802
+ return typeof name === "string" ? name : null;
803
+ }
804
+ function isMissingOrKindError(error) {
805
+ const name = errorName(error);
806
+ return name === "NotFoundError" || name === "TypeMismatchError";
807
+ }
808
+ function copyBytes(data) {
809
+ const source = ArrayBuffer.isView(data) ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : new Uint8Array(data);
810
+ const copy = new Uint8Array(source.byteLength);
811
+ copy.set(source);
812
+ return copy.buffer;
813
+ }
814
+ function equalBytes(left, right) {
815
+ const leftBytes = new Uint8Array(left);
816
+ const rightBytes = new Uint8Array(right);
817
+ return leftBytes.byteLength === rightBytes.byteLength && leftBytes.every((value, index) => value === rightBytes[index]);
818
+ }
819
+ function equalTreeContents(left, leftRoot, right, rightRoot) {
820
+ if (left.entries.length !== right.entries.length) return false;
821
+ const rightByRelativePath = /* @__PURE__ */ new Map();
822
+ for (const entry of right.entries) {
823
+ const relativePath = treeRelativePath(rightRoot, entry.path);
824
+ if (relativePath === null || rightByRelativePath.has(relativePath)) return false;
825
+ rightByRelativePath.set(relativePath, entry);
826
+ }
827
+ for (const entry of left.entries) {
828
+ const relativePath = treeRelativePath(leftRoot, entry.path);
829
+ if (relativePath === null) return false;
830
+ const counterpart = rightByRelativePath.get(relativePath);
831
+ if (!counterpart || counterpart.kind !== entry.kind) return false;
832
+ if (entry.kind === "file") {
833
+ if (counterpart.kind !== "file" || !equalBytes(entry.data, counterpart.data)) return false;
834
+ }
835
+ }
836
+ return true;
837
+ }
838
+ function treeRelativePath(root, path) {
839
+ if (path === root) return "";
840
+ const prefix = root ? `${root}/` : "";
841
+ return path.startsWith(prefix) ? path.slice(prefix.length) : null;
842
+ }
843
+ function withoutData(entry) {
844
+ if (entry.kind === "directory") return Object.freeze({ ...entry });
845
+ return Object.freeze({
846
+ kind: "file",
847
+ path: entry.path,
848
+ name: entry.name,
849
+ size: entry.size,
850
+ version: entry.version,
851
+ lastModified: entry.lastModified
852
+ });
853
+ }
854
+ function copySnapshotEntry(entry) {
855
+ if (entry.kind === "directory") return Object.freeze({ ...entry });
856
+ return Object.freeze({
857
+ kind: "file",
858
+ path: entry.path,
859
+ name: entry.name,
860
+ size: entry.size,
861
+ version: entry.version,
862
+ lastModified: entry.lastModified,
863
+ data: entry.data.slice(0)
864
+ });
865
+ }
866
+ function newestLastModified(children) {
867
+ let newest = 0;
868
+ for (const child of children) {
869
+ const timestamp = Date.parse(child.lastModified);
870
+ if (Number.isFinite(timestamp)) newest = Math.max(newest, timestamp);
871
+ }
872
+ return newest ? new Date(newest).toISOString() : EPOCH;
873
+ }
874
+ function compareSnapshots(left, right) {
875
+ if (left.kind !== right.kind) return left.kind === "directory" ? -1 : 1;
876
+ return compareText(left.name, right.name);
877
+ }
878
+ function compareText(left, right) {
879
+ return left < right ? -1 : left > right ? 1 : 0;
880
+ }
881
+
882
+ // src/filesystem/native-provider.ts
883
+ function isNativeFileSystemSupported() {
884
+ return typeof globalThis !== "undefined" && typeof globalThis.showDirectoryPicker === "function";
885
+ }
886
+ var HANDLE_DB_NAME = "docblocks-handles";
887
+ var HANDLE_STORE_NAME = "directory-handles";
888
+ function openHandleDB() {
889
+ return new Promise((resolve, reject) => {
890
+ const req = indexedDB.open(HANDLE_DB_NAME, 1);
891
+ req.onupgradeneeded = () => {
892
+ req.result.createObjectStore(HANDLE_STORE_NAME);
893
+ };
894
+ req.onsuccess = () => resolve(req.result);
895
+ req.onerror = () => reject(req.error);
896
+ });
897
+ }
898
+ async function storeDirectoryHandle(workspaceId, handle) {
899
+ const db = await openHandleDB();
900
+ return new Promise((resolve, reject) => {
901
+ const tx = db.transaction(HANDLE_STORE_NAME, "readwrite");
902
+ tx.objectStore(HANDLE_STORE_NAME).put(handle, workspaceId);
903
+ tx.oncomplete = () => {
904
+ db.close();
905
+ resolve();
906
+ };
907
+ tx.onerror = () => {
908
+ db.close();
909
+ reject(tx.error);
910
+ };
911
+ });
912
+ }
913
+ async function loadDirectoryHandle(workspaceId) {
914
+ const db = await openHandleDB();
915
+ return new Promise((resolve, reject) => {
916
+ const tx = db.transaction(HANDLE_STORE_NAME, "readonly");
917
+ const req = tx.objectStore(HANDLE_STORE_NAME).get(workspaceId);
918
+ req.onsuccess = () => {
919
+ db.close();
920
+ resolve(req.result ?? null);
921
+ };
922
+ req.onerror = () => {
923
+ db.close();
924
+ reject(req.error);
925
+ };
926
+ });
927
+ }
928
+ async function removeDirectoryHandle(workspaceId) {
929
+ const db = await openHandleDB();
930
+ return new Promise((resolve, reject) => {
931
+ const tx = db.transaction(HANDLE_STORE_NAME, "readwrite");
932
+ tx.objectStore(HANDLE_STORE_NAME).delete(workspaceId);
933
+ tx.oncomplete = () => {
934
+ db.close();
935
+ resolve();
936
+ };
937
+ tx.onerror = () => {
938
+ db.close();
939
+ reject(tx.error);
940
+ };
941
+ });
942
+ }
943
+ function normalisePath(p) {
944
+ return parseWorkspacePath(p);
945
+ }
946
+ function errorName2(error) {
947
+ return typeof error === "object" && error !== null && "name" in error ? String(error.name) : void 0;
948
+ }
949
+ function isMissingEntryError(error) {
950
+ const name = errorName2(error);
951
+ return name === "NotFoundError" || name === "TypeMismatchError";
952
+ }
953
+ function isPermissionError(error) {
954
+ const name = errorName2(error);
955
+ return name === "NotAllowedError" || name === "SecurityError";
956
+ }
957
+ var NativeMoveRecoveryError = class extends Error {
958
+ constructor(message, causes) {
959
+ super(message);
960
+ this.name = "NativeMoveRecoveryError";
961
+ this.causes = causes;
962
+ }
963
+ };
964
+ async function resolveDir(root, dirPath) {
965
+ if (!dirPath) return root;
966
+ const parts = dirPath.split("/");
967
+ let current = root;
968
+ for (const part of parts) {
969
+ try {
970
+ current = await current.getDirectoryHandle(part);
971
+ } catch (error) {
972
+ if (isMissingEntryError(error)) return null;
973
+ throw error;
974
+ }
975
+ }
976
+ return current;
977
+ }
978
+ async function resolveDirCreate(root, dirPath) {
979
+ if (!dirPath) return root;
980
+ const parts = dirPath.split("/");
981
+ let current = root;
982
+ for (const part of parts) {
983
+ current = await current.getDirectoryHandle(part, { create: true });
984
+ }
985
+ return current;
986
+ }
987
+ function parentDir(p) {
988
+ const idx = p.lastIndexOf("/");
989
+ return idx === -1 ? "" : p.slice(0, idx);
990
+ }
991
+ function baseName(p) {
992
+ const idx = p.lastIndexOf("/");
993
+ return idx === -1 ? p : p.slice(idx + 1);
994
+ }
995
+ function toWritableBinary(data) {
996
+ if (data instanceof ArrayBuffer) return data;
997
+ const copy = new Uint8Array(data.byteLength);
998
+ copy.set(data);
999
+ return copy.buffer;
1000
+ }
1001
+ async function writeAndClose2(writable, data) {
1002
+ try {
1003
+ await writable.write(data);
1004
+ await writable.close();
1005
+ } catch (error) {
1006
+ await writable.abort(error).catch(() => void 0);
1007
+ throw error;
1008
+ }
1009
+ }
1010
+ var NativeFileSystemProvider = class {
1011
+ constructor(id, root) {
1012
+ this.id = id;
1013
+ this.label = root.name;
1014
+ this.root = root;
1015
+ this.v2 = new NativeFileSystemProviderV2(id, root);
1016
+ }
1017
+ withMutationLock(operation) {
1018
+ return withFileCommitLock(`native:${this.id}:workspace`, operation);
1019
+ }
1020
+ async copyDirectory(oldDirPath, newDirPath) {
1021
+ const source = await resolveDir(this.root, oldDirPath);
1022
+ if (!source)
1023
+ throw new DOMException(`Source directory does not exist: ${oldDirPath}`, "NotFoundError");
1024
+ await resolveDirCreate(this.root, newDirPath);
1025
+ for await (const [name, handle] of source) {
1026
+ const oldChild = oldDirPath ? `${oldDirPath}/${name}` : name;
1027
+ const newChild = newDirPath ? `${newDirPath}/${name}` : name;
1028
+ if (handle.kind === "directory") {
1029
+ await this.copyDirectory(oldChild, newChild);
1030
+ } else {
1031
+ const fileHandle = await source.getFileHandle(name);
1032
+ const file = await fileHandle.getFile();
1033
+ await this.writeBinaryUnlocked(newChild, await file.arrayBuffer());
1034
+ }
1035
+ }
1036
+ }
1037
+ async readFile(path) {
1038
+ const p = normalisePath(path);
1039
+ const dir = await resolveDir(this.root, parentDir(p));
1040
+ if (!dir) return null;
1041
+ try {
1042
+ const fileHandle = await dir.getFileHandle(baseName(p));
1043
+ const file = await fileHandle.getFile();
1044
+ return file.text();
1045
+ } catch (error) {
1046
+ if (isMissingEntryError(error)) return null;
1047
+ throw error;
1048
+ }
1049
+ }
1050
+ async writeFile(path, content) {
1051
+ return this.withMutationLock(() => this.writeFileUnlocked(path, content));
1052
+ }
1053
+ async writeFileUnlocked(path, content) {
1054
+ const p = normalisePath(path);
1055
+ const dir = await resolveDirCreate(this.root, parentDir(p));
1056
+ const fileHandle = await dir.getFileHandle(baseName(p), { create: true });
1057
+ const writable = await fileHandle.createWritable();
1058
+ await writeAndClose2(writable, content);
1059
+ }
1060
+ async commitFile(path, content, expectedContent) {
1061
+ const p = normalisePath(path);
1062
+ return this.withMutationLock(async () => {
1063
+ const current = await this.readFile(p);
1064
+ if (current !== expectedContent && current !== content) {
1065
+ const meta2 = await this.stat(p);
1066
+ return {
1067
+ status: "conflict",
1068
+ content: current,
1069
+ version: meta2?.lastModified ?? null
1070
+ };
1071
+ }
1072
+ await this.writeFileUnlocked(p, content);
1073
+ const meta = await this.stat(p);
1074
+ return { status: "committed", version: meta?.lastModified ?? null };
1075
+ });
1076
+ }
1077
+ async delete(path) {
1078
+ return this.withMutationLock(() => this.deleteUnlocked(path));
1079
+ }
1080
+ async deleteUnlocked(path) {
1081
+ const p = normalisePath(path);
1082
+ if (!p) throw new Error("Cannot delete the filesystem root");
1083
+ const parent = parentDir(p);
1084
+ const name = baseName(p);
1085
+ const dir = await resolveDir(this.root, parent);
1086
+ if (!dir) return;
1087
+ try {
1088
+ await dir.removeEntry(name, { recursive: true });
1089
+ } catch (error) {
1090
+ if (isMissingEntryError(error)) return;
1091
+ throw error;
1092
+ }
1093
+ }
1094
+ async rename(oldPath, newPath) {
1095
+ return this.withMutationLock(() => this.renameUnlocked(oldPath, newPath));
1096
+ }
1097
+ async renameUnlocked(oldPath, newPath) {
1098
+ const op = normalisePath(oldPath);
1099
+ const np = normalisePath(newPath);
1100
+ if (op === np) return;
1101
+ if (!op || !np) {
1102
+ throw new Error("Cannot rename the filesystem root");
1103
+ }
1104
+ if (np.startsWith(op + "/")) {
1105
+ throw new Error("Cannot move a directory into itself");
1106
+ }
1107
+ if (!await this.exists(op)) {
1108
+ throw new Error(`Source does not exist: ${oldPath}`);
1109
+ }
1110
+ if (await this.exists(np)) {
1111
+ throw new Error(`Destination exists: ${newPath}`);
1112
+ }
1113
+ const oldParent = await resolveDir(this.root, parentDir(op));
1114
+ if (!oldParent) {
1115
+ throw new DOMException(`Source does not exist: ${oldPath}`, "NotFoundError");
1116
+ }
1117
+ let fileHandle = null;
1118
+ try {
1119
+ fileHandle = await oldParent.getFileHandle(baseName(op));
1120
+ } catch (error) {
1121
+ if (!isMissingEntryError(error)) throw error;
1122
+ }
1123
+ if (fileHandle) {
1124
+ const file = await fileHandle.getFile();
1125
+ await this.writeBinaryUnlocked(np, await file.arrayBuffer());
1126
+ try {
1127
+ await this.deleteUnlocked(op);
1128
+ } catch (error) {
1129
+ try {
1130
+ await this.deleteUnlocked(np);
1131
+ } catch (rollbackError) {
1132
+ throw new NativeMoveRecoveryError(
1133
+ `Move partially completed and rollback failed: ${oldPath} -> ${newPath}`,
1134
+ [error, rollbackError]
1135
+ );
1136
+ }
1137
+ throw error;
1138
+ }
1139
+ return;
1140
+ }
1141
+ try {
1142
+ await this.copyDirectory(op, np);
1143
+ } catch (error) {
1144
+ try {
1145
+ await this.deleteUnlocked(np);
1146
+ } catch (rollbackError) {
1147
+ throw new NativeMoveRecoveryError(
1148
+ `Move failed and destination cleanup failed: ${oldPath} -> ${newPath}`,
1149
+ [error, rollbackError]
1150
+ );
1151
+ }
1152
+ throw error;
1153
+ }
1154
+ try {
1155
+ await this.deleteUnlocked(op);
1156
+ } catch (error) {
1157
+ try {
1158
+ await this.deleteUnlocked(np);
1159
+ } catch (rollbackError) {
1160
+ throw new NativeMoveRecoveryError(
1161
+ `Move partially completed and rollback failed: ${oldPath} -> ${newPath}`,
1162
+ [error, rollbackError]
1163
+ );
1164
+ }
1165
+ throw error;
1166
+ }
1167
+ }
1168
+ async readDirectory(path) {
1169
+ const p = normalisePath(path);
1170
+ const dir = await resolveDir(this.root, p);
1171
+ if (!dir) return [];
1172
+ const entries = [];
1173
+ for await (const [name, handle] of dir) {
1174
+ const entryPath = p ? `${p}/${name}` : name;
1175
+ if (handle.kind === "directory") {
1176
+ entries.push({ kind: "directory", name, path: entryPath });
1177
+ } else {
1178
+ entries.push({ kind: "file", name, path: entryPath });
1179
+ }
1180
+ }
1181
+ entries.sort((a, b) => {
1182
+ if (a.kind !== b.kind) return a.kind === "directory" ? -1 : 1;
1183
+ return a.name.localeCompare(b.name);
1184
+ });
1185
+ return entries;
1186
+ }
1187
+ async exists(path) {
1188
+ const p = normalisePath(path);
1189
+ const parent = parentDir(p);
1190
+ const name = baseName(p);
1191
+ const dir = await resolveDir(this.root, parent);
1192
+ if (!dir) return false;
1193
+ try {
1194
+ await dir.getFileHandle(name);
1195
+ return true;
1196
+ } catch (fileError) {
1197
+ if (!isMissingEntryError(fileError)) throw fileError;
1198
+ try {
1199
+ await dir.getDirectoryHandle(name);
1200
+ return true;
1201
+ } catch (directoryError) {
1202
+ if (!isMissingEntryError(directoryError)) throw directoryError;
1203
+ return false;
1204
+ }
1205
+ }
1206
+ }
1207
+ async createDirectory(path) {
1208
+ return this.withMutationLock(async () => {
1209
+ const p = normalisePath(path);
1210
+ await resolveDirCreate(this.root, p);
1211
+ });
1212
+ }
1213
+ async stat(path) {
1214
+ const p = normalisePath(path);
1215
+ const dir = await resolveDir(this.root, parentDir(p));
1216
+ if (!dir) return null;
1217
+ try {
1218
+ const fileHandle = await dir.getFileHandle(baseName(p));
1219
+ const file = await fileHandle.getFile();
1220
+ return {
1221
+ name: file.name,
1222
+ path: p,
1223
+ size: file.size,
1224
+ lastModified: new Date(file.lastModified).toISOString()
1225
+ };
1226
+ } catch (error) {
1227
+ if (isMissingEntryError(error)) return null;
1228
+ throw error;
1229
+ }
1230
+ }
1231
+ async readBinary(path) {
1232
+ const p = normalisePath(path);
1233
+ const dir = await resolveDir(this.root, parentDir(p));
1234
+ if (!dir) return null;
1235
+ try {
1236
+ const fileHandle = await dir.getFileHandle(baseName(p));
1237
+ const file = await fileHandle.getFile();
1238
+ return file.arrayBuffer();
1239
+ } catch (error) {
1240
+ if (isMissingEntryError(error)) return null;
1241
+ throw error;
1242
+ }
1243
+ }
1244
+ async writeBinary(path, data) {
1245
+ return this.withMutationLock(() => this.writeBinaryUnlocked(path, data));
1246
+ }
1247
+ async writeBinaryUnlocked(path, data) {
1248
+ const p = normalisePath(path);
1249
+ const dir = await resolveDirCreate(this.root, parentDir(p));
1250
+ const fileHandle = await dir.getFileHandle(baseName(p), { create: true });
1251
+ const writable = await fileHandle.createWritable();
1252
+ await writeAndClose2(writable, toWritableBinary(data));
1253
+ }
1254
+ };
1255
+ async function openNativeFolder() {
1256
+ if (!isNativeFileSystemSupported()) {
1257
+ throw new Error("File System Access API is not supported in this browser");
1258
+ }
1259
+ const handle = await globalThis.showDirectoryPicker({ mode: "readwrite" });
1260
+ const id = `native-${handle.name}-${Date.now()}`;
1261
+ await storeDirectoryHandle(id, handle);
1262
+ return new NativeFileSystemProvider(id, handle);
1263
+ }
1264
+ async function restoreNativeFolder(workspaceId) {
1265
+ const handle = await loadDirectoryHandle(workspaceId);
1266
+ if (!handle) return null;
1267
+ const opts = { mode: "readwrite" };
1268
+ const h = handle;
1269
+ try {
1270
+ if (await h.queryPermission(opts) === "granted") {
1271
+ return new NativeFileSystemProvider(workspaceId, handle);
1272
+ }
1273
+ if (await h.requestPermission(opts) === "granted") {
1274
+ return new NativeFileSystemProvider(workspaceId, handle);
1275
+ }
1276
+ } catch (error) {
1277
+ if (!isPermissionError(error)) throw error;
1278
+ }
1279
+ return null;
1280
+ }
1281
+
1282
+ export {
1283
+ NativeFileSystemMoveRecoveryError,
1284
+ NativeFileSystemProviderV2,
1285
+ isNativeFileSystemSupported,
1286
+ storeDirectoryHandle,
1287
+ loadDirectoryHandle,
1288
+ removeDirectoryHandle,
1289
+ NativeFileSystemProvider,
1290
+ openNativeFolder,
1291
+ restoreNativeFolder
1292
+ };
1293
+ //# sourceMappingURL=chunk-2D7Y4IR3.js.map