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.
- package/README.md +62 -439
- package/dist/BaseOPFS-BPw9AIQ-.cjs +4 -0
- package/dist/BaseOPFS-BPw9AIQ-.cjs.map +1 -0
- package/dist/BaseOPFS-Cylz6ijk.js +2036 -0
- package/dist/BaseOPFS-Cylz6ijk.js.map +1 -0
- package/dist/OPFSAsync-BKSAJJeh.js +124 -0
- package/dist/OPFSAsync-BKSAJJeh.js.map +1 -0
- package/dist/OPFSAsync-s2P7y3sw.cjs +2 -0
- package/dist/OPFSAsync-s2P7y3sw.cjs.map +1 -0
- package/dist/OPFSFacade-DePnNRgt.cjs +2 -0
- package/dist/OPFSFacade-DePnNRgt.cjs.map +1 -0
- package/dist/OPFSFacade-XohWlHuu.js +281 -0
- package/dist/OPFSFacade-XohWlHuu.js.map +1 -0
- package/dist/OPFSSync-C7KBepe_.cjs +2 -0
- package/dist/OPFSSync-C7KBepe_.cjs.map +1 -0
- package/dist/OPFSSync-CsSQkpk5.js +208 -0
- package/dist/OPFSSync-CsSQkpk5.js.map +1 -0
- package/dist/assets/dedicated.worker-nY3tK4zi.js.map +1 -0
- package/dist/{OPFSWorker.d.ts → core/BaseOPFS.d.ts} +34 -304
- package/dist/core/BaseOPFS.d.ts.map +1 -0
- package/dist/core/OPFSAsync.d.ts +55 -0
- package/dist/core/OPFSAsync.d.ts.map +1 -0
- package/dist/core/OPFSSync.d.ts +54 -0
- package/dist/core/OPFSSync.d.ts.map +1 -0
- package/dist/createOPFSAsync-BwKDmHsy.cjs +2 -0
- package/dist/createOPFSAsync-BwKDmHsy.cjs.map +1 -0
- package/dist/createOPFSAsync-Y4Mg6qte.js +10 -0
- package/dist/createOPFSAsync-Y4Mg6qte.js.map +1 -0
- package/dist/createOPFSDedicated-CsnV6MLP.js +2333 -0
- package/dist/createOPFSDedicated-CsnV6MLP.js.map +1 -0
- package/dist/createOPFSDedicated-pKTRWLdz.cjs +2283 -0
- package/dist/createOPFSDedicated-pKTRWLdz.cjs.map +1 -0
- package/dist/createOPFSShared-CGdd__PH.js +23 -0
- package/dist/createOPFSShared-CGdd__PH.js.map +1 -0
- package/dist/createOPFSShared-CskROt7U.cjs +2 -0
- package/dist/createOPFSShared-CskROt7U.cjs.map +1 -0
- package/dist/dedicated.worker.js +2281 -0
- package/dist/dedicated.worker.js.map +1 -0
- package/dist/{OPFSFacade.d.ts → facade/OPFSFacade.d.ts} +31 -23
- package/dist/facade/OPFSFacade.d.ts.map +1 -0
- package/dist/facade/createOPFSAsync.d.ts +16 -0
- package/dist/facade/createOPFSAsync.d.ts.map +1 -0
- package/dist/facade/createOPFSDedicated.d.ts +17 -0
- package/dist/facade/createOPFSDedicated.d.ts.map +1 -0
- package/dist/facade/createOPFSShared.d.ts +24 -0
- package/dist/facade/createOPFSShared.d.ts.map +1 -0
- package/dist/index.async.cjs +2 -0
- package/dist/index.async.cjs.map +1 -0
- package/dist/index.async.d.ts +10 -0
- package/dist/index.async.d.ts.map +1 -0
- package/dist/index.async.js +55 -0
- package/dist/index.async.js.map +1 -0
- package/dist/index.cjs +1 -2477
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +17 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -2828
- package/dist/index.js.map +1 -1
- package/dist/index.pure.cjs +1 -1
- package/dist/index.pure.cjs.map +1 -1
- package/dist/index.pure.d.ts +3 -1
- package/dist/index.pure.d.ts.map +1 -1
- package/dist/index.pure.js +50 -1053
- package/dist/index.pure.js.map +1 -1
- package/dist/index.sharedworker.cjs +2 -0
- package/dist/index.sharedworker.cjs.map +1 -0
- package/dist/index.sharedworker.d.ts +11 -0
- package/dist/index.sharedworker.d.ts.map +1 -0
- package/dist/index.sharedworker.js +55 -0
- package/dist/index.sharedworker.js.map +1 -0
- package/dist/index.sync.cjs +2 -0
- package/dist/index.sync.cjs.map +1 -0
- package/dist/index.sync.d.ts +11 -0
- package/dist/index.sync.d.ts.map +1 -0
- package/dist/index.sync.js +55 -0
- package/dist/index.sync.js.map +1 -0
- package/dist/shared.worker.js +2166 -0
- package/dist/shared.worker.js.map +1 -0
- package/dist/types.d.ts +9 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/worker/createDedicatedWorker.d.ts +30 -0
- package/dist/worker/createDedicatedWorker.d.ts.map +1 -0
- package/dist/worker/createSharedWorker.d.ts +37 -0
- package/dist/worker/createSharedWorker.d.ts.map +1 -0
- package/dist/worker/dedicated.worker.d.ts +2 -0
- package/dist/worker/dedicated.worker.d.ts.map +1 -0
- package/dist/worker/shared.worker.d.ts +2 -0
- package/dist/worker/shared.worker.d.ts.map +1 -0
- package/docs/README.md +30 -0
- package/docs/api/backend.md +36 -0
- package/docs/api/create.md +82 -0
- package/docs/api/facade.md +52 -0
- package/docs/choosing-a-mode.md +41 -0
- package/docs/file-descriptors.md +8 -9
- package/docs/guides/async.md +36 -0
- package/docs/guides/dedicated.md +76 -0
- package/docs/guides/hashing.md +34 -0
- package/docs/guides/pure.md +46 -0
- package/docs/guides/sharedworker.md +57 -0
- package/docs/guides/streaming.md +51 -0
- package/docs/guides/watching.md +35 -0
- package/docs/migration.md +41 -0
- package/docs/types.md +20 -6
- package/package.json +42 -3
- package/src/{OPFSWorker.ts → core/BaseOPFS.ts} +54 -647
- package/src/core/OPFSAsync.ts +248 -0
- package/src/core/OPFSSync.ts +460 -0
- package/src/{OPFSFacade.ts → facade/OPFSFacade.ts} +83 -54
- package/src/facade/createOPFSAsync.ts +22 -0
- package/src/facade/createOPFSDedicated.ts +23 -0
- package/src/facade/createOPFSShared.ts +30 -0
- package/src/index.async.ts +11 -0
- package/src/index.pure.ts +3 -2
- package/src/index.sharedworker.ts +12 -0
- package/src/index.sync.ts +12 -0
- package/src/index.ts +37 -8
- package/src/types.ts +8 -4
- package/src/worker/createDedicatedWorker.ts +74 -0
- package/src/worker/createSharedWorker.ts +67 -0
- package/src/worker/dedicated.worker.ts +13 -0
- package/src/worker/shared.worker.ts +17 -0
- package/dist/OPFSFacade.d.ts.map +0 -1
- package/dist/OPFSWorker.d.ts.map +0 -1
- package/dist/assets/worker.entry-DUlEoroc.js.map +0 -1
- package/dist/createOPFSWorker.d.ts +0 -17
- package/dist/createOPFSWorker.d.ts.map +0 -1
- package/dist/helpers-DNj8ZoMu.cjs +0 -4
- package/dist/helpers-DNj8ZoMu.cjs.map +0 -1
- package/dist/helpers-WY2jfbOT.js +0 -1427
- package/dist/helpers-WY2jfbOT.js.map +0 -1
- package/dist/worker.entry.d.ts +0 -2
- package/dist/worker.entry.d.ts.map +0 -1
- package/docs/api-reference.md +0 -815
- package/src/createOPFSWorker.ts +0 -57
- package/src/worker.entry.ts +0 -6
package/README.md
CHANGED
|
@@ -1,111 +1,23 @@
|
|
|
1
1
|
# OPFS Worker
|
|
2
2
|
|
|
3
|
-
[npm
|
|
3
|
+
[npm](https://www.npmjs.com/package/opfs-worker)
|
|
4
|
+
[demo](https://kachurun.github.io/opfs-worker/)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
## What is OPFS?
|
|
6
7
|
|
|
7
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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.
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
308
|
-
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
-
|
|
324
|
-
|
|
325
|
-
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
- [
|
|
340
|
-
- [
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
-
|
|
347
|
-
-
|
|
348
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|