@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 +187 -0
- package/dist/index.d.ts +425 -0
- package/dist/usefilesystem.js +573 -0
- package/dist/usefilesystem.umd.cjs +1 -0
- package/package.json +93 -0
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
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|