@mikestools/usefilesystem 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # @mikestools/usefilesystem
2
+
3
+ Vue 3 composables for in-memory virtual filesystem and ZIP archive operations.
4
+
5
+ ## Features
6
+
7
+ - **Virtual Filesystem** - In-memory filesystem with familiar Node.js-like API
8
+ - **ZIP Archives** - Create and extract ZIP files using native browser compression
9
+ - **Reactive State** - Vue 3 composables with reactive stats and progress tracking
10
+ - **Zero Dependencies** - Uses native browser APIs (CompressionStream/DecompressionStream)
11
+ - **TypeScript** - Full type safety with exported types
12
+ - **Glob Search** - Find files with patterns like `**/*.ts`
13
+ - **File Watchers** - Subscribe to create, modify, delete events
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @mikestools/usefilesystem
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```typescript
24
+ import { useFileSystem, useZip } from '@mikestools/usefilesystem'
25
+
26
+ const fs = useFileSystem()
27
+
28
+ // Write files - directories auto-created
29
+ fs.writeFile('/src/index.ts', 'export const version = "1.0.0"')
30
+ fs.writeFile('/readme.md', '# My Project')
31
+
32
+ // Reactive stats
33
+ console.log(fs.fileCount.value) // 2
34
+ console.log(fs.totalSize.value) // bytes
35
+
36
+ // Export as ZIP
37
+ const zip = useZip()
38
+ await zip.download(
39
+ fs.getAllFiles().map(f => ({ path: f.path, content: f.content })),
40
+ 'project.zip'
41
+ )
42
+ ```
43
+
44
+ ## useFileSystem
45
+
46
+ In-memory virtual filesystem with reactive stats.
47
+
48
+ ```typescript
49
+ const fs = useFileSystem()
50
+
51
+ // File operations
52
+ fs.writeFile('/path/to/file.txt', 'content')
53
+ fs.appendFile('/path/to/file.txt', 'more content')
54
+ const content = fs.readFile('/path/to/file.txt')
55
+ const binary = fs.readFile('/image.png', { encoding: 'binary' })
56
+ fs.remove('/path/to/file.txt')
57
+ fs.copy('/source.txt', '/dest.txt')
58
+ fs.move('/old.txt', '/new.txt')
59
+
60
+ // Directory operations
61
+ fs.mkdir('/a/b/c') // Creates all parent directories
62
+ fs.rmdir('/empty') // Removes empty directory
63
+ fs.rmdirRecursive('/folder') // Removes with contents
64
+ const entries = fs.list('/src', { recursive: true })
65
+
66
+ // Search
67
+ const tsFiles = fs.find('/src/**/*.ts')
68
+ const jsonFiles = fs.findByExtension('.json')
69
+
70
+ // Metadata
71
+ const meta = fs.stat('/file.txt') // { name, path, size, mimeType, createdAt, modifiedAt }
72
+ fs.exists('/path')
73
+ fs.isFile('/path')
74
+ fs.isDirectory('/path')
75
+
76
+ // Reactive stats
77
+ console.log(fs.stats.value) // { totalFiles, totalDirectories, totalSize }
78
+ console.log(fs.fileCount.value)
79
+ console.log(fs.totalSize.value)
80
+
81
+ // Watchers
82
+ const unsubscribe = fs.watch((event, path, type) => {
83
+ console.log(`${event} ${type}: ${path}`)
84
+ })
85
+
86
+ // Serialization
87
+ const json = fs.toJSON()
88
+ fs.fromJSON(json)
89
+ fs.clear()
90
+ ```
91
+
92
+ ## useZip
93
+
94
+ ZIP archive operations with reactive state.
95
+
96
+ ```typescript
97
+ const zip = useZip()
98
+
99
+ // Create ZIP
100
+ const blob = await zip.create([
101
+ { path: 'readme.txt', content: 'Hello!' },
102
+ { path: 'data/config.json', content: '{}' }
103
+ ])
104
+
105
+ // Download ZIP
106
+ await zip.download(files, 'archive.zip')
107
+
108
+ // Extract ZIP
109
+ const entries = await zip.extract(zipBlob)
110
+ const filtered = await zip.extract(zipBlob, {
111
+ filter: entry => entry.path.endsWith('.txt')
112
+ })
113
+
114
+ // Extract single file
115
+ const content = await zip.extractSingle(zipBlob, 'readme.txt')
116
+
117
+ // List without extracting
118
+ const metadata = await zip.list(zipBlob)
119
+
120
+ // Reactive state
121
+ console.log(zip.isProcessing.value) // boolean
122
+ console.log(zip.progress.value) // 0-100
123
+ console.log(zip.error.value) // string | undefined
124
+
125
+ // Compression utilities
126
+ const compressed = await zip.compress(data)
127
+ const decompressed = await zip.decompress(data)
128
+ const checksum = zip.computeCrc32(data)
129
+ ```
130
+
131
+ ## Standalone Functions
132
+
133
+ Core functions are also available without Vue:
134
+
135
+ ```typescript
136
+ import {
137
+ createFileSystem,
138
+ createZip,
139
+ extractZip,
140
+ listZip,
141
+ compress,
142
+ decompress,
143
+ computeCrc32,
144
+ downloadBlob,
145
+ downloadAsZip
146
+ } from '@mikestools/usefilesystem'
147
+
148
+ // Pure filesystem (no Vue reactivity)
149
+ const fs = createFileSystem()
150
+
151
+ // Direct ZIP operations
152
+ const blob = await createZip([{ path: 'file.txt', content: 'Hello' }])
153
+ const entries = await extractZip(blob)
154
+ ```
155
+
156
+ ## Types
157
+
158
+ All types are exported:
159
+
160
+ ```typescript
161
+ import type {
162
+ VirtualFile,
163
+ FileMetadata,
164
+ DirectoryMetadata,
165
+ DirectoryEntry,
166
+ FileSystemStats,
167
+ FileSystemWatcher,
168
+ StorageAdapter,
169
+ ZipEntry,
170
+ ZipEntryMetadata,
171
+ ZipInput,
172
+ CreateZipOptions,
173
+ ExtractZipOptions
174
+ } from '@mikestools/usefilesystem'
175
+ ```
176
+
177
+ ## Browser Support
178
+
179
+ Requires browsers with support for:
180
+ - CompressionStream/DecompressionStream (Chrome 80+, Firefox 113+, Safari 16.4+)
181
+ - TextEncoder/TextDecoder
182
+ - Blob, ArrayBuffer, Uint8Array
183
+
184
+ ## License
185
+
186
+ MIT
187
+
@@ -0,0 +1,425 @@
1
+ import { ComputedRef } from 'vue';
2
+ import { Ref } from 'vue';
3
+
4
+ /**
5
+ * Compresses data using DEFLATE (native CompressionStream).
6
+ */
7
+ export declare function compress(data: Uint8Array): Promise<Uint8Array>;
8
+
9
+ /**
10
+ * Computes CRC-32 checksum for data.
11
+ */
12
+ export declare function computeCrc32(data: Uint8Array): number;
13
+
14
+ /**
15
+ * Options for copying files.
16
+ */
17
+ export declare interface CopyOptions {
18
+ readonly overwrite?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Creates an in-memory virtual filesystem with a familiar API.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const filesystem = createFileSystem()
27
+ *
28
+ * // Write files
29
+ * filesystem.writeFile('/readme.txt', 'Hello World!')
30
+ * filesystem.writeFile('/src/index.ts', 'export const foo = 42')
31
+ *
32
+ * // Read files
33
+ * const content = filesystem.readFile('/readme.txt')
34
+ *
35
+ * // Directory operations
36
+ * filesystem.mkdir('/config')
37
+ * filesystem.list('/src')
38
+ *
39
+ * // Copy, move, delete
40
+ * filesystem.copy('/readme.txt', '/docs/readme.txt')
41
+ * filesystem.move('/old.txt', '/new.txt')
42
+ * filesystem.remove('/temp.txt')
43
+ * ```
44
+ */
45
+ export declare function createFileSystem(): {
46
+ writeFile: (path: string, content: string | Uint8Array, options?: WriteFileOptions) => FileSystemResult;
47
+ appendFile: (path: string, content: string | Uint8Array) => FileSystemResult;
48
+ readFile: {
49
+ (path: string, options?: {
50
+ encoding: "utf8";
51
+ }): string | undefined;
52
+ (path: string, options: {
53
+ encoding: "binary";
54
+ }): Uint8Array | undefined;
55
+ };
56
+ stat: (path: string) => FileMetadata | undefined;
57
+ exists: (path: string) => boolean;
58
+ isFile: (path: string) => boolean;
59
+ isDirectory: (path: string) => boolean;
60
+ remove: (path: string) => FileSystemResult;
61
+ copy: (source: string, destination: string, options?: CopyOptions) => FileSystemResult;
62
+ move: (source: string, destination: string, options?: MoveOptions) => FileSystemResult;
63
+ rename: (oldPath: string, newPath: string) => FileSystemResult;
64
+ mkdir: (path: string) => FileSystemResult;
65
+ rmdir: (path: string) => FileSystemResult;
66
+ rmdirRecursive: (path: string) => FileSystemResult;
67
+ list: (path: string, options?: ListOptions) => readonly DirectoryEntry[];
68
+ statDirectory: (path: string) => DirectoryMetadata | undefined;
69
+ getAllFiles: () => readonly VirtualFile[];
70
+ getAllPaths: () => readonly string[];
71
+ getStats: () => FileSystemStats;
72
+ clear: () => void;
73
+ find: (pattern: string) => readonly string[];
74
+ findByExtension: (extension: string) => readonly string[];
75
+ watch: (callback: FileSystemWatcher) => () => void;
76
+ toJSON: () => Record<string, {
77
+ content: string;
78
+ mimeType: string;
79
+ }>;
80
+ fromJSON: (data: Record<string, {
81
+ content: string;
82
+ mimeType: string;
83
+ }>) => void;
84
+ };
85
+
86
+ /**
87
+ * Creates a ZIP archive from files.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const zipBlob = await createZip([
92
+ * { path: 'readme.txt', content: 'Hello World!' },
93
+ * { path: 'data/config.json', content: JSON.stringify({ enabled: true }) },
94
+ * { path: 'images/logo.png', content: pngBytes }
95
+ * ])
96
+ * ```
97
+ */
98
+ export declare function createZip(files: readonly ZipInput[], options?: CreateZipOptions): Promise<Blob>;
99
+
100
+ /**
101
+ * Options for creating a ZIP archive.
102
+ */
103
+ export declare interface CreateZipOptions extends ZipCompressionOptions {
104
+ /** Comment to include in the ZIP archive */
105
+ readonly comment?: string;
106
+ }
107
+
108
+ /**
109
+ * Decompresses DEFLATE data (native DecompressionStream).
110
+ */
111
+ export declare function decompress(data: Uint8Array): Promise<Uint8Array>;
112
+
113
+ /**
114
+ * Directory entry for listing.
115
+ */
116
+ export declare interface DirectoryEntry {
117
+ readonly name: string;
118
+ readonly path: string;
119
+ readonly type: 'file' | 'directory';
120
+ readonly size?: number;
121
+ }
122
+
123
+ /**
124
+ * Represents directory metadata.
125
+ */
126
+ export declare interface DirectoryMetadata {
127
+ readonly name: string;
128
+ readonly path: string;
129
+ readonly createdAt: number;
130
+ }
131
+
132
+ /**
133
+ * Downloads data as a ZIP file.
134
+ */
135
+ export declare function downloadAsZip(files: readonly ZipInput[], fileName: string, options?: CreateZipOptions): Promise<void>;
136
+
137
+ /**
138
+ * Downloads a blob as a file.
139
+ */
140
+ export declare function downloadBlob(blob: Blob, fileName: string): void;
141
+
142
+ /**
143
+ * Extracts a single file from a ZIP archive.
144
+ */
145
+ export declare function extractFile(zipData: Uint8Array | Blob, filePath: string): Promise<Uint8Array | undefined>;
146
+
147
+ /**
148
+ * Extracts all files from a ZIP archive.
149
+ *
150
+ * @example
151
+ * ```ts
152
+ * const entries = await extractZip(zipBlob)
153
+ * for (const entry of entries) {
154
+ * console.log(entry.path, entry.content.length)
155
+ * }
156
+ * ```
157
+ */
158
+ export declare function extractZip(zipData: Uint8Array | Blob, options?: ExtractZipOptions): Promise<readonly ZipEntry[]>;
159
+
160
+ /**
161
+ * Options for extracting from a ZIP archive.
162
+ */
163
+ export declare interface ExtractZipOptions {
164
+ /** Filter function to select which entries to extract */
165
+ readonly filter?: (entry: ZipEntryMetadata) => boolean;
166
+ }
167
+
168
+ /**
169
+ * Represents file metadata without content.
170
+ */
171
+ export declare interface FileMetadata {
172
+ readonly name: string;
173
+ readonly path: string;
174
+ readonly size: number;
175
+ readonly mimeType: string;
176
+ readonly createdAt: number;
177
+ readonly modifiedAt: number;
178
+ }
179
+
180
+ /**
181
+ * Type for the filesystem instance.
182
+ */
183
+ declare type FileSystem_2 = ReturnType<typeof createFileSystem>;
184
+ export { FileSystem_2 as FileSystem }
185
+
186
+ /**
187
+ * Result of a filesystem operation.
188
+ */
189
+ export declare interface FileSystemResult<T = void> {
190
+ readonly success: boolean;
191
+ readonly data?: T;
192
+ readonly error?: string;
193
+ }
194
+
195
+ /**
196
+ * Filesystem statistics.
197
+ */
198
+ export declare interface FileSystemStats {
199
+ readonly totalFiles: number;
200
+ readonly totalDirectories: number;
201
+ readonly totalSize: number;
202
+ }
203
+
204
+ /**
205
+ * Watcher callback for filesystem changes.
206
+ */
207
+ export declare type FileSystemWatcher = (event: 'create' | 'modify' | 'delete', path: string, type: 'file' | 'directory') => void;
208
+
209
+ /**
210
+ * Options for listing directory contents.
211
+ */
212
+ export declare interface ListOptions {
213
+ readonly recursive?: boolean;
214
+ readonly filesOnly?: boolean;
215
+ readonly directoriesOnly?: boolean;
216
+ }
217
+
218
+ /**
219
+ * Reads entries from a ZIP archive without extracting content.
220
+ */
221
+ export declare function listZip(zipData: Uint8Array | Blob): Promise<readonly ZipEntryMetadata[]>;
222
+
223
+ /**
224
+ * Options for moving files.
225
+ */
226
+ export declare interface MoveOptions {
227
+ readonly overwrite?: boolean;
228
+ }
229
+
230
+ /**
231
+ * Options for reading a file.
232
+ */
233
+ export declare interface ReadFileOptions {
234
+ readonly encoding?: 'utf8' | 'binary';
235
+ }
236
+
237
+ /**
238
+ * Storage adapter interface for persistence.
239
+ */
240
+ export declare interface StorageAdapter {
241
+ save(key: string, data: Uint8Array): Promise<void>;
242
+ load(key: string): Promise<Uint8Array | undefined>;
243
+ remove(key: string): Promise<void>;
244
+ list(): Promise<readonly string[]>;
245
+ clear(): Promise<void>;
246
+ }
247
+
248
+ /**
249
+ * Vue composable wrapper for the virtual filesystem with reactivity.
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * const filesystem = useFileSystem()
254
+ *
255
+ * // Reactive stats
256
+ * console.log(filesystem.fileCount.value)
257
+ * console.log(filesystem.totalSize.value)
258
+ *
259
+ * // All filesystem operations available
260
+ * filesystem.writeFile('/readme.txt', 'Hello!')
261
+ * ```
262
+ */
263
+ export declare function useFileSystem(options?: UseFileSystemOptions): UseFileSystemReturn;
264
+
265
+ /**
266
+ * Options for the useFileSystem composable.
267
+ */
268
+ export declare interface UseFileSystemOptions {
269
+ readonly adapter?: StorageAdapter;
270
+ readonly autoPersist?: boolean;
271
+ }
272
+
273
+ /**
274
+ * Return type for useFileSystem composable.
275
+ */
276
+ export declare interface UseFileSystemReturn extends FileSystem_2 {
277
+ /** Reactive filesystem stats */
278
+ readonly stats: ComputedRef<FileSystemStats>;
279
+ /** Reactive file count */
280
+ readonly fileCount: ComputedRef<number>;
281
+ /** Reactive total size in bytes */
282
+ readonly totalSize: ComputedRef<number>;
283
+ /** Persist to storage adapter */
284
+ persist: () => Promise<void>;
285
+ /** Restore from storage adapter */
286
+ restore: () => Promise<void>;
287
+ }
288
+
289
+ /**
290
+ * Vue composable for ZIP operations with reactive state.
291
+ *
292
+ * @example
293
+ * ```ts
294
+ * const zip = useZip()
295
+ *
296
+ * // Create and download a ZIP
297
+ * await zip.download([
298
+ * { path: 'readme.txt', content: 'Hello!' },
299
+ * { path: 'data.json', content: JSON.stringify({ foo: 'bar' }) }
300
+ * ], 'archive.zip')
301
+ *
302
+ * // Extract a ZIP file
303
+ * const entries = await zip.extract(zipBlob)
304
+ * for (const entry of entries) {
305
+ * console.log(entry.path, new TextDecoder().decode(entry.content))
306
+ * }
307
+ *
308
+ * // Check processing state
309
+ * if (zip.isProcessing.value) {
310
+ * console.log(`Progress: ${zip.progress.value}%`)
311
+ * }
312
+ * ```
313
+ */
314
+ export declare function useZip(): UseZipReturn;
315
+
316
+ /**
317
+ * Return type for useZip composable.
318
+ */
319
+ export declare interface UseZipReturn {
320
+ /** Current operation state */
321
+ readonly state: ComputedRef<ZipOperationState>;
322
+ /** Whether an operation is in progress */
323
+ readonly isProcessing: Ref<boolean>;
324
+ /** Progress percentage (0-100) */
325
+ readonly progress: Ref<number>;
326
+ /** Last error message */
327
+ readonly error: Ref<string | undefined>;
328
+ /** Create a ZIP archive from files */
329
+ create: (files: readonly ZipInput[], options?: CreateZipOptions) => Promise<Blob>;
330
+ /** Extract all files from a ZIP archive */
331
+ extract: (zipData: Uint8Array | Blob, options?: ExtractZipOptions) => Promise<readonly ZipEntry[]>;
332
+ /** Extract a single file from a ZIP archive */
333
+ extractSingle: (zipData: Uint8Array | Blob, filePath: string) => Promise<Uint8Array | undefined>;
334
+ /** List entries in a ZIP archive without extracting */
335
+ list: (zipData: Uint8Array | Blob) => Promise<readonly ZipEntryMetadata[]>;
336
+ /** Download a ZIP archive */
337
+ download: (files: readonly ZipInput[], fileName: string, options?: CreateZipOptions) => Promise<void>;
338
+ /** Download a blob as a file */
339
+ downloadBlob: (blob: Blob, fileName: string) => void;
340
+ /** Compress data using DEFLATE */
341
+ compress: (data: Uint8Array) => Promise<Uint8Array>;
342
+ /** Decompress DEFLATE data */
343
+ decompress: (data: Uint8Array) => Promise<Uint8Array>;
344
+ /** Compute CRC-32 checksum */
345
+ computeCrc32: (data: Uint8Array) => number;
346
+ /** Reset error state */
347
+ clearError: () => void;
348
+ }
349
+
350
+ /**
351
+ * Represents a complete file with content.
352
+ */
353
+ export declare interface VirtualFile extends FileMetadata {
354
+ readonly content: Uint8Array;
355
+ }
356
+
357
+ /**
358
+ * Options for writing a file.
359
+ */
360
+ export declare interface WriteFileOptions {
361
+ readonly mimeType?: string;
362
+ readonly overwrite?: boolean;
363
+ }
364
+
365
+ /**
366
+ * Options for ZIP compression.
367
+ */
368
+ export declare interface ZipCompressionOptions {
369
+ /** Compression level: 'none' stores files, 'fast'/'default' uses deflate */
370
+ readonly level?: 'none' | 'fast' | 'default';
371
+ }
372
+
373
+ /**
374
+ * Represents an entry in a ZIP archive.
375
+ */
376
+ export declare interface ZipEntry {
377
+ readonly path: string;
378
+ readonly content: Uint8Array;
379
+ readonly compressedSize: number;
380
+ readonly uncompressedSize: number;
381
+ readonly compressionMethod: 'store' | 'deflate';
382
+ readonly crc32: number;
383
+ readonly modifiedAt: Date;
384
+ readonly isDirectory: boolean;
385
+ }
386
+
387
+ /**
388
+ * Metadata for a ZIP entry (without content).
389
+ */
390
+ export declare interface ZipEntryMetadata {
391
+ readonly path: string;
392
+ readonly compressedSize: number;
393
+ readonly uncompressedSize: number;
394
+ readonly compressionMethod: 'store' | 'deflate';
395
+ readonly isDirectory: boolean;
396
+ readonly modifiedAt: Date;
397
+ }
398
+
399
+ /**
400
+ * Input for adding files to a ZIP archive.
401
+ */
402
+ export declare interface ZipInput {
403
+ readonly path: string;
404
+ readonly content: Uint8Array | string;
405
+ }
406
+
407
+ /**
408
+ * State for ZIP operations.
409
+ */
410
+ export declare interface ZipOperationState {
411
+ readonly isProcessing: boolean;
412
+ readonly progress: number;
413
+ readonly error: string | undefined;
414
+ }
415
+
416
+ /**
417
+ * Result of a ZIP operation.
418
+ */
419
+ export declare interface ZipResult<T = void> {
420
+ readonly success: boolean;
421
+ readonly data?: T;
422
+ readonly error?: string;
423
+ }
424
+
425
+ export { }
@@ -0,0 +1,573 @@
1
+ import { shallowRef as ue, computed as N, ref as _ } from "vue";
2
+ function fe() {
3
+ const n = {
4
+ name: "",
5
+ path: "/",
6
+ createdAt: Date.now(),
7
+ files: /* @__PURE__ */ new Map(),
8
+ directories: /* @__PURE__ */ new Map()
9
+ }, o = /* @__PURE__ */ new Set();
10
+ function s(e, t, i) {
11
+ for (const r of o)
12
+ r(e, t, i);
13
+ }
14
+ function c(e) {
15
+ const t = e.split(/[/\\]/).filter((r) => r.length > 0 && r !== "."), i = [];
16
+ for (const r of t)
17
+ r === ".." ? i.pop() : i.push(r);
18
+ return "/" + i.join("/");
19
+ }
20
+ function l(e) {
21
+ const t = c(e);
22
+ return { segments: t.split("/").filter((r) => r.length > 0), normalized: t };
23
+ }
24
+ function f(e) {
25
+ const { segments: t } = l(e);
26
+ if (t.length === 0)
27
+ return { parentPath: "/", name: "" };
28
+ const i = t[t.length - 1] ?? "";
29
+ return { parentPath: "/" + t.slice(0, -1).join("/"), name: i };
30
+ }
31
+ function u(e, t = !1) {
32
+ const { segments: i } = l(e);
33
+ let r = n;
34
+ for (const a of i) {
35
+ let d = r.directories.get(a);
36
+ if (!d) {
37
+ if (!t)
38
+ return;
39
+ d = {
40
+ name: a,
41
+ path: r.path === "/" ? `/${a}` : `${r.path}/${a}`,
42
+ createdAt: Date.now(),
43
+ files: /* @__PURE__ */ new Map(),
44
+ directories: /* @__PURE__ */ new Map()
45
+ }, r.directories.set(a, d), s("create", d.path, "directory");
46
+ }
47
+ r = d;
48
+ }
49
+ return r;
50
+ }
51
+ function w(e) {
52
+ return new TextEncoder().encode(e);
53
+ }
54
+ function A(e) {
55
+ return new TextDecoder().decode(e);
56
+ }
57
+ function h(e) {
58
+ const t = e.split(".").pop()?.toLowerCase() ?? "";
59
+ return {
60
+ txt: "text/plain",
61
+ html: "text/html",
62
+ htm: "text/html",
63
+ css: "text/css",
64
+ js: "text/javascript",
65
+ mjs: "text/javascript",
66
+ ts: "text/typescript",
67
+ tsx: "text/typescript",
68
+ json: "application/json",
69
+ xml: "application/xml",
70
+ csv: "text/csv",
71
+ md: "text/markdown",
72
+ yaml: "text/yaml",
73
+ yml: "text/yaml",
74
+ png: "image/png",
75
+ jpg: "image/jpeg",
76
+ jpeg: "image/jpeg",
77
+ gif: "image/gif",
78
+ svg: "image/svg+xml",
79
+ webp: "image/webp",
80
+ ico: "image/x-icon",
81
+ pdf: "application/pdf",
82
+ zip: "application/zip",
83
+ gz: "application/gzip",
84
+ tar: "application/x-tar",
85
+ woff: "font/woff",
86
+ woff2: "font/woff2",
87
+ ttf: "font/ttf",
88
+ otf: "font/otf",
89
+ mp3: "audio/mpeg",
90
+ wav: "audio/wav",
91
+ mp4: "video/mp4",
92
+ webm: "video/webm"
93
+ }[t] ?? "application/octet-stream";
94
+ }
95
+ function x(e) {
96
+ return new Uint8Array(e);
97
+ }
98
+ function p(e, t, i = {}) {
99
+ const { parentPath: r, name: a } = f(e), { normalized: d } = l(e);
100
+ if (!a)
101
+ return { success: !1, error: "Invalid file path: no filename specified" };
102
+ const S = u(r, !0);
103
+ if (!S)
104
+ return { success: !1, error: `Failed to create parent directory: ${r}` };
105
+ const E = S.files.get(a);
106
+ if (E && i.overwrite === !1)
107
+ return { success: !1, error: `File already exists: ${d}` };
108
+ const $ = typeof t == "string" ? w(t) : x(t), H = Date.now(), ae = {
109
+ name: a,
110
+ path: d,
111
+ content: $,
112
+ size: $.length,
113
+ mimeType: i.mimeType ?? h(a),
114
+ createdAt: E?.createdAt ?? H,
115
+ modifiedAt: H
116
+ };
117
+ return S.files.set(a, ae), s(E ? "modify" : "create", d, "file"), { success: !0 };
118
+ }
119
+ function m(e, t) {
120
+ const i = y(e, { encoding: "binary" }), r = typeof t == "string" ? w(t) : t;
121
+ if (i === void 0)
122
+ return p(e, r);
123
+ const a = i, d = new Uint8Array(a.length + r.length);
124
+ return d.set(a, 0), d.set(r, a.length), p(e, d);
125
+ }
126
+ function y(e, t = {}) {
127
+ const { parentPath: i, name: r } = f(e), d = u(i)?.files.get(r);
128
+ return d ? (t.encoding ?? "utf8") === "binary" ? x(d.content) : A(d.content) : void 0;
129
+ }
130
+ function T(e) {
131
+ const { parentPath: t, name: i } = f(e), a = u(t)?.files.get(i);
132
+ if (a)
133
+ return {
134
+ name: a.name,
135
+ path: a.path,
136
+ size: a.size,
137
+ mimeType: a.mimeType,
138
+ createdAt: a.createdAt,
139
+ modifiedAt: a.modifiedAt
140
+ };
141
+ }
142
+ function U(e) {
143
+ return T(e) !== void 0 || b(e);
144
+ }
145
+ function v(e) {
146
+ return T(e) !== void 0;
147
+ }
148
+ function b(e) {
149
+ return u(e) !== void 0;
150
+ }
151
+ function O(e) {
152
+ const { parentPath: t, name: i } = f(e), { normalized: r } = l(e), a = u(t);
153
+ return a ? a.files.has(i) ? (a.files.delete(i), s("delete", r, "file"), { success: !0 }) : { success: !1, error: `File not found: ${r}` } : { success: !1, error: `Parent directory not found: ${t}` };
154
+ }
155
+ function z(e, t, i = {}) {
156
+ const r = y(e, { encoding: "binary" });
157
+ if (r === void 0)
158
+ return { success: !1, error: `Source file not found: ${e}` };
159
+ const a = T(e);
160
+ return p(t, r, {
161
+ mimeType: a?.mimeType,
162
+ overwrite: i.overwrite
163
+ });
164
+ }
165
+ function B(e, t, i = {}) {
166
+ const r = z(e, t, { overwrite: i.overwrite });
167
+ return r.success ? O(e) : r;
168
+ }
169
+ function C(e, t) {
170
+ return B(e, t, { overwrite: !1 });
171
+ }
172
+ function R(e) {
173
+ return u(e, !0) ? { success: !0 } : { success: !1, error: `Failed to create directory: ${e}` };
174
+ }
175
+ function L(e) {
176
+ const { parentPath: t, name: i } = f(e), { normalized: r } = l(e);
177
+ if (r === "/")
178
+ return { success: !1, error: "Cannot remove root directory" };
179
+ const a = u(t);
180
+ if (!a)
181
+ return { success: !1, error: `Parent directory not found: ${t}` };
182
+ const d = a.directories.get(i);
183
+ return d ? d.files.size > 0 || d.directories.size > 0 ? { success: !1, error: `Directory not empty: ${r}` } : (a.directories.delete(i), s("delete", r, "directory"), { success: !0 }) : { success: !1, error: `Directory not found: ${r}` };
184
+ }
185
+ function D(e) {
186
+ const { parentPath: t, name: i } = f(e), { normalized: r } = l(e);
187
+ if (r === "/")
188
+ return { success: !1, error: "Cannot remove root directory" };
189
+ const a = u(t);
190
+ return a ? a.directories.get(i) ? (a.directories.delete(i), s("delete", r, "directory"), { success: !0 }) : { success: !1, error: `Directory not found: ${r}` } : { success: !1, error: `Parent directory not found: ${t}` };
191
+ }
192
+ function F(e, t = {}) {
193
+ const { recursive: i = !1, filesOnly: r = !1, directoriesOnly: a = !1 } = t, d = e === "/" || e === "" ? n : u(e);
194
+ if (!d)
195
+ return [];
196
+ const S = [];
197
+ if (!a)
198
+ for (const E of d.files.values())
199
+ S.push({
200
+ name: E.name,
201
+ path: E.path,
202
+ type: "file",
203
+ size: E.size
204
+ });
205
+ if (!r) {
206
+ for (const E of d.directories.values())
207
+ if (S.push({
208
+ name: E.name,
209
+ path: E.path,
210
+ type: "directory"
211
+ }), i) {
212
+ const $ = F(E.path, t);
213
+ S.push(...$);
214
+ }
215
+ }
216
+ return S;
217
+ }
218
+ function g(e) {
219
+ const t = u(e);
220
+ if (t)
221
+ return {
222
+ name: t.name,
223
+ path: t.path,
224
+ createdAt: t.createdAt
225
+ };
226
+ }
227
+ function P() {
228
+ const e = [];
229
+ function t(i) {
230
+ for (const r of i.files.values())
231
+ e.push(r);
232
+ for (const r of i.directories.values())
233
+ t(r);
234
+ }
235
+ return t(n), e;
236
+ }
237
+ function I() {
238
+ return P().map((e) => e.path);
239
+ }
240
+ function ne() {
241
+ let e = 0, t = 0, i = 0;
242
+ function r(a) {
243
+ for (const d of a.files.values())
244
+ e++, i += d.size;
245
+ for (const d of a.directories.values())
246
+ t++, r(d);
247
+ }
248
+ return r(n), { totalFiles: e, totalDirectories: t, totalSize: i };
249
+ }
250
+ function W() {
251
+ n.files.clear(), n.directories.clear(), s("delete", "/", "directory");
252
+ }
253
+ function re(e) {
254
+ const t = I(), i = e.replace(/\./g, "\\.").replace(/\*\*/g, "<<<GLOBSTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<GLOBSTAR>>>/g, ".*"), r = new RegExp(`^${i}$`);
255
+ return t.filter((a) => r.test(a));
256
+ }
257
+ function oe(e) {
258
+ const t = e.startsWith(".") ? e : `.${e}`;
259
+ return I().filter((i) => i.endsWith(t));
260
+ }
261
+ function se(e) {
262
+ return o.add(e), () => {
263
+ o.delete(e);
264
+ };
265
+ }
266
+ function ie() {
267
+ const e = {};
268
+ for (const t of P()) {
269
+ const i = btoa(
270
+ Array.from(t.content, (r) => String.fromCharCode(r)).join("")
271
+ );
272
+ e[t.path] = {
273
+ content: i,
274
+ mimeType: t.mimeType
275
+ };
276
+ }
277
+ return e;
278
+ }
279
+ function ce(e) {
280
+ W();
281
+ for (const [t, { content: i, mimeType: r }] of Object.entries(e)) {
282
+ const a = atob(i), d = Uint8Array.from(a, (S) => S.charCodeAt(0));
283
+ p(t, d, { mimeType: r });
284
+ }
285
+ }
286
+ return {
287
+ // File operations
288
+ writeFile: p,
289
+ appendFile: m,
290
+ readFile: y,
291
+ stat: T,
292
+ exists: U,
293
+ isFile: v,
294
+ isDirectory: b,
295
+ remove: O,
296
+ copy: z,
297
+ move: B,
298
+ rename: C,
299
+ // Directory operations
300
+ mkdir: R,
301
+ rmdir: L,
302
+ rmdirRecursive: D,
303
+ list: F,
304
+ statDirectory: g,
305
+ // Bulk operations
306
+ getAllFiles: P,
307
+ getAllPaths: I,
308
+ getStats: ne,
309
+ clear: W,
310
+ // Search
311
+ find: re,
312
+ findByExtension: oe,
313
+ // Watchers
314
+ watch: se,
315
+ // Serialization
316
+ toJSON: ie,
317
+ fromJSON: ce
318
+ };
319
+ }
320
+ function he(n = {}) {
321
+ const { adapter: o, autoPersist: s = !1 } = n, c = fe(), l = ue(0);
322
+ function f() {
323
+ l.value++, s && o && h();
324
+ }
325
+ c.watch(() => {
326
+ f();
327
+ });
328
+ const u = N(() => (l.value, c.getStats())), w = N(() => u.value.totalFiles), A = N(() => u.value.totalSize);
329
+ async function h() {
330
+ if (!o)
331
+ throw new Error("No storage adapter configured");
332
+ const p = c.toJSON(), m = new TextEncoder().encode(JSON.stringify(p));
333
+ await o.save("filesystem", m);
334
+ }
335
+ async function x() {
336
+ if (!o)
337
+ throw new Error("No storage adapter configured");
338
+ const p = await o.load("filesystem");
339
+ if (p) {
340
+ const m = JSON.parse(new TextDecoder().decode(p));
341
+ c.fromJSON(m), f();
342
+ }
343
+ }
344
+ return {
345
+ ...c,
346
+ stats: u,
347
+ fileCount: w,
348
+ totalSize: A,
349
+ persist: h,
350
+ restore: x
351
+ };
352
+ }
353
+ const Y = 67324752, G = 33639248, q = 101010256, le = 0, M = 8, de = (function() {
354
+ const o = new Uint32Array(256);
355
+ for (let s = 0; s < 256; s++) {
356
+ let c = s;
357
+ for (let l = 0; l < 8; l++)
358
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
359
+ o[s] = c;
360
+ }
361
+ return o;
362
+ })();
363
+ function K(n) {
364
+ let o = 4294967295;
365
+ for (let s = 0; s < n.length; s++) {
366
+ const c = n[s] ?? 0, l = (o ^ c) & 255;
367
+ o = o >>> 8 ^ (de[l] ?? 0);
368
+ }
369
+ return ~o >>> 0;
370
+ }
371
+ function j(n) {
372
+ const o = new ArrayBuffer(n.byteLength);
373
+ return new Uint8Array(o).set(n), o;
374
+ }
375
+ function V(n) {
376
+ return new TextEncoder().encode(n);
377
+ }
378
+ function Q(n) {
379
+ return new TextDecoder().decode(n);
380
+ }
381
+ async function X(n) {
382
+ const s = new Blob([j(n)]).stream().pipeThrough(new CompressionStream("deflate-raw")), c = await new Response(s).blob();
383
+ return new Uint8Array(await c.arrayBuffer());
384
+ }
385
+ async function ee(n) {
386
+ const s = new Blob([j(n)]).stream().pipeThrough(new DecompressionStream("deflate-raw")), c = await new Response(s).blob();
387
+ return new Uint8Array(await c.arrayBuffer());
388
+ }
389
+ function me(n) {
390
+ const o = n.getHours(), s = n.getMinutes(), c = Math.floor(n.getSeconds() / 2), l = o << 11 | s << 5 | c, f = n.getFullYear() - 1980, u = n.getMonth() + 1, w = n.getDate(), A = f << 9 | u << 5 | w;
391
+ return { time: l, date: A };
392
+ }
393
+ function Z(n, o) {
394
+ const s = (n & 31) * 2, c = n >> 5 & 63, l = n >> 11 & 31, f = o & 31, u = (o >> 5 & 15) - 1, w = (o >> 9 & 127) + 1980;
395
+ return new Date(w, u, f, l, c, s);
396
+ }
397
+ async function J(n, o = {}) {
398
+ const { level: s = "default", comment: c = "" } = o, l = s !== "none", f = [], u = [];
399
+ let w = 0;
400
+ const h = me(/* @__PURE__ */ new Date());
401
+ for (const U of n) {
402
+ const v = V(U.path), b = typeof U.content == "string" ? V(U.content) : U.content, O = K(b), z = b.length, B = l ? await X(b) : b, C = B.length, R = l ? M : le, L = new ArrayBuffer(30), D = new DataView(L);
403
+ D.setUint32(0, Y, !0), D.setUint16(4, 20, !0), D.setUint16(6, 0, !0), D.setUint16(8, R, !0), D.setUint16(10, h.time, !0), D.setUint16(12, h.date, !0), D.setUint32(14, O, !0), D.setUint32(18, C, !0), D.setUint32(22, z, !0), D.setUint16(26, v.length, !0), D.setUint16(28, 0, !0), f.push(new Uint8Array(L), v, B);
404
+ const F = new ArrayBuffer(46), g = new DataView(F);
405
+ g.setUint32(0, G, !0), g.setUint16(4, 20, !0), g.setUint16(6, 20, !0), g.setUint16(8, 0, !0), g.setUint16(10, R, !0), g.setUint16(12, h.time, !0), g.setUint16(14, h.date, !0), g.setUint32(16, O, !0), g.setUint32(20, C, !0), g.setUint32(24, z, !0), g.setUint16(28, v.length, !0), g.setUint16(30, 0, !0), g.setUint16(32, 0, !0), g.setUint16(34, 0, !0), g.setUint16(36, 0, !0), g.setUint32(38, 0, !0), g.setUint32(42, w, !0);
406
+ const P = new Uint8Array(46 + v.length);
407
+ P.set(new Uint8Array(F), 0), P.set(v, 46), u.push(P), w += 30 + v.length + C;
408
+ }
409
+ const x = u.reduce((U, v) => U + v.length, 0), p = V(c), m = new ArrayBuffer(22), y = new DataView(m);
410
+ y.setUint32(0, q, !0), y.setUint16(4, 0, !0), y.setUint16(6, 0, !0), y.setUint16(8, n.length, !0), y.setUint16(10, n.length, !0), y.setUint32(12, x, !0), y.setUint32(16, w, !0), y.setUint16(20, p.length, !0);
411
+ const T = [
412
+ ...f.map((U) => j(U)),
413
+ ...u.map((U) => j(U)),
414
+ m,
415
+ j(p)
416
+ ];
417
+ return new Blob(T, { type: "application/zip" });
418
+ }
419
+ async function pe(n) {
420
+ const o = n instanceof Blob ? new Uint8Array(await n.arrayBuffer()) : n, s = [], c = ge(o);
421
+ if (!c)
422
+ throw new Error("Invalid ZIP file: end of central directory not found");
423
+ const l = new DataView(o.buffer, o.byteOffset, o.byteLength);
424
+ let f = c.centralDirectoryOffset;
425
+ for (let u = 0; u < c.entryCount; u++) {
426
+ if (l.getUint32(f, !0) !== G)
427
+ throw new Error("Invalid ZIP file: invalid central directory entry");
428
+ const A = l.getUint16(f + 10, !0), h = l.getUint16(f + 12, !0), x = l.getUint16(f + 14, !0), p = l.getUint32(f + 20, !0), m = l.getUint32(f + 24, !0), y = l.getUint16(f + 28, !0), T = l.getUint16(f + 30, !0), U = l.getUint16(f + 32, !0), v = o.slice(f + 46, f + 46 + y), b = Q(v);
429
+ s.push({
430
+ path: b,
431
+ compressedSize: p,
432
+ uncompressedSize: m,
433
+ compressionMethod: A === M ? "deflate" : "store",
434
+ isDirectory: b.endsWith("/"),
435
+ modifiedAt: Z(h, x)
436
+ }), f += 46 + y + T + U;
437
+ }
438
+ return s;
439
+ }
440
+ async function te(n, o = {}) {
441
+ const { filter: s } = o, c = n instanceof Blob ? new Uint8Array(await n.arrayBuffer()) : n, l = [], f = new DataView(c.buffer, c.byteOffset, c.byteLength);
442
+ let u = 0;
443
+ for (; u < c.length - 4; ) {
444
+ const w = f.getUint32(u, !0);
445
+ if (w === Y) {
446
+ const A = f.getUint16(u + 8, !0), h = f.getUint16(u + 10, !0), x = f.getUint16(u + 12, !0), p = f.getUint32(u + 14, !0), m = f.getUint32(u + 18, !0), y = f.getUint32(u + 22, !0), T = f.getUint16(u + 26, !0), U = f.getUint16(u + 28, !0), v = c.slice(u + 30, u + 30 + T), b = Q(v), O = u + 30 + T + U, z = c.slice(O, O + m), B = b.endsWith("/"), C = A === M ? "deflate" : "store";
447
+ if (s) {
448
+ const L = {
449
+ path: b,
450
+ compressedSize: m,
451
+ uncompressedSize: y,
452
+ compressionMethod: C,
453
+ isDirectory: B,
454
+ modifiedAt: Z(h, x)
455
+ };
456
+ if (!s(L)) {
457
+ u = O + m;
458
+ continue;
459
+ }
460
+ }
461
+ let R;
462
+ B ? R = new Uint8Array(0) : A === M ? R = await ee(z) : R = new Uint8Array(z), l.push({
463
+ path: b,
464
+ content: R,
465
+ compressedSize: m,
466
+ uncompressedSize: y,
467
+ compressionMethod: C,
468
+ crc32: p,
469
+ modifiedAt: Z(h, x),
470
+ isDirectory: B
471
+ }), u = O + m;
472
+ } else {
473
+ if (w === G)
474
+ break;
475
+ u++;
476
+ }
477
+ }
478
+ return l;
479
+ }
480
+ async function ye(n, o) {
481
+ const s = await te(n, {
482
+ filter: (c) => c.path === o
483
+ });
484
+ return s.length > 0 ? s[0]?.content : void 0;
485
+ }
486
+ function ge(n) {
487
+ const o = new DataView(n.buffer, n.byteOffset, n.byteLength);
488
+ for (let s = n.length - 22; s >= 0; s--)
489
+ if (o.getUint32(s, !0) === q)
490
+ return {
491
+ entryCount: o.getUint16(s + 10, !0),
492
+ centralDirectorySize: o.getUint32(s + 12, !0),
493
+ centralDirectoryOffset: o.getUint32(s + 16, !0)
494
+ };
495
+ }
496
+ function k(n, o) {
497
+ const s = URL.createObjectURL(n), c = document.createElement("a");
498
+ c.href = s, c.download = o, document.body.appendChild(c), c.click(), document.body.removeChild(c), URL.revokeObjectURL(s);
499
+ }
500
+ async function Ue(n, o, s = {}) {
501
+ const c = await J(n, s);
502
+ k(c, o);
503
+ }
504
+ function ve() {
505
+ const n = _(!1), o = _(0), s = _(void 0), c = N(() => ({
506
+ isProcessing: n.value,
507
+ progress: o.value,
508
+ error: s.value
509
+ }));
510
+ async function l(p) {
511
+ n.value = !0, o.value = 0, s.value = void 0;
512
+ try {
513
+ const m = await p();
514
+ return o.value = 100, m;
515
+ } catch (m) {
516
+ const y = m instanceof Error ? m.message : "Unknown error";
517
+ throw s.value = y, m;
518
+ } finally {
519
+ n.value = !1;
520
+ }
521
+ }
522
+ async function f(p, m = {}) {
523
+ return l(() => J(p, m));
524
+ }
525
+ async function u(p, m = {}) {
526
+ return l(() => te(p, m));
527
+ }
528
+ async function w(p, m) {
529
+ return l(() => ye(p, m));
530
+ }
531
+ async function A(p) {
532
+ return l(() => pe(p));
533
+ }
534
+ async function h(p, m, y = {}) {
535
+ return l(async () => {
536
+ const T = await J(p, y);
537
+ k(T, m);
538
+ });
539
+ }
540
+ function x() {
541
+ s.value = void 0;
542
+ }
543
+ return {
544
+ state: c,
545
+ isProcessing: n,
546
+ progress: o,
547
+ error: s,
548
+ create: f,
549
+ extract: u,
550
+ extractSingle: w,
551
+ list: A,
552
+ download: h,
553
+ downloadBlob: k,
554
+ compress: X,
555
+ decompress: ee,
556
+ computeCrc32: K,
557
+ clearError: x
558
+ };
559
+ }
560
+ export {
561
+ X as compress,
562
+ K as computeCrc32,
563
+ fe as createFileSystem,
564
+ J as createZip,
565
+ ee as decompress,
566
+ Ue as downloadAsZip,
567
+ k as downloadBlob,
568
+ ye as extractFile,
569
+ te as extractZip,
570
+ pe as listZip,
571
+ he as useFileSystem,
572
+ ve as useZip
573
+ };
@@ -0,0 +1 @@
1
+ (function(w,E){typeof exports=="object"&&typeof module<"u"?E(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],E):(w=typeof globalThis<"u"?globalThis:w||self,E(w.usefilesystem={},w.Vue))})(this,(function(w,E){"use strict";function K(){const n={name:"",path:"/",createdAt:Date.now(),files:new Map,directories:new Map},o=new Set;function s(e,t,i){for(const r of o)r(e,t,i)}function c(e){const t=e.split(/[/\\]/).filter(r=>r.length>0&&r!=="."),i=[];for(const r of t)r===".."?i.pop():i.push(r);return"/"+i.join("/")}function l(e){const t=c(e);return{segments:t.split("/").filter(r=>r.length>0),normalized:t}}function f(e){const{segments:t}=l(e);if(t.length===0)return{parentPath:"/",name:""};const i=t[t.length-1]??"";return{parentPath:"/"+t.slice(0,-1).join("/"),name:i}}function u(e,t=!1){const{segments:i}=l(e);let r=n;for(const a of i){let d=r.directories.get(a);if(!d){if(!t)return;d={name:a,path:r.path==="/"?`/${a}`:`${r.path}/${a}`,createdAt:Date.now(),files:new Map,directories:new Map},r.directories.set(a,d),s("create",d.path,"directory")}r=d}return r}function h(e){return new TextEncoder().encode(e)}function T(e){return new TextDecoder().decode(e)}function U(e){const t=e.split(".").pop()?.toLowerCase()??"";return{txt:"text/plain",html:"text/html",htm:"text/html",css:"text/css",js:"text/javascript",mjs:"text/javascript",ts:"text/typescript",tsx:"text/typescript",json:"application/json",xml:"application/xml",csv:"text/csv",md:"text/markdown",yaml:"text/yaml",yml:"text/yaml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",webp:"image/webp",ico:"image/x-icon",pdf:"application/pdf",zip:"application/zip",gz:"application/gzip",tar:"application/x-tar",woff:"font/woff",woff2:"font/woff2",ttf:"font/ttf",otf:"font/otf",mp3:"audio/mpeg",wav:"audio/wav",mp4:"video/mp4",webm:"video/webm"}[t]??"application/octet-stream"}function S(e){return new Uint8Array(e)}function p(e,t,i={}){const{parentPath:r,name:a}=f(e),{normalized:d}=l(e);if(!a)return{success:!1,error:"Invalid file path: no filename specified"};const O=u(r,!0);if(!O)return{success:!1,error:`Failed to create parent directory: ${r}`};const B=O.files.get(a);if(B&&i.overwrite===!1)return{success:!1,error:`File already exists: ${d}`};const _=typeof t=="string"?h(t):S(t),oe=Date.now(),he={name:a,path:d,content:_,size:_.length,mimeType:i.mimeType??U(a),createdAt:B?.createdAt??oe,modifiedAt:oe};return O.files.set(a,he),s(B?"modify":"create",d,"file"),{success:!0}}function m(e,t){const i=y(e,{encoding:"binary"}),r=typeof t=="string"?h(t):t;if(i===void 0)return p(e,r);const a=i,d=new Uint8Array(a.length+r.length);return d.set(a,0),d.set(r,a.length),p(e,d)}function y(e,t={}){const{parentPath:i,name:r}=f(e),d=u(i)?.files.get(r);return d?(t.encoding??"utf8")==="binary"?S(d.content):T(d.content):void 0}function D(e){const{parentPath:t,name:i}=f(e),a=u(t)?.files.get(i);if(a)return{name:a.name,path:a.path,size:a.size,mimeType:a.mimeType,createdAt:a.createdAt,modifiedAt:a.modifiedAt}}function v(e){return D(e)!==void 0||A(e)}function b(e){return D(e)!==void 0}function A(e){return u(e)!==void 0}function C(e){const{parentPath:t,name:i}=f(e),{normalized:r}=l(e),a=u(t);return a?a.files.has(i)?(a.files.delete(i),s("delete",r,"file"),{success:!0}):{success:!1,error:`File not found: ${r}`}:{success:!1,error:`Parent directory not found: ${t}`}}function z(e,t,i={}){const r=y(e,{encoding:"binary"});if(r===void 0)return{success:!1,error:`Source file not found: ${e}`};const a=D(e);return p(t,r,{mimeType:a?.mimeType,overwrite:i.overwrite})}function P(e,t,i={}){const r=z(e,t,{overwrite:i.overwrite});return r.success?C(e):r}function F(e,t){return P(e,t,{overwrite:!1})}function R(e){return u(e,!0)?{success:!0}:{success:!1,error:`Failed to create directory: ${e}`}}function j(e){const{parentPath:t,name:i}=f(e),{normalized:r}=l(e);if(r==="/")return{success:!1,error:"Cannot remove root directory"};const a=u(t);if(!a)return{success:!1,error:`Parent directory not found: ${t}`};const d=a.directories.get(i);return d?d.files.size>0||d.directories.size>0?{success:!1,error:`Directory not empty: ${r}`}:(a.directories.delete(i),s("delete",r,"directory"),{success:!0}):{success:!1,error:`Directory not found: ${r}`}}function x(e){const{parentPath:t,name:i}=f(e),{normalized:r}=l(e);if(r==="/")return{success:!1,error:"Cannot remove root directory"};const a=u(t);return a?a.directories.get(i)?(a.directories.delete(i),s("delete",r,"directory"),{success:!0}):{success:!1,error:`Directory not found: ${r}`}:{success:!1,error:`Parent directory not found: ${t}`}}function M(e,t={}){const{recursive:i=!1,filesOnly:r=!1,directoriesOnly:a=!1}=t,d=e==="/"||e===""?n:u(e);if(!d)return[];const O=[];if(!a)for(const B of d.files.values())O.push({name:B.name,path:B.path,type:"file",size:B.size});if(!r){for(const B of d.directories.values())if(O.push({name:B.name,path:B.path,type:"directory"}),i){const _=M(B.path,t);O.push(..._)}}return O}function g(e){const t=u(e);if(t)return{name:t.name,path:t.path,createdAt:t.createdAt}}function L(){const e=[];function t(i){for(const r of i.files.values())e.push(r);for(const r of i.directories.values())t(r)}return t(n),e}function q(){return L().map(e=>e.path)}function de(){let e=0,t=0,i=0;function r(a){for(const d of a.files.values())e++,i+=d.size;for(const d of a.directories.values())t++,r(d)}return r(n),{totalFiles:e,totalDirectories:t,totalSize:i}}function re(){n.files.clear(),n.directories.clear(),s("delete","/","directory")}function me(e){const t=q(),i=e.replace(/\./g,"\\.").replace(/\*\*/g,"<<<GLOBSTAR>>>").replace(/\*/g,"[^/]*").replace(/<<<GLOBSTAR>>>/g,".*"),r=new RegExp(`^${i}$`);return t.filter(a=>r.test(a))}function pe(e){const t=e.startsWith(".")?e:`.${e}`;return q().filter(i=>i.endsWith(t))}function ye(e){return o.add(e),()=>{o.delete(e)}}function ge(){const e={};for(const t of L()){const i=btoa(Array.from(t.content,r=>String.fromCharCode(r)).join(""));e[t.path]={content:i,mimeType:t.mimeType}}return e}function we(e){re();for(const[t,{content:i,mimeType:r}]of Object.entries(e)){const a=atob(i),d=Uint8Array.from(a,O=>O.charCodeAt(0));p(t,d,{mimeType:r})}}return{writeFile:p,appendFile:m,readFile:y,stat:D,exists:v,isFile:b,isDirectory:A,remove:C,copy:z,move:P,rename:F,mkdir:R,rmdir:j,rmdirRecursive:x,list:M,statDirectory:g,getAllFiles:L,getAllPaths:q,getStats:de,clear:re,find:me,findByExtension:pe,watch:ye,toJSON:ge,fromJSON:we}}function se(n={}){const{adapter:o,autoPersist:s=!1}=n,c=K(),l=E.shallowRef(0);function f(){l.value++,s&&o&&U()}c.watch(()=>{f()});const u=E.computed(()=>(l.value,c.getStats())),h=E.computed(()=>u.value.totalFiles),T=E.computed(()=>u.value.totalSize);async function U(){if(!o)throw new Error("No storage adapter configured");const p=c.toJSON(),m=new TextEncoder().encode(JSON.stringify(p));await o.save("filesystem",m)}async function S(){if(!o)throw new Error("No storage adapter configured");const p=await o.load("filesystem");if(p){const m=JSON.parse(new TextDecoder().decode(p));c.fromJSON(m),f()}}return{...c,stats:u,fileCount:h,totalSize:T,persist:U,restore:S}}const Q=67324752,V=33639248,X=101010256,ie=0,N=8,ce=(function(){const o=new Uint32Array(256);for(let s=0;s<256;s++){let c=s;for(let l=0;l<8;l++)c=c&1?3988292384^c>>>1:c>>>1;o[s]=c}return o})();function J(n){let o=4294967295;for(let s=0;s<n.length;s++){const c=n[s]??0,l=(o^c)&255;o=o>>>8^(ce[l]??0)}return~o>>>0}function $(n){const o=new ArrayBuffer(n.byteLength);return new Uint8Array(o).set(n),o}function k(n){return new TextEncoder().encode(n)}function ee(n){return new TextDecoder().decode(n)}async function G(n){const s=new Blob([$(n)]).stream().pipeThrough(new CompressionStream("deflate-raw")),c=await new Response(s).blob();return new Uint8Array(await c.arrayBuffer())}async function W(n){const s=new Blob([$(n)]).stream().pipeThrough(new DecompressionStream("deflate-raw")),c=await new Response(s).blob();return new Uint8Array(await c.arrayBuffer())}function ae(n){const o=n.getHours(),s=n.getMinutes(),c=Math.floor(n.getSeconds()/2),l=o<<11|s<<5|c,f=n.getFullYear()-1980,u=n.getMonth()+1,h=n.getDate(),T=f<<9|u<<5|h;return{time:l,date:T}}function H(n,o){const s=(n&31)*2,c=n>>5&63,l=n>>11&31,f=o&31,u=(o>>5&15)-1,h=(o>>9&127)+1980;return new Date(h,u,f,l,c,s)}async function I(n,o={}){const{level:s="default",comment:c=""}=o,l=s!=="none",f=[],u=[];let h=0;const U=ae(new Date);for(const v of n){const b=k(v.path),A=typeof v.content=="string"?k(v.content):v.content,C=J(A),z=A.length,P=l?await G(A):A,F=P.length,R=l?N:ie,j=new ArrayBuffer(30),x=new DataView(j);x.setUint32(0,Q,!0),x.setUint16(4,20,!0),x.setUint16(6,0,!0),x.setUint16(8,R,!0),x.setUint16(10,U.time,!0),x.setUint16(12,U.date,!0),x.setUint32(14,C,!0),x.setUint32(18,F,!0),x.setUint32(22,z,!0),x.setUint16(26,b.length,!0),x.setUint16(28,0,!0),f.push(new Uint8Array(j),b,P);const M=new ArrayBuffer(46),g=new DataView(M);g.setUint32(0,V,!0),g.setUint16(4,20,!0),g.setUint16(6,20,!0),g.setUint16(8,0,!0),g.setUint16(10,R,!0),g.setUint16(12,U.time,!0),g.setUint16(14,U.date,!0),g.setUint32(16,C,!0),g.setUint32(20,F,!0),g.setUint32(24,z,!0),g.setUint16(28,b.length,!0),g.setUint16(30,0,!0),g.setUint16(32,0,!0),g.setUint16(34,0,!0),g.setUint16(36,0,!0),g.setUint32(38,0,!0),g.setUint32(42,h,!0);const L=new Uint8Array(46+b.length);L.set(new Uint8Array(M),0),L.set(b,46),u.push(L),h+=30+b.length+F}const S=u.reduce((v,b)=>v+b.length,0),p=k(c),m=new ArrayBuffer(22),y=new DataView(m);y.setUint32(0,X,!0),y.setUint16(4,0,!0),y.setUint16(6,0,!0),y.setUint16(8,n.length,!0),y.setUint16(10,n.length,!0),y.setUint32(12,S,!0),y.setUint32(16,h,!0),y.setUint16(20,p.length,!0);const D=[...f.map(v=>$(v)),...u.map(v=>$(v)),m,$(p)];return new Blob(D,{type:"application/zip"})}async function te(n){const o=n instanceof Blob?new Uint8Array(await n.arrayBuffer()):n,s=[],c=ue(o);if(!c)throw new Error("Invalid ZIP file: end of central directory not found");const l=new DataView(o.buffer,o.byteOffset,o.byteLength);let f=c.centralDirectoryOffset;for(let u=0;u<c.entryCount;u++){if(l.getUint32(f,!0)!==V)throw new Error("Invalid ZIP file: invalid central directory entry");const T=l.getUint16(f+10,!0),U=l.getUint16(f+12,!0),S=l.getUint16(f+14,!0),p=l.getUint32(f+20,!0),m=l.getUint32(f+24,!0),y=l.getUint16(f+28,!0),D=l.getUint16(f+30,!0),v=l.getUint16(f+32,!0),b=o.slice(f+46,f+46+y),A=ee(b);s.push({path:A,compressedSize:p,uncompressedSize:m,compressionMethod:T===N?"deflate":"store",isDirectory:A.endsWith("/"),modifiedAt:H(U,S)}),f+=46+y+D+v}return s}async function Y(n,o={}){const{filter:s}=o,c=n instanceof Blob?new Uint8Array(await n.arrayBuffer()):n,l=[],f=new DataView(c.buffer,c.byteOffset,c.byteLength);let u=0;for(;u<c.length-4;){const h=f.getUint32(u,!0);if(h===Q){const T=f.getUint16(u+8,!0),U=f.getUint16(u+10,!0),S=f.getUint16(u+12,!0),p=f.getUint32(u+14,!0),m=f.getUint32(u+18,!0),y=f.getUint32(u+22,!0),D=f.getUint16(u+26,!0),v=f.getUint16(u+28,!0),b=c.slice(u+30,u+30+D),A=ee(b),C=u+30+D+v,z=c.slice(C,C+m),P=A.endsWith("/"),F=T===N?"deflate":"store";if(s){const j={path:A,compressedSize:m,uncompressedSize:y,compressionMethod:F,isDirectory:P,modifiedAt:H(U,S)};if(!s(j)){u=C+m;continue}}let R;P?R=new Uint8Array(0):T===N?R=await W(z):R=new Uint8Array(z),l.push({path:A,content:R,compressedSize:m,uncompressedSize:y,compressionMethod:F,crc32:p,modifiedAt:H(U,S),isDirectory:P}),u=C+m}else{if(h===V)break;u++}}return l}async function ne(n,o){const s=await Y(n,{filter:c=>c.path===o});return s.length>0?s[0]?.content:void 0}function ue(n){const o=new DataView(n.buffer,n.byteOffset,n.byteLength);for(let s=n.length-22;s>=0;s--)if(o.getUint32(s,!0)===X)return{entryCount:o.getUint16(s+10,!0),centralDirectorySize:o.getUint32(s+12,!0),centralDirectoryOffset:o.getUint32(s+16,!0)}}function Z(n,o){const s=URL.createObjectURL(n),c=document.createElement("a");c.href=s,c.download=o,document.body.appendChild(c),c.click(),document.body.removeChild(c),URL.revokeObjectURL(s)}async function fe(n,o,s={}){const c=await I(n,s);Z(c,o)}function le(){const n=E.ref(!1),o=E.ref(0),s=E.ref(void 0),c=E.computed(()=>({isProcessing:n.value,progress:o.value,error:s.value}));async function l(p){n.value=!0,o.value=0,s.value=void 0;try{const m=await p();return o.value=100,m}catch(m){const y=m instanceof Error?m.message:"Unknown error";throw s.value=y,m}finally{n.value=!1}}async function f(p,m={}){return l(()=>I(p,m))}async function u(p,m={}){return l(()=>Y(p,m))}async function h(p,m){return l(()=>ne(p,m))}async function T(p){return l(()=>te(p))}async function U(p,m,y={}){return l(async()=>{const D=await I(p,y);Z(D,m)})}function S(){s.value=void 0}return{state:c,isProcessing:n,progress:o,error:s,create:f,extract:u,extractSingle:h,list:T,download:U,downloadBlob:Z,compress:G,decompress:W,computeCrc32:J,clearError:S}}w.compress=G,w.computeCrc32=J,w.createFileSystem=K,w.createZip=I,w.decompress=W,w.downloadAsZip=fe,w.downloadBlob=Z,w.extractFile=ne,w.extractZip=Y,w.listZip=te,w.useFileSystem=se,w.useZip=le,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@mikestools/usefilesystem",
3
+ "version": "0.0.1",
4
+ "description": "Vue 3 composables for in-memory virtual filesystem and ZIP archive operations",
5
+ "type": "module",
6
+ "main": "./dist/usefilesystem.umd.cjs",
7
+ "module": "./dist/usefilesystem.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/usefilesystem.js",
13
+ "require": "./dist/usefilesystem.umd.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "keywords": [
21
+ "vue",
22
+ "vue3",
23
+ "composables",
24
+ "composition-api",
25
+ "typescript",
26
+ "filesystem",
27
+ "virtual-filesystem",
28
+ "zip",
29
+ "archive",
30
+ "compression",
31
+ "deflate",
32
+ "in-memory"
33
+ ],
34
+ "author": "mikesaintsg",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/mikesaintsg/usefilesystem.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/mikesaintsg/usefilesystem/issues"
42
+ },
43
+ "homepage": "https://github.com/mikesaintsg/usefilesystem#readme",
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "sideEffects": false,
48
+ "peerDependencies": {
49
+ "vue": "^3.3.0"
50
+ },
51
+ "scripts": {
52
+ "dev": "vite --config configs/vite.showcase.config.ts",
53
+ "build": "npm run clean dist && vite build && npm run show",
54
+ "build:showcase": "vite build --config configs/vite.showcase.config.ts",
55
+ "check": "npm run decache && vue-tsc",
56
+ "check:showcase": "npm run decache && vue-tsc -p configs/tsconfig.showcase.json",
57
+ "test": "npm run decache && vitest run --no-cache",
58
+ "format": "npm run decache && eslint . --fix",
59
+ "show": "npm run build:showcase && npm run copy dist/showcase/index.html showcase.html && npm run clean dist/showcase",
60
+ "clean": "node -e \"try{require('fs').rmSync(process.argv[1],{recursive:true,force:true});console.log(`Deleted: ${process.argv[1]}`)}catch(e){throw e}\"",
61
+ "copy": "node -e \"try{require('fs').cpSync(process.argv[1],process.argv[2],{force:true});console.log(`Copied: ${process.argv[1]} to ${process.argv[2]}`)}catch(e){throw e}\"",
62
+ "decache": "node -e \"const fs=require('fs');['.eslintcache','node_modules/.vite','node_modules/.vitest','node_modules/.cache'].forEach(p=>{try{fs.rmSync(p,{recursive:true,force:true});console.log(`Cleared: ${p}`)}catch(e){}})\"",
63
+ "prepublishOnly": "npm run check && npm run format && npm test && npm run show && npm run build"
64
+ },
65
+ "devDependencies": {
66
+ "@eslint/js": "^9.39.1",
67
+ "@mikestools/usebootstrap": "^0.0.5",
68
+ "@mikestools/usetable": "^0.0.1",
69
+ "@mikestools/usetools": "^0.0.10",
70
+ "@popperjs/core": "^2.11.8",
71
+ "@types/bootstrap": "^5.2.10",
72
+ "@types/node": "^24.10.1",
73
+ "@vitejs/plugin-vue": "^6.0.2",
74
+ "@vitest/eslint-plugin": "^1.5.1",
75
+ "@vue/test-utils": "^2.4.6",
76
+ "bootstrap": "^5.3.8",
77
+ "bootstrap-icons": "^1.13.1",
78
+ "eslint": "^9.39.1",
79
+ "eslint-plugin-vue": "^10.6.2",
80
+ "globals": "^16.5.0",
81
+ "happy-dom": "^20.0.11",
82
+ "jiti": "^2.6.1",
83
+ "sass": "^1.94.2",
84
+ "typescript": "^5.9.3",
85
+ "typescript-eslint": "^8.48.1",
86
+ "vite": "^7.2.6",
87
+ "vite-plugin-dts": "^4.5.4",
88
+ "vite-plugin-singlefile": "^2.3.0",
89
+ "vite-svg-loader": "^5.1.0",
90
+ "vitest": "^4.0.15",
91
+ "vue-tsc": "^3.1.6"
92
+ }
93
+ }