opfs-worker 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +62 -439
  2. package/dist/BaseOPFS-BPw9AIQ-.cjs +4 -0
  3. package/dist/BaseOPFS-BPw9AIQ-.cjs.map +1 -0
  4. package/dist/BaseOPFS-Cylz6ijk.js +2036 -0
  5. package/dist/BaseOPFS-Cylz6ijk.js.map +1 -0
  6. package/dist/OPFSAsync-BKSAJJeh.js +124 -0
  7. package/dist/OPFSAsync-BKSAJJeh.js.map +1 -0
  8. package/dist/OPFSAsync-s2P7y3sw.cjs +2 -0
  9. package/dist/OPFSAsync-s2P7y3sw.cjs.map +1 -0
  10. package/dist/OPFSFacade-DePnNRgt.cjs +2 -0
  11. package/dist/OPFSFacade-DePnNRgt.cjs.map +1 -0
  12. package/dist/OPFSFacade-XohWlHuu.js +281 -0
  13. package/dist/OPFSFacade-XohWlHuu.js.map +1 -0
  14. package/dist/OPFSSync-C7KBepe_.cjs +2 -0
  15. package/dist/OPFSSync-C7KBepe_.cjs.map +1 -0
  16. package/dist/OPFSSync-CsSQkpk5.js +208 -0
  17. package/dist/OPFSSync-CsSQkpk5.js.map +1 -0
  18. package/dist/assets/dedicated.worker-nY3tK4zi.js.map +1 -0
  19. package/dist/{OPFSWorker.d.ts → core/BaseOPFS.d.ts} +34 -304
  20. package/dist/core/BaseOPFS.d.ts.map +1 -0
  21. package/dist/core/OPFSAsync.d.ts +55 -0
  22. package/dist/core/OPFSAsync.d.ts.map +1 -0
  23. package/dist/core/OPFSSync.d.ts +54 -0
  24. package/dist/core/OPFSSync.d.ts.map +1 -0
  25. package/dist/createOPFSAsync-BwKDmHsy.cjs +2 -0
  26. package/dist/createOPFSAsync-BwKDmHsy.cjs.map +1 -0
  27. package/dist/createOPFSAsync-Y4Mg6qte.js +10 -0
  28. package/dist/createOPFSAsync-Y4Mg6qte.js.map +1 -0
  29. package/dist/createOPFSDedicated-CsnV6MLP.js +2333 -0
  30. package/dist/createOPFSDedicated-CsnV6MLP.js.map +1 -0
  31. package/dist/createOPFSDedicated-pKTRWLdz.cjs +2283 -0
  32. package/dist/createOPFSDedicated-pKTRWLdz.cjs.map +1 -0
  33. package/dist/createOPFSShared-CGdd__PH.js +23 -0
  34. package/dist/createOPFSShared-CGdd__PH.js.map +1 -0
  35. package/dist/createOPFSShared-CskROt7U.cjs +2 -0
  36. package/dist/createOPFSShared-CskROt7U.cjs.map +1 -0
  37. package/dist/dedicated.worker.js +2281 -0
  38. package/dist/dedicated.worker.js.map +1 -0
  39. package/dist/{OPFSFacade.d.ts → facade/OPFSFacade.d.ts} +31 -23
  40. package/dist/facade/OPFSFacade.d.ts.map +1 -0
  41. package/dist/facade/createOPFSAsync.d.ts +16 -0
  42. package/dist/facade/createOPFSAsync.d.ts.map +1 -0
  43. package/dist/facade/createOPFSDedicated.d.ts +17 -0
  44. package/dist/facade/createOPFSDedicated.d.ts.map +1 -0
  45. package/dist/facade/createOPFSShared.d.ts +24 -0
  46. package/dist/facade/createOPFSShared.d.ts.map +1 -0
  47. package/dist/index.async.cjs +2 -0
  48. package/dist/index.async.cjs.map +1 -0
  49. package/dist/index.async.d.ts +10 -0
  50. package/dist/index.async.d.ts.map +1 -0
  51. package/dist/index.async.js +55 -0
  52. package/dist/index.async.js.map +1 -0
  53. package/dist/index.cjs +1 -2477
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.d.ts +17 -3
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +61 -2828
  58. package/dist/index.js.map +1 -1
  59. package/dist/index.pure.cjs +1 -1
  60. package/dist/index.pure.cjs.map +1 -1
  61. package/dist/index.pure.d.ts +3 -1
  62. package/dist/index.pure.d.ts.map +1 -1
  63. package/dist/index.pure.js +50 -1053
  64. package/dist/index.pure.js.map +1 -1
  65. package/dist/index.sharedworker.cjs +2 -0
  66. package/dist/index.sharedworker.cjs.map +1 -0
  67. package/dist/index.sharedworker.d.ts +11 -0
  68. package/dist/index.sharedworker.d.ts.map +1 -0
  69. package/dist/index.sharedworker.js +55 -0
  70. package/dist/index.sharedworker.js.map +1 -0
  71. package/dist/index.sync.cjs +2 -0
  72. package/dist/index.sync.cjs.map +1 -0
  73. package/dist/index.sync.d.ts +11 -0
  74. package/dist/index.sync.d.ts.map +1 -0
  75. package/dist/index.sync.js +55 -0
  76. package/dist/index.sync.js.map +1 -0
  77. package/dist/shared.worker.js +2166 -0
  78. package/dist/shared.worker.js.map +1 -0
  79. package/dist/types.d.ts +9 -4
  80. package/dist/types.d.ts.map +1 -1
  81. package/dist/worker/createDedicatedWorker.d.ts +30 -0
  82. package/dist/worker/createDedicatedWorker.d.ts.map +1 -0
  83. package/dist/worker/createSharedWorker.d.ts +37 -0
  84. package/dist/worker/createSharedWorker.d.ts.map +1 -0
  85. package/dist/worker/dedicated.worker.d.ts +2 -0
  86. package/dist/worker/dedicated.worker.d.ts.map +1 -0
  87. package/dist/worker/shared.worker.d.ts +2 -0
  88. package/dist/worker/shared.worker.d.ts.map +1 -0
  89. package/docs/README.md +30 -0
  90. package/docs/api/backend.md +36 -0
  91. package/docs/api/create.md +82 -0
  92. package/docs/api/facade.md +52 -0
  93. package/docs/choosing-a-mode.md +41 -0
  94. package/docs/file-descriptors.md +8 -9
  95. package/docs/guides/async.md +36 -0
  96. package/docs/guides/dedicated.md +76 -0
  97. package/docs/guides/hashing.md +34 -0
  98. package/docs/guides/pure.md +46 -0
  99. package/docs/guides/sharedworker.md +57 -0
  100. package/docs/guides/streaming.md +51 -0
  101. package/docs/guides/watching.md +35 -0
  102. package/docs/migration.md +41 -0
  103. package/docs/types.md +20 -6
  104. package/package.json +42 -3
  105. package/src/{OPFSWorker.ts → core/BaseOPFS.ts} +54 -647
  106. package/src/core/OPFSAsync.ts +248 -0
  107. package/src/core/OPFSSync.ts +460 -0
  108. package/src/{OPFSFacade.ts → facade/OPFSFacade.ts} +83 -54
  109. package/src/facade/createOPFSAsync.ts +22 -0
  110. package/src/facade/createOPFSDedicated.ts +23 -0
  111. package/src/facade/createOPFSShared.ts +30 -0
  112. package/src/index.async.ts +11 -0
  113. package/src/index.pure.ts +3 -2
  114. package/src/index.sharedworker.ts +12 -0
  115. package/src/index.sync.ts +12 -0
  116. package/src/index.ts +37 -8
  117. package/src/types.ts +8 -4
  118. package/src/worker/createDedicatedWorker.ts +74 -0
  119. package/src/worker/createSharedWorker.ts +67 -0
  120. package/src/worker/dedicated.worker.ts +13 -0
  121. package/src/worker/shared.worker.ts +17 -0
  122. package/dist/OPFSFacade.d.ts.map +0 -1
  123. package/dist/OPFSWorker.d.ts.map +0 -1
  124. package/dist/assets/worker.entry-DUlEoroc.js.map +0 -1
  125. package/dist/createOPFSWorker.d.ts +0 -17
  126. package/dist/createOPFSWorker.d.ts.map +0 -1
  127. package/dist/helpers-DNj8ZoMu.cjs +0 -4
  128. package/dist/helpers-DNj8ZoMu.cjs.map +0 -1
  129. package/dist/helpers-WY2jfbOT.js +0 -1427
  130. package/dist/helpers-WY2jfbOT.js.map +0 -1
  131. package/dist/worker.entry.d.ts +0 -2
  132. package/dist/worker.entry.d.ts.map +0 -1
  133. package/docs/api-reference.md +0 -815
  134. package/src/createOPFSWorker.ts +0 -57
  135. package/src/worker.entry.ts +0 -6
package/README.md CHANGED
@@ -1,111 +1,23 @@
1
1
  # OPFS Worker
2
2
 
3
- [npm version](https://www.npmjs.com/package/opfs-worker)
3
+ [npm](https://www.npmjs.com/package/opfs-worker)
4
+ [demo](https://kachurun.github.io/opfs-worker/)
4
5
 
5
- A robust TypeScript library for working with Origin Private File System (OPFS) through Web Workers, providing a Node.js-like file system API for browser environments.
6
+ ## What is OPFS?
6
7
 
7
- ## Table of Contents
8
+ OPFS ([Origin Private File System](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system)) is a private file store for your origin. No file picker, no permission prompts. It lives under the usual site quota, and clearing site data wipes it.
8
9
 
9
- - [Features](#features)
10
- - [Installation](#installation)
11
- - [Quick Start](#quick-start)
12
- - [Demo](#demo)
13
- - [API Reference](#api-reference)
14
- - [Types](#types)
15
- - [Browser Support](#browser-support)
16
- - [Development](#development)
17
- - [License](#license)
10
+ This library gives you a real filesystem on top of it — read, write, and watch files with a familiar Node-like API.
18
11
 
19
- ## Features
20
-
21
- ### 🚀 **Performance & Architecture**
22
-
23
- - **Web Worker-based**: Runs in a separate thread, keeping your main thread responsive
24
- - **Zero-copy data transfer**: Efficient binary data handling with Comlink transfer
25
- - **Faster than IndexedDB hacks**: Direct OPFS access without the overhead of database abstractions
26
- - **Efficient file watching**: Real-time change detection with minimatch patterns, no polling delays
27
-
28
- ### 🛠️ **Developer Experience**
29
-
30
- - **Better DX vs File System Access API**: Familiar Node.js-like API instead of complex browser APIs
31
- - **Type-safe**: Full TypeScript support with comprehensive type definitions
32
- - **Comlink-powered**: Seamless RPC communication between main thread and worker
33
-
34
- ### 🌐 **Compatibility & Standards**
35
-
36
- - **Cross-browser compatible**: Works in all modern browsers including Safari, Firefox, Chrome, and Edge
37
- - **OPFS-native**: Built directly on Origin Private File System standards
38
- - **No polyfills needed**: Uses native browser capabilities
39
-
40
- ### 📁 **File System Operations**
41
-
42
- - **Complete API**: `readFile`, `writeFile`, `mkdir`, `remove`, `copy`, `rename`, and more
43
- - **Binary file support**: Handle images, documents, and any binary data seamlessly
44
- - **Hash support**: Cheap `etag` by default (`mtime-size`), plus optional content hashes (SHA-1 / SHA-256 / SHA-384 / SHA-512)
45
- - **File indexing**: Complete file system indexing with metadata and search
46
-
47
- ### 🔄 **Advanced Features**
48
-
49
- - **Sync operations**: Bulk file synchronization from external data sources
50
- - **Event broadcasting**: Real-time file change notifications via BroadcastChannel
51
- - **Include/exclude filters**: Fine-grained control over file watching patterns
52
- - **Comprehensive error handling**: Detailed error types for better debugging
53
-
54
- ## When to Use
55
-
56
- **Use OPFS Worker when you need a persistent file system directly in the browser with an API almost like Node.js `fs`.** It's faster than IndexedDB for file operations and provides better DX than raw File System Access API.
57
-
58
- Here are the key use cases:
59
-
60
- ### 🚀 **Offline-First Applications**
61
-
62
- - **Progressive Web Apps (PWAs)**: Store user data, cached resources, and app state locally
63
- - **Media applications**: Cache images, videos, and audio files for offline playback
64
- - **Data synchronization**: Local-first data with background sync to remote servers
65
-
66
- ### 💾 **Performance & Caching**
67
-
68
- - **Asset caching**: Store and serve static files (CSS, JS, images) from local storage
69
- - **Database caching**: Cache API responses and database queries locally
70
- - **Session persistence**: Maintain user sessions and preferences across browser restarts
71
- - **Resource preloading**: Preload critical resources for instant access
72
-
73
- ### 🎨 **Editor & IDE Features**
74
-
75
- - **Code editors**: File tree navigation, syntax highlighting, and project management
76
- - **Design tools**: Canvas state persistence, project files, and asset management
77
- - **Document editors**: Rich text, markdown, and collaborative editing with local storage
78
- - **Build tools**: Local development servers, build caches, and project configurations
79
-
80
- ### 🔄 **State Management**
81
-
82
- - **Application state**: Persist complex application state and user preferences
83
- - **Form data**: Auto-save forms and restore user input on page refresh
84
- - **Game state**: Save game progress, levels, and user achievements
85
- - **User preferences**: Settings, themes, and personalized configurations
86
-
87
- ### 📱 **Mobile & Cross-Platform**
88
-
89
- - **Mobile web apps**: Native-like file management on mobile devices
90
- - **Cross-tab synchronization**: Share data between multiple browser tabs
91
- - **Worker-based processing**: Offload file operations to background threads
92
- - **Progressive Web Apps**: Full offline capabilities with file system access
93
-
94
- ### ❌ **When NOT to Use**
95
-
96
- - **Server-side storage**: This is a client-side solution, not a replacement for server storage
97
- - **Cross-origin access**: Files are isolated to the specific domain/origin
98
- - **Large datasets**: OPFS has browser-specific storage limitations:
99
- - **Chrome/Edge**: Up to ~50% of free disk space (check via `navigator.storage.estimate()`)
100
- - **Safari**: Usually 1-2 GB, sometimes less
101
- - **Firefox**: Shared limit with IndexedDB, similar to Chromium
102
-
103
- ### ⚡ **Performance Notes**
104
-
105
- - **Faster than IndexedDB**: Direct file system access without database overhead
106
- - **Slower than native FS**: Browser APIs have performance limitations compared to desktop file systems
107
- - **Worker-based**: File operations run in background threads, keeping main thread responsive
108
- - **Memory efficient**: Files are stored in browser's native file system, not in memory
12
+ - **High-level Node-like API.** `readFile`, `writeFile`, `mkdir`, `stat`, `rename`, `copy`, `readDir` — with encodings, auto-detection by extension, and `string | URL` paths. You write `fs` code, not handle plumbing. → [facade API](docs/api/facade.md)
13
+ - **Low-level positional I/O.** `open` / `read` / `write` at an offset, plus `ftruncate` and `fsync` — enough for random-access formats and databases. → [file descriptors](docs/file-descriptors.md)
14
+ - **Sync API by default.** `createOPFS()` runs sync access handles in a dedicated worker: the fastest path OPFS offers, with file descriptors, and it works in every browser that has OPFS — including older Safari. You don't manage `Worker`, Comlink, or handles yourself. → [dedicated](docs/guides/dedicated.md)
15
+ - **Async API for modern browsers / SharedWorkers.** Uses `createWritable()` — runs on the main thread or inside a SharedWorker, no dedicated worker needed. Safari 26+ for writes. → [async](docs/guides/async.md) · [sharedworker](docs/guides/sharedworker.md)
16
+ - **Any way you want to run it.** Inlined dedicated worker, your own worker file, raw classes inside a worker you control, or one SharedWorker for every tab. Same methods in all of them. → [choosing a mode](docs/choosing-a-mode.md)
17
+ - **Memory-friendly for big files.** Streaming support means you can pull in gigabyte-sized files without blowing RAM — `importStream` writes a `ReadableStream`, `Blob`, or `File` chunk by chunk, with optional progress callbacks. → [streaming](docs/guides/streaming.md)
18
+ - **Cross-tab `watch()`.** Changes broadcast over `BroadcastChannel`, so listeners fire in every context that shares the filesystem. [watching](docs/guides/watching.md)
19
+ - **Built-in file hashing.** `stat()` can carry a cheap etag or a real SHA hash, with a size cap so big files don't get hashed by accident — handy for caching and diffing. → [hashing](docs/guides/hashing.md)
20
+ - **Small and typed.** Full TypeScript types, ESM and CJS, two small runtime deps (`comlink`, `minimatch`) — and the async entry pulls in no worker code at all. → [types](docs/types.md)
109
21
 
110
22
  ## Installation
111
23
 
@@ -113,362 +25,73 @@ Here are the key use cases:
113
25
  npm install opfs-worker
114
26
  ```
115
27
 
116
- **Dependencies:**
117
-
118
- - Modes 1 and 2 depend on `comlink` (worker communication is handled for you)
119
- - Mode 3 does not use Comlink — you run the class inside your own worker
120
- - Modes 1 and 2 ship an inlined worker, so your bundler does not need a `?worker` setup
121
-
122
- ## Quick Start
123
-
124
- OPFS sync I/O only works inside a dedicated Web Worker. The library covers three common setups:
125
-
126
- | | Import | Best when |
127
- | ---------- | --------------------------------------- | ----------------------------------------------------------------- |
128
- | **Mode 1** | `createOPFS()` from `opfs-worker` | You want a Node-like `fs` API on the main thread |
129
- | **Mode 2** | `createOPFSWorker()` from `opfs-worker` | You want the low-level worker API, but still from the main thread |
130
- | **Mode 3** | `OPFSWorker` from `opfs-worker/pure` | Your code already runs inside a worker |
131
-
132
- Modes 1 and 2 both pull Comlink. Mode 3 does not.
133
-
134
- ### Mode 1: Node-like facade (recommended)
135
-
136
- The usual path. `createOPFS()` starts an inlined worker and returns a facade with a Node-like API — strings, encodings, auto-detection. Comlink stays under the hood.
137
-
138
- > 1.x alias: `createWorker` still works and returns the same facade — prefer `createOPFS`.
28
+ ## Quick start
139
29
 
140
30
  ```typescript
141
31
  import { createOPFS } from 'opfs-worker';
142
32
 
143
- async function basicExample() {
144
- const fs = createOPFS();
145
-
146
- await fs.writeFile('/config.json', JSON.stringify({ theme: 'dark' }));
147
- const config = await fs.readFile('/config.json');
148
- console.log(JSON.parse(config as string));
149
- }
150
-
151
- async function extendedExample() {
152
- const broadcastChannel = new BroadcastChannel('my-app-events');
153
-
154
- const fs = createOPFS({
155
- root: '/my-app',
156
- namespace: 'my-app:fs',
157
- hashAlgorithm: 'SHA-256',
158
- maxFileSize: 100 * 1024 * 1024, // 100MB
159
- broadcastChannel
160
- });
161
-
162
- const imageData = new Uint8Array([58 /* binary data */]);
163
- await fs.writeFile('/image.png', imageData);
164
- const binaryData = await fs.readFile('/image.png', 'binary');
165
-
166
- await fs.writeFile('/config.txt', 'Hello, World!');
167
- const textContent = await fs.readFile('/config.txt'); // string
168
-
169
- await fs.watch('/', {
170
- recursive: true,
171
- include: ['*.json'],
172
- exclude: ['dist/**']
173
- });
174
-
175
- broadcastChannel.onmessage = (event) => {
176
- console.log('File changed:', event.data);
177
- };
178
-
179
- const stats = await fs.stat('/image.png');
180
- console.log(`File size: ${stats.size} bytes`, `Hash: ${stats.hash}`);
181
- }
182
- ```
183
-
184
- > Watch events go over `BroadcastChannel` (default name `opfs-worker`). Pass `broadcastChannel` to customize. Only watched paths emit events.
185
-
186
- ### Mode 2: Raw worker API from the main thread
187
-
188
- Same package entry, same inlined worker but without the facade. You get a Comlink proxy to `OPFSWorker`: bytes in, bytes out. Useful if you want to wrap it yourself or prefer the lower-level API.
189
-
190
- ```typescript
191
- import { createOPFSWorker } from 'opfs-worker';
192
-
193
- const { fs, dispose } = createOPFSWorker({
194
- root: '/my-app',
195
- namespace: 'my-app:fs',
196
- hashAlgorithm: 'SHA-256',
197
- maxFileSize: 100 * 1024 * 1024,
198
- broadcastChannel: 'my-app-events',
199
- });
200
-
201
- await fs.writeFile('/config.json', new TextEncoder().encode(JSON.stringify({ theme: 'dark' })));
202
- const bytes = await fs.readFile('/config.json');
203
- console.log(JSON.parse(new TextDecoder().decode(bytes)));
204
-
205
- dispose(); // worker.dispose() + terminate()
206
- ```
207
-
208
- > Want your own facade? Our `OPFSFacade` is built on `createOPFSWorker` — copy [`src/OPFSFacade.ts`](src/OPFSFacade.ts), swap the imports to `opfs-worker` and change what you need.
209
-
210
- ### Mode 3: `OPFSWorker` inside your own worker
211
-
212
- If you already have a worker (or want full control over it), import the class and use it directly. No nested worker, no Comlink. Bundling that worker and talking to the main thread — if you need that at all — is up to you.
213
-
214
- ```typescript
215
- // my-app.worker.ts (this file IS the worker)
216
- import { OPFSWorker } from 'opfs-worker/pure';
217
-
218
- const fs = new OPFSWorker({
219
- root: '/my-app',
220
- namespace: 'my-app:fs',
221
- hashAlgorithm: 'SHA-256',
222
- broadcastChannel: 'my-app-events', // name only — BroadcastChannel instances can't cross the wire
33
+ const fs = createOPFS({
34
+ root: '/my-app',
35
+ hashAlgorithm: 'SHA-256'
223
36
  });
224
37
 
225
- await fs.writeFile('/config.json', new TextEncoder().encode(JSON.stringify({ theme: 'dark' })));
226
- const bytes = await fs.readFile('/config.json');
227
- console.log(JSON.parse(new TextDecoder().decode(bytes)));
228
- ```
229
-
230
- `opfs-worker/pure` does not call Comlink `expose()`, so it will not take over your worker's message port.
231
-
232
- ### Hash Algorithm Configuration
233
-
234
- Set once via options / `setOptions()` — used by `stat()`, `index()`, and watch events.
235
-
236
- Default is `'etag'`: a cheap weak tag from `File.lastModified` + size (no content read). Switch to a SHA algorithm when you need a real content hash.
237
-
238
- ```typescript
239
- import { createOPFS } from 'opfs-worker';
240
-
241
- async function hashExample() {
242
- // Default: etag (fast, no content read)
243
- const fs = createOPFS();
244
- await fs.writeFile('/data.txt', 'Hello World');
245
-
246
- const etagStat = await fs.stat('/data.txt');
247
- console.log(`ETag: ${etagStat.hash}`); // e.g. "m1abc-c" (mtime36-size36)
248
-
249
- // Opt into a cryptographic content hash
250
- await fs.setOptions({ hashAlgorithm: 'SHA-256' });
251
- const shaStat = await fs.stat('/data.txt');
252
- console.log(`SHA-256: ${shaStat.hash}`);
253
-
254
- // Index / watch events also include hash when enabled
255
- const index = await fs.index();
256
- for (const [path, stat] of index) {
257
- if (stat.isFile && stat.hash) {
258
- console.log(`${path}: ${stat.hash}`);
259
- }
260
- }
261
-
262
- const channel = new BroadcastChannel('opfs-worker');
263
- channel.onmessage = (event) => {
264
- if (event.data.hash) {
265
- console.log(`File ${event.data.path} changed, hash: ${event.data.hash}`);
266
- }
267
- };
268
- }
269
-
270
- // maxFileSize only applies to SHA-* (content) hashing — etag ignores it
271
- async function maxFileSizeExample() {
272
- const fs = createOPFS({
273
- hashAlgorithm: 'SHA-256',
274
- maxFileSize: 100 * 1024 * 1024 // 100MB (default 50MB)
275
- });
276
-
277
- const stats = await fs.stat('/large-file.dat');
278
- if (stats.hash) {
279
- console.log(`Hash: ${stats.hash}`);
280
- } else {
281
- console.log('File too large for content hashing');
282
- }
283
- }
284
- ```
285
-
286
- **Supported Hash Algorithms:**
287
-
288
- - `'etag'` — **default**. Weak tag from mtime + size; free, no content read
289
- - `'SHA-1'` / `'SHA-256'` / `'SHA-384'` / `'SHA-512'` — content hashes via Web Crypto (bounded by `maxFileSize`)
290
- - `null` / `false` — disable hashing
291
-
292
- **Note:** Prefer `'etag'` for watch / change-detection. Use SHA only when you need integrity of file bytes. Set to `null`/`false` if you don't need `hash` at all.
293
-
294
- ### Root Path Configuration
295
-
296
- The file system supports configuring the root path through options. The root path determines where in OPFS the file system's root will be created. All file paths passed to the API are relative to this root path.
297
-
298
- ```typescript
299
- import { createOPFS } from 'opfs-worker';
300
-
301
- async function rootPathExample() {
302
- // Use default root path '/'
303
- const fsDefault = createOPFS();
304
- await fsDefault.writeFile('/config.json', '{}');
305
- // This creates /config.json in OPFS root
38
+ await fs.mkdir('/project');
39
+ await fs.writeFile('/project/hello.txt', 'Hello, OPFS!');
40
+ await fs.rename('/project/hello.txt', '/project/readme.txt');
306
41
 
307
- // Use custom root path
308
- const fsCustom = createOPFS({ root: '/my-app' });
309
- await fsCustom.writeFile('/config.json', '{}');
310
- // This creates /my-app/config.json in OPFS root
311
-
312
- // Change root path dynamically
313
- await fsCustom.setOptions({ root: '/new-app' });
314
- await fsCustom.writeFile('/config.json', '{}');
315
- // This creates /new-app/config.json in OPFS root
316
- }
42
+ const files = await fs.readDir('/project');
43
+ const text = await fs.readFile('/project/readme.txt'); // 'Hello, OPFS!'
317
44
  ```
318
45
 
319
- **Root Path Behavior:**
320
-
321
- - **Default**: Uses `/` (OPFS root directory)
322
- - **Custom**: Creates a subdirectory within OPFS for isolation
323
- - **Dynamic**: Can be changed at runtime via `setOptions()`
324
- - **Auto-mount**: Automatically mounts to the specified root when needed
325
- - **Path Resolution**: All API paths are relative to the configured root
326
-
327
- ## Demo
328
-
329
- Check out the live demo powered by Vite and hosted on GitHub Pages.
330
-
331
- [Live Demo](https://kachurun.github.io/opfs-worker/)
332
-
333
- ## API Reference
334
-
335
- The complete API reference is available in the [docs/api-reference.md](docs/api-reference.md) file.
336
-
337
- **Additional Documentation:**
338
-
339
- - [File Descriptors Guide](docs/file-descriptors.md) - Comprehensive guide to low-level file I/O operations
340
- - [Types Reference](docs/types.md) - Complete TypeScript type definitions
341
-
342
- ### Quick API Overview
343
-
344
- **Entry Points:**
345
-
346
- - `createOPFS(options?)` — Mode 1: Node-like facade on the main thread
347
- - `createOPFSWorker(options?)` — Mode 2: raw `OPFSWorker` proxy on the main thread
348
- - `OPFSWorker` from `opfs-worker/pure` — Mode 3: use the class inside your own worker
349
- - `OPFSFacade` — Mode 1 class returned by `createOPFS`
350
-
351
- **Core File Operations:**
352
-
353
- - `readFile(path, encoding?)` - Read files as text or binary with auto-detection
354
- - `writeFile(path, data, encoding?)` - Write text or binary data with auto-detection
355
- - `readText(path, encoding?)` - Read files as text with specified encoding
356
- - `writeText(path, text, encoding?)` - Write text with specified encoding
357
- - `appendText(path, text, encoding?)` - Append text with specified encoding
358
-
359
- **Common Operations:**
360
-
361
- - `mkdir(path, options?)` - Create directories
362
- - `readDir(path)` - List directory contents
363
- - `stat(path)` - Get file/directory statistics
364
- - `remove(path, options?)` - Remove files/directories
365
- - `copy(source, destination, options?)` - Copy files/directories
366
- - `rename(oldPath, newPath)` - Rename files/directories
367
-
368
- **File Descriptors (Low-level I/O):**
369
-
370
- - `open(path, options?)` - Open file and return descriptor
371
- - `read(fd, buffer, offset, length, position?)` - Read from descriptor (returns `{bytesRead, buffer}`)
372
- - `write(fd, buffer, offset?, length?, position?)` - Write to descriptor
373
- - `fstat(fd)` - Get stats by descriptor
374
- - `ftruncate(fd, size?)` - Truncate file by descriptor
375
- - `fsync(fd)` - Sync file data to storage
376
- - `close(fd)` - Close file descriptor
377
-
378
- **Note**: The `read()` method uses `Comlink.transfer()` for efficient buffer handling. **From the main window**, you must transfer buffer ownership to the worker, and **from the worker**, the buffer is transferred back to you. See [File Descriptors Guide](docs/file-descriptors.md) for complete usage examples.
379
-
380
- _For detailed file descriptor documentation, see [File Descriptors Guide](docs/file-descriptors.md)_
381
-
382
- **Advanced Features:**
383
-
384
- - `watch(path, options?)` - Watch for file changes with minimatch patterns
385
- - `index()` - Get complete file system index
386
- - `sync(entries, options?)` - Bulk synchronization
387
- - `setOptions(options)` - Update configuration
388
-
389
- **Binary File Support:**
390
-
391
- - Full support for images, documents, and any binary data
392
- - Automatic conversion between Uint8Array, ArrayBuffer, and Blob
393
- - Multiple text encodings (UTF-8, UTF-16, ASCII, Base64, etc.)
394
-
395
- **Utility Functions:**
396
-
397
- - Path manipulation (`basename`, `dirname`, `normalizePath`, etc.)
398
- - Data conversion helpers
399
- - OPFS support detection
400
-
401
- For detailed API documentation with examples, see [docs/api-reference.md](docs/api-reference.md).
402
-
403
- ## Types
404
-
405
- Full TypeScript types are provided — see [docs/types.md](docs/types.md) for complete type definitions including `FileStat`, `DirentData`, `WatchOptions`, `OPFSOptions`, and more.
406
-
407
- ## Browser Support
408
-
409
- This library works in **all modern browsers**, including Safari, Firefox, Chrome, and Edge.
410
-
411
- **Requirements:**
412
-
413
- - Web Workers support (available in all modern browsers)
414
- - File System Access API (for OPFS functionality)
415
-
416
- **Browser Compatibility:**
417
-
418
- - ✅ Chrome 86+
419
- - ✅ Edge 86+
420
- - ✅ Firefox 111+
421
- - ✅ Safari 15.2+
422
- - ✅ Opera 72+
46
+ ## Choose a mode
47
+
48
+ | What you want | Use |
49
+ | ---------------------------------------------------------------- | ---------------------------------------------------- |
50
+ | Node-like `fs` via dedicated worker (default, works everywhere) | `createOPFS()` |
51
+ | Node-like `fs` on the main thread (modern browsers / Safari 26+) | `createOPFSAsync()` from `opfs-worker/async` |
52
+ | Node-like `fs` via SharedWorker (modern browsers / Safari 26+) | `createOPFSShared()` from `opfs-worker/sharedworker` |
53
+ | OPFS inside a worker you already run (classes only) | `OPFSSync` / `OPFSAsync` from `opfs-worker/pure` |
54
+
55
+ Trade-offs (FDs, Safari, size, CSP): [Choosing a mode](docs/choosing-a-mode.md).
56
+
57
+ ## Docs
58
+
59
+ - [Docs index](docs/README.md)
60
+ - [Choosing a mode](docs/choosing-a-mode.md)
61
+ - Guides
62
+ - [Dedicated worker](docs/guides/dedicated.md)
63
+ - [Async](docs/guides/async.md)
64
+ - [SharedWorker](docs/guides/sharedworker.md)
65
+ - [Pure classes](docs/guides/pure.md)
66
+ - [Streaming](docs/guides/streaming.md)
67
+ - [Watching](docs/guides/watching.md)
68
+ - [Hashing](docs/guides/hashing.md)
69
+ - API
70
+ - [Create helpers & options](docs/api/create.md)
71
+ - [Facade](docs/api/facade.md)
72
+ - [Backend](docs/api/backend.md)
73
+ - [File descriptors](docs/file-descriptors.md)
74
+ - [Types](docs/types.md)
75
+ - [Migration from 1.x](docs/migration.md)
423
76
 
424
77
  ## Development
425
78
 
426
- ### Building
427
-
428
79
  ```bash
429
- npm run build
80
+ bun install
81
+ bun run build
82
+ bun run test
83
+ bun run lint
84
+ bun run type-check
430
85
  ```
431
86
 
432
- ### Development Server
433
-
434
- ```bash
435
- npm run dev
436
- ```
437
-
438
- ### Testing
439
-
440
- ```bash
441
- npm test
442
- npm run test:coverage
443
- ```
444
-
445
- ### Linting
446
-
447
- ```bash
448
- npm run lint
449
- ```
87
+ Demo app: `bun run dev:demo` / `bun run build:demo` / `bun run preview`.
450
88
 
451
89
  ## License
452
90
 
453
91
  MIT
454
92
 
455
- ## Contributing
456
-
457
- Contributions are welcome!
458
-
459
- **How to contribute:**
460
-
461
- - 🐛 **Report bugs** or suggest features via [GitHub Issues](https://github.com/kachurun/opfs-worker/issues)
462
- - 💡 **Submit ideas** for improvements or new features
463
- - 🔧 **Send PRs** for bug fixes, documentation, or enhancements
464
- - 📚 **Improve docs** - help make the library more accessible
465
-
466
- **Getting started:**
93
+ ## 👤 Maintainer
467
94
 
468
- 1. Fork the repository
469
- 2. Create a feature branch
470
- 3. Make your changes
471
- 4. Add tests if applicable
472
- 5. Submit a Pull Request
95
+ <img src="https://github.com/kachurun.png" width="100" height="100" alt="@kachurun's avatar">
473
96
 
474
- _We welcome all contributions, big and small!_
97
+ Maintained with ❤️ by [@kachurun](https://github.com/kachurun)
@@ -0,0 +1,4 @@
1
+ "use strict";var N=(s=>(s.Added="added",s.Changed="changed",s.Removed="removed",s))(N||{});const Pt={ENOENT:-2,EISDIR:-21,ENOTDIR:-20,EACCES:-13,EEXIST:-17,ENOTEMPTY:-39,EINVAL:-22,EIO:-5,ENOSPC:-28,EBUSY:-16,EINTR:-4,ENOTSUP:-95,ERANGE:-34,EBADF:-9,EROOT:-1};class m extends Error{errno;syscall;path;constructor(t,e,n,r,i){super(t,{cause:i}),this.name=e,this.errno=Pt[e]||-1,this.path=n,this.syscall=r}}class ot extends m{constructor(t){super("OPFS is not supported in this browser","ENOTSUP",void 0,void 0,t)}}class at extends m{constructor(t,e,n){super(t,"INVALID_PATH",e,"access",n)}}class M extends m{constructor(t,e,n){const r={file:`File not found: ${e}`,directory:`Directory not found: ${e}`,source:`Source does not exist: ${e}`};super(r[t],"ENOENT",e,"access",n)}}class ct extends m{constructor(t,e,n){super(`Permission denied for ${e} on: ${t}`,"EACCES",t,e,n)}}class lt extends m{constructor(t,e,n){super(t,"ENOSPC",e,"write",n)}}class ht extends m{constructor(t,e){super(`File is busy: ${t}`,"EBUSY",t,"open",e)}}class U extends m{constructor(t,e,n){const r=t==="directory"?`Is a directory: ${e}`:`Not a directory: ${e}`,i=t==="directory"?"EISDIR":"ENOTDIR";super(r,i,e,"access",n)}}class D extends m{constructor(t,e,n,r){const i={argument:"EINVAL",format:"INVALID_FORMAT",descriptor:"EBADF",overflow:"ERANGE"};super(e,i[t],n,"validate",r)}}class ut extends m{constructor(t,e){super(`Operation aborted: ${t}`,"EINTR",t,"interrupt",e)}}class Y extends m{constructor(t,e,n){super(t,"EIO",e,"io",n)}}class Z extends m{constructor(t,e){super(`Operation not supported: ${t}`,"ENOTSUP",t,"operation",e)}}class H extends m{constructor(t,e,n){const r={RM_FAILED:`Failed to remove entry: ${e}`,ENOTEMPTY:`Directory not empty: ${e}. Use recursive option to force removal.`,EROOT:"Cannot remove root directory"};super(r[t]||`Directory operation failed: ${e}`,t,e,"unlink",n)}}class ft extends m{constructor(t,e){super("Failed to initialize OPFS","INIT_FAILED",t,"init",e)}}class B extends m{constructor(t,e,n){super(`Failed to ${t}: ${e}`,`${t.toUpperCase()}_FAILED`,e,t,n)}}class X extends m{constructor(t,e){super(`Destination already exists: ${t}`,"EEXIST",t,"open",e)}}function It(s,t,e,n){const r=`${s.toUpperCase()}_FAILED`;return new m(`Failed to ${s} file descriptor: ${t}`,r,e,s,n)}function b(s,t){const e=t?.path,n=t?.isDirectory,r=t?.existenceType??"file",i=t?.operation;switch(s.name){case"InvalidStateError":return new ht(e||"unknown",s);case"QuotaExceededError":return new lt(`No space left on device: ${e||"unknown"}`,e,s);case"NotFoundError":return new M(r,e,s);case"TypeMismatchError":return n!==void 0?n?new U("directory",e||"unknown",s):new U("file",e||"unknown",s):new D("argument",`Type mismatch: ${e||"unknown"}`,e,s);case"NotAllowedError":case"SecurityError":return new ct(e,"unknown",s);case"InvalidModificationError":return i==="remove"?new H("ENOTEMPTY",e,s):new D("argument",`Invalid modification: ${e||"unknown"}`,e,s);case"AbortError":return new ut(e||"unknown",s);case"OperationError":return new Y(`Operation failed: ${e||"unknown"}`,e,s);case"TypeError":return new Z(e||"unknown",s);default:return i==="remove"?new H("RM_FAILED",e,s):new Y(`I/O error: ${e||"unknown"}`,e,s)}}const dt=(s,t,e)=>{const n=s instanceof RegExp?K(s,e):s,r=t instanceof RegExp?K(t,e):t,i=n!==null&&r!=null&&Mt(n,r,e);return i&&{start:i[0],end:i[1],pre:e.slice(0,i[0]),body:e.slice(i[0]+n.length,i[1]),post:e.slice(i[1]+r.length)}},K=(s,t)=>{const e=t.match(s);return e?e[0]:null},Mt=(s,t,e)=>{let n,r,i,o,a,c=e.indexOf(s),h=e.indexOf(t,c+1),l=c;if(c>=0&&h>0){if(s===t)return[c,h];for(n=[],i=e.length;l>=0&&!a;){if(l===c)n.push(l),c=e.indexOf(s,l+1);else if(n.length===1){const d=n.pop();d!==void 0&&(a=[d,h])}else r=n.pop(),r!==void 0&&r<i&&(i=r,o=h),h=e.indexOf(t,l+1);l=c<h&&c>=0?c:h}n.length&&o!==void 0&&(a=[i,o])}return a},pt="\0SLASH"+Math.random()+"\0",gt="\0OPEN"+Math.random()+"\0",J="\0CLOSE"+Math.random()+"\0",mt="\0COMMA"+Math.random()+"\0",wt="\0PERIOD"+Math.random()+"\0",Rt=new RegExp(pt,"g"),kt=new RegExp(gt,"g"),Lt=new RegExp(J,"g"),zt=new RegExp(mt,"g"),Bt=new RegExp(wt,"g"),jt=/\\\\/g,Wt=/\\{/g,Ut=/\\}/g,Ht=/\\,/g,_t=/\\./g;function V(s){return isNaN(s)?s.charCodeAt(0):parseInt(s,10)}function qt(s){return s.replace(jt,pt).replace(Wt,gt).replace(Ut,J).replace(Ht,mt).replace(_t,wt)}function Gt(s){return s.replace(Rt,"\\").replace(kt,"{").replace(Lt,"}").replace(zt,",").replace(Bt,".")}function yt(s){if(!s)return[""];const t=[],e=dt("{","}",s);if(!e)return s.split(",");const{pre:n,body:r,post:i}=e,o=n.split(",");o[o.length-1]+="{"+r+"}";const a=yt(i);return i.length&&(o[o.length-1]+=a.shift(),o.push.apply(o,a)),t.push.apply(t,o),t}function Vt(s){return s?(s.slice(0,2)==="{}"&&(s="\\{\\}"+s.slice(2)),P(qt(s),!0).map(Gt)):[]}function Yt(s){return"{"+s+"}"}function Xt(s){return/^-?0\d/.test(s)}function Zt(s,t){return s<=t}function Jt(s,t){return s>=t}function P(s,t){const e=[],n=dt("{","}",s);if(!n)return[s];const r=n.pre,i=n.post.length?P(n.post,!1):[""];if(/\$$/.test(n.pre))for(let o=0;o<i.length;o++){const a=r+"{"+n.body+"}"+i[o];e.push(a)}else{const o=/^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(n.body),a=/^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(n.body),c=o||a,h=n.body.indexOf(",")>=0;if(!c&&!h)return n.post.match(/,(?!,).*\}/)?(s=n.pre+"{"+n.body+J+n.post,P(s)):[s];let l;if(c)l=n.body.split(/\.\./);else if(l=yt(n.body),l.length===1&&l[0]!==void 0&&(l=P(l[0],!1).map(Yt),l.length===1))return i.map(u=>n.pre+l[0]+u);let d;if(c&&l[0]!==void 0&&l[1]!==void 0){const u=V(l[0]),f=V(l[1]),g=Math.max(l[0].length,l[1].length);let p=l.length===3&&l[2]!==void 0?Math.abs(V(l[2])):1,S=Zt;f<u&&(p*=-1,S=Jt);const C=l.some(Xt);d=[];for(let x=u;S(x,f);x+=p){let w;if(a)w=String.fromCharCode(x),w==="\\"&&(w="");else if(w=String(x),C){const T=g-w.length;if(T>0){const L=new Array(T+1).join("0");x<0?w="-"+L+w.slice(1):w=L+w}}d.push(w)}}else{d=[];for(let u=0;u<l.length;u++)d.push.apply(d,P(l[u],!1))}for(let u=0;u<d.length;u++)for(let f=0;f<i.length;f++){const g=r+d[u]+i[f];(!t||c||g)&&e.push(g)}}return e}const Qt=1024*64,_=s=>{if(typeof s!="string")throw new TypeError("invalid pattern");if(s.length>Qt)throw new TypeError("pattern is too long")},Kt={"[:alnum:]":["\\p{L}\\p{Nl}\\p{Nd}",!0],"[:alpha:]":["\\p{L}\\p{Nl}",!0],"[:ascii:]":["\\x00-\\x7f",!1],"[:blank:]":["\\p{Zs}\\t",!0],"[:cntrl:]":["\\p{Cc}",!0],"[:digit:]":["\\p{Nd}",!0],"[:graph:]":["\\p{Z}\\p{C}",!0,!0],"[:lower:]":["\\p{Ll}",!0],"[:print:]":["\\p{C}",!0],"[:punct:]":["\\p{P}",!0],"[:space:]":["\\p{Z}\\t\\r\\n\\v\\f",!0],"[:upper:]":["\\p{Lu}",!0],"[:word:]":["\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}",!0],"[:xdigit:]":["A-Fa-f0-9",!1]},F=s=>s.replace(/[[\]\\-]/g,"\\$&"),te=s=>s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),tt=s=>s.join(""),ee=(s,t)=>{const e=t;if(s.charAt(e)!=="[")throw new Error("not in a brace expression");const n=[],r=[];let i=e+1,o=!1,a=!1,c=!1,h=!1,l=e,d="";t:for(;i<s.length;){const p=s.charAt(i);if((p==="!"||p==="^")&&i===e+1){h=!0,i++;continue}if(p==="]"&&o&&!c){l=i+1;break}if(o=!0,p==="\\"&&!c){c=!0,i++;continue}if(p==="["&&!c){for(const[S,[A,C,x]]of Object.entries(Kt))if(s.startsWith(S,i)){if(d)return["$.",!1,s.length-e,!0];i+=S.length,x?r.push(A):n.push(A),a=a||C;continue t}}if(c=!1,d){p>d?n.push(F(d)+"-"+F(p)):p===d&&n.push(F(p)),d="",i++;continue}if(s.startsWith("-]",i+1)){n.push(F(p+"-")),i+=2;continue}if(s.startsWith("-",i+1)){d=p,i+=2;continue}n.push(F(p)),i++}if(l<i)return["",!1,0,!1];if(!n.length&&!r.length)return["$.",!1,s.length-e,!0];if(r.length===0&&n.length===1&&/^\\?.$/.test(n[0])&&!h){const p=n[0].length===2?n[0].slice(-1):n[0];return[te(p),!1,l-e,!1]}const u="["+(h?"^":"")+tt(n)+"]",f="["+(h?"":"^")+tt(r)+"]";return[n.length&&r.length?"("+u+"|"+f+")":n.length?u:f,a,l-e,!0]},I=(s,{windowsPathsNoEscape:t=!1}={})=>t?s.replace(/\[([^\/\\])\]/g,"$1"):s.replace(/((?!\\).|^)\[([^\/\\])\]/g,"$1$2").replace(/\\([^\/])/g,"$1"),se=new Set(["!","?","+","*","@"]),et=s=>se.has(s),ne="(?!(?:^|/)\\.\\.?(?:$|/))",z="(?!\\.)",re=new Set(["[","."]),ie=new Set(["..","."]),oe=new Set("().*{}+?[]^$\\!"),ae=s=>s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),Q="[^/]",st=Q+"*?",nt=Q+"+?";class E{type;#s;#n;#i=!1;#t=[];#e;#o;#c;#a=!1;#r;#l;#u=!1;constructor(t,e,n={}){this.type=t,t&&(this.#n=!0),this.#e=e,this.#s=this.#e?this.#e.#s:this,this.#r=this.#s===this?n:this.#s.#r,this.#c=this.#s===this?[]:this.#s.#c,t==="!"&&!this.#s.#a&&this.#c.push(this),this.#o=this.#e?this.#e.#t.length:0}get hasMagic(){if(this.#n!==void 0)return this.#n;for(const t of this.#t)if(typeof t!="string"&&(t.type||t.hasMagic))return this.#n=!0;return this.#n}toString(){return this.#l!==void 0?this.#l:this.type?this.#l=this.type+"("+this.#t.map(t=>String(t)).join("|")+")":this.#l=this.#t.map(t=>String(t)).join("")}#d(){if(this!==this.#s)throw new Error("should only call on root");if(this.#a)return this;this.toString(),this.#a=!0;let t;for(;t=this.#c.pop();){if(t.type!=="!")continue;let e=t,n=e.#e;for(;n;){for(let r=e.#o+1;!n.type&&r<n.#t.length;r++)for(const i of t.#t){if(typeof i=="string")throw new Error("string part in extglob AST??");i.copyIn(n.#t[r])}e=n,n=e.#e}}return this}push(...t){for(const e of t)if(e!==""){if(typeof e!="string"&&!(e instanceof E&&e.#e===this))throw new Error("invalid part: "+e);this.#t.push(e)}}toJSON(){const t=this.type===null?this.#t.slice().map(e=>typeof e=="string"?e:e.toJSON()):[this.type,...this.#t.map(e=>e.toJSON())];return this.isStart()&&!this.type&&t.unshift([]),this.isEnd()&&(this===this.#s||this.#s.#a&&this.#e?.type==="!")&&t.push({}),t}isStart(){if(this.#s===this)return!0;if(!this.#e?.isStart())return!1;if(this.#o===0)return!0;const t=this.#e;for(let e=0;e<this.#o;e++){const n=t.#t[e];if(!(n instanceof E&&n.type==="!"))return!1}return!0}isEnd(){if(this.#s===this||this.#e?.type==="!")return!0;if(!this.#e?.isEnd())return!1;if(!this.type)return this.#e?.isEnd();const t=this.#e?this.#e.#t.length:0;return this.#o===t-1}copyIn(t){typeof t=="string"?this.push(t):this.push(t.clone(this))}clone(t){const e=new E(this.type,t);for(const n of this.#t)e.copyIn(n);return e}static#h(t,e,n,r){let i=!1,o=!1,a=-1,c=!1;if(e.type===null){let f=n,g="";for(;f<t.length;){const p=t.charAt(f++);if(i||p==="\\"){i=!i,g+=p;continue}if(o){f===a+1?(p==="^"||p==="!")&&(c=!0):p==="]"&&!(f===a+2&&c)&&(o=!1),g+=p;continue}else if(p==="["){o=!0,a=f,c=!1,g+=p;continue}if(!r.noext&&et(p)&&t.charAt(f)==="("){e.push(g),g="";const S=new E(p,e);f=E.#h(t,S,f,r),e.push(S);continue}g+=p}return e.push(g),f}let h=n+1,l=new E(null,e);const d=[];let u="";for(;h<t.length;){const f=t.charAt(h++);if(i||f==="\\"){i=!i,u+=f;continue}if(o){h===a+1?(f==="^"||f==="!")&&(c=!0):f==="]"&&!(h===a+2&&c)&&(o=!1),u+=f;continue}else if(f==="["){o=!0,a=h,c=!1,u+=f;continue}if(et(f)&&t.charAt(h)==="("){l.push(u),u="";const g=new E(f,l);l.push(g),h=E.#h(t,g,h,r);continue}if(f==="|"){l.push(u),u="",d.push(l),l=new E(null,e);continue}if(f===")")return u===""&&e.#t.length===0&&(e.#u=!0),l.push(u),u="",e.push(...d,l),h;u+=f}return e.type=null,e.#n=void 0,e.#t=[t.substring(n-1)],h}static fromGlob(t,e={}){const n=new E(null,void 0,e);return E.#h(t,n,0,e),n}toMMPattern(){if(this!==this.#s)return this.#s.toMMPattern();const t=this.toString(),[e,n,r,i]=this.toRegExpSource();if(!(r||this.#n||this.#r.nocase&&!this.#r.nocaseMagicOnly&&t.toUpperCase()!==t.toLowerCase()))return n;const a=(this.#r.nocase?"i":"")+(i?"u":"");return Object.assign(new RegExp(`^${e}$`,a),{_src:e,_glob:t})}get options(){return this.#r}toRegExpSource(t){const e=t??!!this.#r.dot;if(this.#s===this&&this.#d(),!this.type){const c=this.isStart()&&this.isEnd(),h=this.#t.map(f=>{const[g,p,S,A]=typeof f=="string"?E.#p(f,this.#n,c):f.toRegExpSource(t);return this.#n=this.#n||S,this.#i=this.#i||A,g}).join("");let l="";if(this.isStart()&&typeof this.#t[0]=="string"&&!(this.#t.length===1&&ie.has(this.#t[0]))){const g=re,p=e&&g.has(h.charAt(0))||h.startsWith("\\.")&&g.has(h.charAt(2))||h.startsWith("\\.\\.")&&g.has(h.charAt(4)),S=!e&&!t&&g.has(h.charAt(0));l=p?ne:S?z:""}let d="";return this.isEnd()&&this.#s.#a&&this.#e?.type==="!"&&(d="(?:$|\\/)"),[l+h+d,I(h),this.#n=!!this.#n,this.#i]}const n=this.type==="*"||this.type==="+",r=this.type==="!"?"(?:(?!(?:":"(?:";let i=this.#f(e);if(this.isStart()&&this.isEnd()&&!i&&this.type!=="!"){const c=this.toString();return this.#t=[c],this.type=null,this.#n=void 0,[c,I(this.toString()),!1,!1]}let o=!n||t||e||!z?"":this.#f(!0);o===i&&(o=""),o&&(i=`(?:${i})(?:${o})*?`);let a="";if(this.type==="!"&&this.#u)a=(this.isStart()&&!e?z:"")+nt;else{const c=this.type==="!"?"))"+(this.isStart()&&!e&&!t?z:"")+st+")":this.type==="@"?")":this.type==="?"?")?":this.type==="+"&&o?")":this.type==="*"&&o?")?":`)${this.type}`;a=r+i+c}return[a,I(i),this.#n=!!this.#n,this.#i]}#f(t){return this.#t.map(e=>{if(typeof e=="string")throw new Error("string type in extglob ast??");const[n,r,i,o]=e.toRegExpSource(t);return this.#i=this.#i||o,n}).filter(e=>!(this.isStart()&&this.isEnd())||!!e).join("|")}static#p(t,e,n=!1){let r=!1,i="",o=!1;for(let a=0;a<t.length;a++){const c=t.charAt(a);if(r){r=!1,i+=(oe.has(c)?"\\":"")+c;continue}if(c==="\\"){a===t.length-1?i+="\\\\":r=!0;continue}if(c==="["){const[h,l,d,u]=ee(t,a);if(d){i+=h,o=o||l,a+=d-1,e=e||u;continue}}if(c==="*"){n&&t==="*"?i+=nt:i+=st,e=!0;continue}if(c==="?"){i+=Q,e=!0;continue}i+=ae(c)}return[i,I(t),!!e,o]}}const ce=(s,{windowsPathsNoEscape:t=!1}={})=>t?s.replace(/[?*()[\]]/g,"[$&]"):s.replace(/[?*()[\]\\]/g,"\\$&"),y=(s,t,e={})=>(_(t),!e.nocomment&&t.charAt(0)==="#"?!1:new q(t,e).match(s)),le=/^\*+([^+@!?\*\[\(]*)$/,he=s=>t=>!t.startsWith(".")&&t.endsWith(s),ue=s=>t=>t.endsWith(s),fe=s=>(s=s.toLowerCase(),t=>!t.startsWith(".")&&t.toLowerCase().endsWith(s)),de=s=>(s=s.toLowerCase(),t=>t.toLowerCase().endsWith(s)),pe=/^\*+\.\*+$/,ge=s=>!s.startsWith(".")&&s.includes("."),me=s=>s!=="."&&s!==".."&&s.includes("."),we=/^\.\*+$/,ye=s=>s!=="."&&s!==".."&&s.startsWith("."),Ee=/^\*+$/,Se=s=>s.length!==0&&!s.startsWith("."),xe=s=>s.length!==0&&s!=="."&&s!=="..",ve=/^\?+([^+@!?\*\[\(]*)?$/,Oe=([s,t=""])=>{const e=Et([s]);return t?(t=t.toLowerCase(),n=>e(n)&&n.toLowerCase().endsWith(t)):e},be=([s,t=""])=>{const e=St([s]);return t?(t=t.toLowerCase(),n=>e(n)&&n.toLowerCase().endsWith(t)):e},Ae=([s,t=""])=>{const e=St([s]);return t?n=>e(n)&&n.endsWith(t):e},De=([s,t=""])=>{const e=Et([s]);return t?n=>e(n)&&n.endsWith(t):e},Et=([s])=>{const t=s.length;return e=>e.length===t&&!e.startsWith(".")},St=([s])=>{const t=s.length;return e=>e.length===t&&e!=="."&&e!==".."},xt=typeof process=="object"&&process?typeof process.env=="object"&&process.env&&process.env.__MINIMATCH_TESTING_PLATFORM__||process.platform:"posix",rt={win32:{sep:"\\"},posix:{sep:"/"}},Ne=xt==="win32"?rt.win32.sep:rt.posix.sep;y.sep=Ne;const O=Symbol("globstar **");y.GLOBSTAR=O;const $e="[^/]",Ce=$e+"*?",Te="(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?",Fe="(?:(?!(?:\\/|^)\\.).)*?",Pe=(s,t={})=>e=>y(e,s,t);y.filter=Pe;const v=(s,t={})=>Object.assign({},s,t),Ie=s=>{if(!s||typeof s!="object"||!Object.keys(s).length)return y;const t=y;return Object.assign((n,r,i={})=>t(n,r,v(s,i)),{Minimatch:class extends t.Minimatch{constructor(r,i={}){super(r,v(s,i))}static defaults(r){return t.defaults(v(s,r)).Minimatch}},AST:class extends t.AST{constructor(r,i,o={}){super(r,i,v(s,o))}static fromGlob(r,i={}){return t.AST.fromGlob(r,v(s,i))}},unescape:(n,r={})=>t.unescape(n,v(s,r)),escape:(n,r={})=>t.escape(n,v(s,r)),filter:(n,r={})=>t.filter(n,v(s,r)),defaults:n=>t.defaults(v(s,n)),makeRe:(n,r={})=>t.makeRe(n,v(s,r)),braceExpand:(n,r={})=>t.braceExpand(n,v(s,r)),match:(n,r,i={})=>t.match(n,r,v(s,i)),sep:t.sep,GLOBSTAR:O})};y.defaults=Ie;const vt=(s,t={})=>(_(s),t.nobrace||!/\{(?:(?!\{).)*\}/.test(s)?[s]:Vt(s));y.braceExpand=vt;const Me=(s,t={})=>new q(s,t).makeRe();y.makeRe=Me;const Re=(s,t,e={})=>{const n=new q(t,e);return s=s.filter(r=>n.match(r)),n.options.nonull&&!s.length&&s.push(t),s};y.match=Re;const it=/[?*]|[+@!]\(.*?\)|\[|\]/,ke=s=>s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");class q{options;set;pattern;windowsPathsNoEscape;nonegate;negate;comment;empty;preserveMultipleSlashes;partial;globSet;globParts;nocase;isWindows;platform;windowsNoMagicRoot;regexp;constructor(t,e={}){_(t),e=e||{},this.options=e,this.pattern=t,this.platform=e.platform||xt,this.isWindows=this.platform==="win32",this.windowsPathsNoEscape=!!e.windowsPathsNoEscape||e.allowWindowsEscape===!1,this.windowsPathsNoEscape&&(this.pattern=this.pattern.replace(/\\/g,"/")),this.preserveMultipleSlashes=!!e.preserveMultipleSlashes,this.regexp=null,this.negate=!1,this.nonegate=!!e.nonegate,this.comment=!1,this.empty=!1,this.partial=!!e.partial,this.nocase=!!this.options.nocase,this.windowsNoMagicRoot=e.windowsNoMagicRoot!==void 0?e.windowsNoMagicRoot:!!(this.isWindows&&this.nocase),this.globSet=[],this.globParts=[],this.set=[],this.make()}hasMagic(){if(this.options.magicalBraces&&this.set.length>1)return!0;for(const t of this.set)for(const e of t)if(typeof e!="string")return!0;return!1}debug(...t){}make(){const t=this.pattern,e=this.options;if(!e.nocomment&&t.charAt(0)==="#"){this.comment=!0;return}if(!t){this.empty=!0;return}this.parseNegate(),this.globSet=[...new Set(this.braceExpand())],e.debug&&(this.debug=(...i)=>console.error(...i)),this.debug(this.pattern,this.globSet);const n=this.globSet.map(i=>this.slashSplit(i));this.globParts=this.preprocess(n),this.debug(this.pattern,this.globParts);let r=this.globParts.map((i,o,a)=>{if(this.isWindows&&this.windowsNoMagicRoot){const c=i[0]===""&&i[1]===""&&(i[2]==="?"||!it.test(i[2]))&&!it.test(i[3]),h=/^[a-z]:/i.test(i[0]);if(c)return[...i.slice(0,4),...i.slice(4).map(l=>this.parse(l))];if(h)return[i[0],...i.slice(1).map(l=>this.parse(l))]}return i.map(c=>this.parse(c))});if(this.debug(this.pattern,r),this.set=r.filter(i=>i.indexOf(!1)===-1),this.isWindows)for(let i=0;i<this.set.length;i++){const o=this.set[i];o[0]===""&&o[1]===""&&this.globParts[i][2]==="?"&&typeof o[3]=="string"&&/^[a-z]:$/i.test(o[3])&&(o[2]="?")}this.debug(this.pattern,this.set)}preprocess(t){if(this.options.noglobstar)for(let n=0;n<t.length;n++)for(let r=0;r<t[n].length;r++)t[n][r]==="**"&&(t[n][r]="*");const{optimizationLevel:e=1}=this.options;return e>=2?(t=this.firstPhasePreProcess(t),t=this.secondPhasePreProcess(t)):e>=1?t=this.levelOneOptimize(t):t=this.adjascentGlobstarOptimize(t),t}adjascentGlobstarOptimize(t){return t.map(e=>{let n=-1;for(;(n=e.indexOf("**",n+1))!==-1;){let r=n;for(;e[r+1]==="**";)r++;r!==n&&e.splice(n,r-n)}return e})}levelOneOptimize(t){return t.map(e=>(e=e.reduce((n,r)=>{const i=n[n.length-1];return r==="**"&&i==="**"?n:r===".."&&i&&i!==".."&&i!=="."&&i!=="**"?(n.pop(),n):(n.push(r),n)},[]),e.length===0?[""]:e))}levelTwoFileOptimize(t){Array.isArray(t)||(t=this.slashSplit(t));let e=!1;do{if(e=!1,!this.preserveMultipleSlashes){for(let r=1;r<t.length-1;r++){const i=t[r];r===1&&i===""&&t[0]===""||(i==="."||i==="")&&(e=!0,t.splice(r,1),r--)}t[0]==="."&&t.length===2&&(t[1]==="."||t[1]==="")&&(e=!0,t.pop())}let n=0;for(;(n=t.indexOf("..",n+1))!==-1;){const r=t[n-1];r&&r!=="."&&r!==".."&&r!=="**"&&(e=!0,t.splice(n-1,2),n-=2)}}while(e);return t.length===0?[""]:t}firstPhasePreProcess(t){let e=!1;do{e=!1;for(let n of t){let r=-1;for(;(r=n.indexOf("**",r+1))!==-1;){let o=r;for(;n[o+1]==="**";)o++;o>r&&n.splice(r+1,o-r);let a=n[r+1];const c=n[r+2],h=n[r+3];if(a!==".."||!c||c==="."||c===".."||!h||h==="."||h==="..")continue;e=!0,n.splice(r,1);const l=n.slice(0);l[r]="**",t.push(l),r--}if(!this.preserveMultipleSlashes){for(let o=1;o<n.length-1;o++){const a=n[o];o===1&&a===""&&n[0]===""||(a==="."||a==="")&&(e=!0,n.splice(o,1),o--)}n[0]==="."&&n.length===2&&(n[1]==="."||n[1]==="")&&(e=!0,n.pop())}let i=0;for(;(i=n.indexOf("..",i+1))!==-1;){const o=n[i-1];if(o&&o!=="."&&o!==".."&&o!=="**"){e=!0;const c=i===1&&n[i+1]==="**"?["."]:[];n.splice(i-1,2,...c),n.length===0&&n.push(""),i-=2}}}}while(e);return t}secondPhasePreProcess(t){for(let e=0;e<t.length-1;e++)for(let n=e+1;n<t.length;n++){const r=this.partsMatch(t[e],t[n],!this.preserveMultipleSlashes);if(r){t[e]=[],t[n]=r;break}}return t.filter(e=>e.length)}partsMatch(t,e,n=!1){let r=0,i=0,o=[],a="";for(;r<t.length&&i<e.length;)if(t[r]===e[i])o.push(a==="b"?e[i]:t[r]),r++,i++;else if(n&&t[r]==="**"&&e[i]===t[r+1])o.push(t[r]),r++;else if(n&&e[i]==="**"&&t[r]===e[i+1])o.push(e[i]),i++;else if(t[r]==="*"&&e[i]&&(this.options.dot||!e[i].startsWith("."))&&e[i]!=="**"){if(a==="b")return!1;a="a",o.push(t[r]),r++,i++}else if(e[i]==="*"&&t[r]&&(this.options.dot||!t[r].startsWith("."))&&t[r]!=="**"){if(a==="a")return!1;a="b",o.push(e[i]),r++,i++}else return!1;return t.length===e.length&&o}parseNegate(){if(this.nonegate)return;const t=this.pattern;let e=!1,n=0;for(let r=0;r<t.length&&t.charAt(r)==="!";r++)e=!e,n++;n&&(this.pattern=t.slice(n)),this.negate=e}matchOne(t,e,n=!1){const r=this.options;if(this.isWindows){const p=typeof t[0]=="string"&&/^[a-z]:$/i.test(t[0]),S=!p&&t[0]===""&&t[1]===""&&t[2]==="?"&&/^[a-z]:$/i.test(t[3]),A=typeof e[0]=="string"&&/^[a-z]:$/i.test(e[0]),C=!A&&e[0]===""&&e[1]===""&&e[2]==="?"&&typeof e[3]=="string"&&/^[a-z]:$/i.test(e[3]),x=S?3:p?0:void 0,w=C?3:A?0:void 0;if(typeof x=="number"&&typeof w=="number"){const[T,L]=[t[x],e[w]];T.toLowerCase()===L.toLowerCase()&&(e[w]=T,w>x?e=e.slice(w):x>w&&(t=t.slice(x)))}}const{optimizationLevel:i=1}=this.options;i>=2&&(t=this.levelTwoFileOptimize(t)),this.debug("matchOne",this,{file:t,pattern:e}),this.debug("matchOne",t.length,e.length);for(var o=0,a=0,c=t.length,h=e.length;o<c&&a<h;o++,a++){this.debug("matchOne loop");var l=e[a],d=t[o];if(this.debug(e,l,d),l===!1)return!1;if(l===O){this.debug("GLOBSTAR",[e,l,d]);var u=o,f=a+1;if(f===h){for(this.debug("** at the end");o<c;o++)if(t[o]==="."||t[o]===".."||!r.dot&&t[o].charAt(0)===".")return!1;return!0}for(;u<c;){var g=t[u];if(this.debug(`
2
+ globstar while`,t,u,e,f,g),this.matchOne(t.slice(u),e.slice(f),n))return this.debug("globstar found match!",u,c,g),!0;if(g==="."||g===".."||!r.dot&&g.charAt(0)==="."){this.debug("dot detected!",t,u,e,f);break}this.debug("globstar swallow a segment, and continue"),u++}return!!(n&&(this.debug(`
3
+ >>> no match, partial?`,t,u,e,f),u===c))}let p;if(typeof l=="string"?(p=d===l,this.debug("string match",l,d,p)):(p=l.test(d),this.debug("pattern match",l,d,p)),!p)return!1}if(o===c&&a===h)return!0;if(o===c)return n;if(a===h)return o===c-1&&t[o]==="";throw new Error("wtf?")}braceExpand(){return vt(this.pattern,this.options)}parse(t){_(t);const e=this.options;if(t==="**")return O;if(t==="")return"";let n,r=null;(n=t.match(Ee))?r=e.dot?xe:Se:(n=t.match(le))?r=(e.nocase?e.dot?de:fe:e.dot?ue:he)(n[1]):(n=t.match(ve))?r=(e.nocase?e.dot?be:Oe:e.dot?Ae:De)(n):(n=t.match(pe))?r=e.dot?me:ge:(n=t.match(we))&&(r=ye);const i=E.fromGlob(t,this.options).toMMPattern();return r&&typeof i=="object"&&Reflect.defineProperty(i,"test",{value:r}),i}makeRe(){if(this.regexp||this.regexp===!1)return this.regexp;const t=this.set;if(!t.length)return this.regexp=!1,this.regexp;const e=this.options,n=e.noglobstar?Ce:e.dot?Te:Fe,r=new Set(e.nocase?["i"]:[]);let i=t.map(c=>{const h=c.map(l=>{if(l instanceof RegExp)for(const d of l.flags.split(""))r.add(d);return typeof l=="string"?ke(l):l===O?O:l._src});return h.forEach((l,d)=>{const u=h[d+1],f=h[d-1];l!==O||f===O||(f===void 0?u!==void 0&&u!==O?h[d+1]="(?:\\/|"+n+"\\/)?"+u:h[d]=n:u===void 0?h[d-1]=f+"(?:\\/|"+n+")?":u!==O&&(h[d-1]=f+"(?:\\/|\\/"+n+"\\/)"+u,h[d+1]=O))}),h.filter(l=>l!==O).join("/")}).join("|");const[o,a]=t.length>1?["(?:",")"]:["",""];i="^"+o+i+a+"$",this.negate&&(i="^(?!"+i+").+$");try{this.regexp=new RegExp(i,[...r].join(""))}catch{this.regexp=!1}return this.regexp}slashSplit(t){return this.preserveMultipleSlashes?t.split("/"):this.isWindows&&/^\/\/[^\/]+/.test(t)?["",...t.split(/\/+/)]:t.split(/\/+/)}match(t,e=this.partial){if(this.debug("match",t,this.pattern),this.comment)return!1;if(this.empty)return t==="";if(t==="/"&&e)return!0;const n=this.options;this.isWindows&&(t=t.split("\\").join("/"));const r=this.slashSplit(t);this.debug(this.pattern,"split",r);const i=this.set;this.debug(this.pattern,"set",i);let o=r[r.length-1];if(!o)for(let a=r.length-2;!o&&a>=0;a--)o=r[a];for(let a=0;a<i.length;a++){const c=i[a];let h=r;if(n.matchBase&&c.length===1&&(h=[o]),this.matchOne(h,c,e))return n.flipNegate?!0:!this.negate}return n.flipNegate?!1:this.negate}static defaults(t){return y.defaults(t).Minimatch}}y.AST=E;y.Minimatch=q;y.escape=ce;y.unescape=I;const Ot=[".jpg",".jpeg",".png",".gif",".bmp",".webp",".ico",".tiff",".tga",".mp3",".wav",".ogg",".flac",".aac",".wma",".m4a",".mp4",".avi",".mov",".wmv",".flv",".webm",".mkv",".m4v",".pdf",".doc",".docx",".xls",".xlsx",".ppt",".pptx",".zip",".rar",".7z",".tar",".gz",".bz2",".exe",".dll",".so",".dylib",".dat",".db",".sqlite",".bin",".obj",".fbx",".3ds"];function Le(s){const t=s.lastIndexOf(".");if(t<=0)return!0;const e=s.slice(t).toLowerCase();return Ot.includes(e)}function bt(s,t="utf-8"){switch(t){case"utf8":case"utf-8":return new TextEncoder().encode(s);case"utf16le":case"utf-16le":case"ucs2":case"ucs-2":return Be(s);case"ascii":return Ue(s);case"latin1":return We(s);case"binary":return Uint8Array.from(s,e=>e.charCodeAt(0));case"base64":return Uint8Array.from(atob(s),e=>e.charCodeAt(0));case"hex":if(!/^[\da-f]+$/i.test(s)||s.length%2!==0)throw new D("format","Invalid hex string");return Uint8Array.from(s.match(/.{1,2}/g).map(e=>parseInt(e,16)));default:return console.warn("Encoding not supported, falling back to UTF-8"),new TextEncoder().encode(s)}}function ze(s,t="utf-8"){switch(t){case"utf8":case"utf-8":return new TextDecoder().decode(s);case"utf16le":case"utf-16le":case"ucs2":case"ucs-2":return je(s);case"latin1":return String.fromCharCode(...s);case"ascii":return String.fromCharCode(...s.map(e=>e&127));case"base64":return btoa(String.fromCharCode(...s));case"hex":return Array.from(s).map(e=>e.toString(16).padStart(2,"0")).join("");default:return console.warn("Unsupported encoding, falling back to UTF-8"),new TextDecoder().decode(s)}}function Be(s){const t=new Uint8Array(s.length*2);for(let e=0;e<s.length;e++){const n=s.charCodeAt(e);t[e*2]=n&255,t[e*2+1]=n>>8}return t}function je(s){s.length%2!==0&&(console.warn("Invalid UTF-16LE buffer length, truncating last byte"),s=s.slice(0,s.length-1));const t=new Uint16Array(s.buffer,s.byteOffset,s.byteLength/2);return String.fromCharCode(...t)}function We(s){const t=new Uint8Array(s.length);for(let e=0;e<s.length;e++)t[e]=s.charCodeAt(e)&255;return t}function Ue(s){const t=new Uint8Array(s.length);for(let e=0;e<s.length;e++)t[e]=s.charCodeAt(e)&127;return t}function At(){if(!("storage"in navigator)||!("getDirectory"in navigator.storage))throw new ot}async function Dt(s,t){return typeof navigator<"u"&&navigator.locks?.request?navigator.locks.request(`opfs:${s.replace(/\/+/g,"/")}`,{mode:"exclusive"},t):t()}function $(s){return Array.isArray(s)?s:(s.startsWith("~/")?s.slice(2):s).split("/").filter(Boolean)}function G(s){return typeof s=="string"?s??"/":`/${s.join("/")}`}function R(s){const t=$(s);return t[t.length-1]||""}function j(s){const t=$(s);return t.pop(),G(t)}function k(s){return!s||s==="/"?"/":s.startsWith("~/")?`/${s.slice(2)}`:s.startsWith("/")?s:`/${s}`}function Nt(s,t=!1){return s=s.replace(/\/$/,""),t&&!s.includes("*")?`${s}/**`:s}function W(s,t){return y(s,t,{dot:!0,matchBase:!0})}function He(s,t){if(!t||Array.isArray(t)&&t.length===0)return!1;const e=k(s);return(Array.isArray(t)?t:[t]).some(r=>y(e,r,{dot:!0}))}function $t(s){const t=k(s),e=$(t),n=[];for(const r of e)if(!(r==="."||r===""))if(r===".."){if(n.length===0)continue;n.pop()}else n.push(r);return G(n)}function _e(s){const t=R(s),e=t.lastIndexOf(".");return e<=0||e===t.length-1?"":t.slice(e)}function qe(s,t="utf-8"){return typeof s=="string"?bt(s,t):s instanceof Uint8Array?s:new Uint8Array(s)}async function Ct(s,t="SHA-1",e=50*1024*1024){if(s instanceof File&&(s=await s.arrayBuffer()),s.byteLength>e)throw new Error(`File size ${s.byteLength} bytes exceeds maximum allowed size ${e} bytes`);const n=new Uint8Array(s),r=await crypto.subtle.digest(t,n);return Array.from(new Uint8Array(r)).map(o=>o.toString(16).padStart(2,"0")).join("")}function Ge(s,t){if(s.length!==t.length)return!1;for(let e=0;e<s.length;e++)if(s[e]!==t[e])return!1;return!0}async function Tt(s){const t=await s.arrayBuffer();return new Uint8Array(t)}async function Ft(s,t,e={}){const n=R(t);return Dt(t,async()=>{const r=e.recursive??!1,i=e.force??!1;try{await s.removeEntry(n,{recursive:r})}catch(o){if(o.name==="NotFoundError"&&i)return;const a=o.name==="TypeMismatchError"&&!r;throw b(o,{path:t,operation:"remove",isDirectory:a})}})}function Ve(s,t,e,n){if(!Number.isInteger(t)||!Number.isInteger(e))throw new D("argument","Invalid offset or length");if(t<0||e<0)throw new D("argument","Negative offset or length not allowed");if(t+e>s)throw new D("overflow","Operation would overflow buffer");if(n!=null&&(!Number.isInteger(n)||n<0))throw new D("argument","Invalid position")}function Ye(s,t,e){try{t.flush(),t.close()}catch(n){console.warn(`Warning: Failed to properly close file descriptor ${s} (${e}):`,n)}}function Xe(s,t,e){if(s>=e)return{isEOF:!0,actualLength:0};const n=Math.min(t,e-s);return n<=0?{isEOF:!0,actualLength:0}:{isEOF:!1,actualLength:n}}async function Ze(s,t){try{return await s.createSyncAccessHandle()}catch(e){throw b(e,{path:t,isDirectory:!1})}}class Je{root;watchers=new Map;mountingPromise=null;broadcastChannel=null;options={root:"/",namespace:"",maxFileSize:50*1024*1024,hashAlgorithm:"etag",broadcastChannel:"opfs-worker"};async notifyChange(t){if(!this.options.broadcastChannel)return;const e=t.path;if(![...this.watchers.values()].some(i=>W(e,i.pattern)&&i.include.some(o=>o&&W(e,o))&&!i.exclude.some(o=>o&&W(e,o))))return;let r;if(this.options.hashAlgorithm)try{r=(await this.stat(e)).hash}catch{}try{this.broadcastChannel||(this.broadcastChannel=new BroadcastChannel(this.options.broadcastChannel));const i={namespace:this.options.namespace,timestamp:new Date().toISOString(),...t,...r&&{hash:r}};this.broadcastChannel.postMessage(i)}catch(i){console.warn("Failed to send event via BroadcastChannel:",i)}}constructor(t){At(),t&&this.setOptions(t)}async mount(){const t=this.options.root;return this.mountingPromise&&await this.mountingPromise,this.mountingPromise=new Promise(async(e,n)=>{try{const r=await navigator.storage.getDirectory();this.root=t==="/"?r:await this.getDirectoryHandle(t,!0,r),e(!0)}catch(r){n(new ft(t,r))}finally{this.mountingPromise=null}}),this.mountingPromise}async setOptions(t){t.hashAlgorithm!==void 0&&(this.options.hashAlgorithm=t.hashAlgorithm),t.maxFileSize!==void 0&&(this.options.maxFileSize=t.maxFileSize),t.broadcastChannel!==void 0&&(this.broadcastChannel&&this.options.broadcastChannel!==t.broadcastChannel&&(this.broadcastChannel.close(),this.broadcastChannel=null),this.options.broadcastChannel=t.broadcastChannel),t.namespace&&(this.options.namespace=t.namespace),t.root!==void 0&&(this.options.root=k(t.root),this.options.namespace||(this.options.namespace=`opfs-worker:${this.options.root}`),await this.mount())}async getDirectoryHandle(t,e=!1,n=this.root){const r=Array.isArray(t)?t:$(t);let i=n;for(const o of r)i=await i.getDirectoryHandle(o,{create:e});return i}async getFileHandle(t,e=!1,n=this.root){const r=$(t);if(r.length===0)throw new at("Path must not be empty",Array.isArray(t)?t.join("/"):t);const i=r.pop();return(await this.getDirectoryHandle(r,e,n)).getFileHandle(i,{create:e})}async index(){const t=new Map,e=async n=>{const r=await this.readDir(n);for(const i of r){const o=`${n==="/"?"":n}/${i.name}`;try{const a=await this.stat(o);t.set(o,a),a.isDirectory&&await e(o)}catch(a){console.warn(`Skipping broken entry: ${o}`,a)}}};return t.set("/",{kind:"directory",size:0,mtime:new Date(0).toISOString(),ctime:new Date(0).toISOString(),isFile:!1,isDirectory:!0}),await e("/"),t}async mkdir(t,e){await this.mount();const n=e?.recursive??!1,r=$(t);let i=this.root;for(let o=0;o<r.length;o++){const a=r[o];try{i=await i.getDirectoryHandle(a,{create:n||o===r.length-1})}catch(c){throw c.name==="NotFoundError"?b(c,{path:G(r.slice(0,o+1)),existenceType:"directory"}):c.name==="TypeMismatchError"?b(c,{path:a,isDirectory:!1}):new B("create directory",a,c)}}await this.notifyChange({path:t,type:N.Added,isDirectory:!0})}async stat(t){if(await this.mount(),t==="/")return{kind:"directory",size:0,mtime:new Date(0).toISOString(),ctime:new Date(0).toISOString(),isFile:!1,isDirectory:!0};const e=R(t);let n;try{n=await this.getDirectoryHandle(j(t),!1);const r=this.options.hashAlgorithm,o=await(await n.getFileHandle(e,{create:!1})).getFile(),a={kind:"file",size:o.size,mtime:new Date(o.lastModified).toISOString(),ctime:new Date(o.lastModified).toISOString(),isFile:!0,isDirectory:!1};if(r==="etag")a.hash=`${o.lastModified.toString(36)}-${o.size.toString(36)}`;else if(typeof r=="string")try{const c=await Ct(o,r,this.options.maxFileSize);a.hash=c}catch(c){console.warn(`Failed to calculate hash for ${t}:`,c)}return a}catch(r){if(r.name==="NotFoundError")throw new M("file",t,r);if(r.name!=="TypeMismatchError")throw new B("stat",t,r)}try{return await n.getDirectoryHandle(e,{create:!1}),{kind:"directory",size:0,mtime:new Date(0).toISOString(),ctime:new Date(0).toISOString(),isFile:!1,isDirectory:!0}}catch(r){throw new B("stat",t,r)}}async readDir(t){await this.mount();const e=await this.getDirectoryHandle(t,!1),n=[];for await(const[r,i]of e.entries()){const o=i.kind==="file";n.push({name:r,kind:i.kind,isFile:o,isDirectory:!o})}return n}async exists(t){if(await this.mount(),t==="/")return!0;const e=R(t);let n=null;try{n=await this.getDirectoryHandle(j(t),!1)}catch(r){if(n=null,r.name!=="NotFoundError"&&r.name!=="TypeMismatchError")throw r}if(!n||!e)return!1;try{return await n.getFileHandle(e,{create:!1}),!0}catch(r){if(r.name!=="NotFoundError"&&r.name!=="TypeMismatchError")throw r;try{return await n.getDirectoryHandle(e,{create:!1}),!0}catch(i){if(i.name!=="NotFoundError"&&i.name!=="TypeMismatchError")throw i;return!1}}}async clear(t="/"){await this.mount();try{const e=await this.readDir(t);for(const n of e){const r=`${t==="/"?"":t}/${n.name}`;await this.remove(r,{recursive:!0})}await this.notifyChange({path:t,type:N.Changed,isDirectory:!0})}catch(e){throw e instanceof m?e:b(e,{path:t,isDirectory:!0})}}async remove(t,e){if(await this.mount(),t==="/")throw new H("EROOT",t);const{recursive:n=!1,force:r=!1}=e||{},i=await this.getDirectoryHandle(j(t),!1),o=await this.stat(t);await Ft(i,t,{recursive:n,force:r}),await this.notifyChange({path:t,type:N.Removed,isDirectory:o.isDirectory})}async realpath(t){await this.mount();try{const e=$t(t);if(!await this.exists(e))throw new M("file",e);return e}catch(e){throw e instanceof m?e:b(e,{path:t})}}async rename(t,e,n){await this.mount();try{const r=n?.overwrite??!1,i=await this.stat(t);if(await this.exists(e)&&!r)throw new X(e);await this.copy(t,e,{recursive:!0,overwrite:r}),await this.remove(t,{recursive:!0}),await this.notifyChange({path:t,type:N.Removed,isDirectory:i.isDirectory}),await this.notifyChange({path:e,type:N.Added,isDirectory:i.isDirectory})}catch(r){throw r instanceof m?r:b(r,{path:t})}}async copy(t,e,n){await this.mount();try{const r=n?.recursive??!1,i=n?.overwrite??!0;if(!await this.exists(t))throw new M("source",t);if(await this.exists(e)&&!i)throw new X(e);if((await this.stat(t)).isFile){const h=await this.readFile(t);await this.writeFile(e,h)}else{if(!r)throw new U("directory",t);await this.mkdir(e,{recursive:!0});const h=await this.readDir(t);for(const l of h){const d=`${t}/${l.name}`,u=`${e}/${l.name}`;await this.copy(d,u,{recursive:!0,overwrite:i})}}}catch(r){throw r instanceof m?r:b(r,{path:t})}}async watch(t,e){if(!this.options.broadcastChannel)throw new Z("This instance is not configured to send events. Please specify options.broadcastChannel to enable watching.");const n={pattern:Nt(t,e?.recursive??!0),include:Array.isArray(e?.include)?e.include:[e?.include??"**"],exclude:Array.isArray(e?.exclude)?e.exclude:[e?.exclude??""]};this.watchers.set(t,n)}unwatch(t){this.watchers.delete(t)}async createIndex(t){await this.mount();try{for(const[e,n]of t){const r=k(e);let i;n instanceof Blob?i=await Tt(n):typeof n=="string"?i=new TextEncoder().encode(n):i=n,await this.writeFile(r,i)}}catch(e){throw e instanceof m?e:b(e)}}dispose(){this.broadcastChannel&&(this.broadcastChannel.close(),this.broadcastChannel=null),this.watchers.clear()}}exports.AlreadyExistsError=X;exports.BINARY_FILE_EXTENSIONS=Ot;exports.BaseOPFS=Je;exports.DirectoryOperationError=H;exports.ExistenceError=M;exports.FileBusyError=ht;exports.FileSystemOperationError=B;exports.FileTypeError=U;exports.IOError=Y;exports.InitializationFailedError=ft;exports.OPFSError=m;exports.OPFSNotSupportedError=ot;exports.OperationAbortedError=ut;exports.OperationNotSupportedError=Z;exports.PathError=at;exports.PermissionError=ct;exports.StorageError=lt;exports.ValidationError=D;exports.WatchEventType=N;exports.basename=R;exports.buffersEqual=Ge;exports.calculateFileHash=Ct;exports.calculateReadLength=Xe;exports.checkOPFSSupport=At;exports.convertBlobToUint8Array=Tt;exports.createBuffer=qe;exports.createFDError=It;exports.createSyncHandleSafe=Ze;exports.decodeBuffer=ze;exports.dirname=j;exports.encodeString=bt;exports.extname=_e;exports.isBinaryFileExtension=Le;exports.isPathExcluded=He;exports.joinPath=G;exports.mapDomError=b;exports.matchMinimatch=W;exports.normalizeMinimatch=Nt;exports.normalizePath=k;exports.removeEntry=Ft;exports.resolvePath=$t;exports.safeCloseSyncHandle=Ye;exports.splitPath=$;exports.validateReadWriteArgs=Ve;exports.withLock=Dt;
4
+ //# sourceMappingURL=BaseOPFS-BPw9AIQ-.cjs.map