opfs-worker 0.2.1 → 0.2.2

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.
@@ -1,83 +0,0 @@
1
- import { OPFSError } from './errors';
2
- export function encodeString(data, encoding = 'utf-8') {
3
- switch (encoding) {
4
- case 'utf8':
5
- case 'utf-8':
6
- return new TextEncoder().encode(data);
7
- case 'utf16le':
8
- case 'ucs2':
9
- case 'ucs-2':
10
- return encodeUtf16LE(data);
11
- case 'ascii':
12
- return encodeAscii(data);
13
- case 'latin1':
14
- return encodeLatin1(data);
15
- case 'binary':
16
- return Uint8Array.from(data, char => char.charCodeAt(0));
17
- case 'base64':
18
- return Uint8Array.from(atob(data), c => c.charCodeAt(0));
19
- case 'hex':
20
- if (!/^[\da-f]+$/i.test(data) || data.length % 2 !== 0) {
21
- throw new OPFSError('Invalid hex string', 'INVALID_HEX_FORMAT');
22
- }
23
- return Uint8Array.from(data.match(/.{1,2}/g).map(b => parseInt(b, 16)));
24
- default:
25
- console.warn('Encoding not supported, falling back to UTF-8');
26
- return new TextEncoder().encode(data);
27
- }
28
- }
29
- export function decodeBuffer(buffer, encoding = 'utf-8') {
30
- switch (encoding) {
31
- case 'utf8':
32
- case 'utf-8':
33
- return new TextDecoder().decode(buffer);
34
- case 'utf16le':
35
- case 'ucs2':
36
- case 'ucs-2':
37
- return decodeUtf16LE(buffer);
38
- case 'latin1':
39
- return String.fromCharCode(...buffer);
40
- case 'binary':
41
- return String.fromCharCode(...buffer);
42
- case 'ascii':
43
- return String.fromCharCode(...buffer.map(b => b & 0x7F));
44
- case 'base64':
45
- return btoa(String.fromCharCode(...buffer));
46
- case 'hex':
47
- return Array.from(buffer).map(b => b.toString(16).padStart(2, '0')).join('');
48
- default:
49
- console.warn('Unsupported encoding, falling back to UTF-8');
50
- return new TextDecoder().decode(buffer);
51
- }
52
- }
53
- function encodeUtf16LE(str) {
54
- const buf = new Uint8Array(str.length * 2);
55
- for (let i = 0; i < str.length; i++) {
56
- const code = str.charCodeAt(i);
57
- buf[(i * 2)] = code & 0xFF;
58
- buf[(i * 2) + 1] = code >> 8;
59
- }
60
- return buf;
61
- }
62
- function decodeUtf16LE(buf) {
63
- if (buf.length % 2 !== 0) {
64
- console.warn('Invalid UTF-16LE buffer length, truncating last byte');
65
- buf = buf.slice(0, buf.length - 1);
66
- }
67
- const codeUnits = new Uint16Array(buf.buffer, buf.byteOffset, buf.byteLength / 2);
68
- return String.fromCharCode(...codeUnits);
69
- }
70
- function encodeLatin1(str) {
71
- const buf = new Uint8Array(str.length);
72
- for (let i = 0; i < str.length; i++) {
73
- buf[i] = str.charCodeAt(i) & 0xFF;
74
- }
75
- return buf;
76
- }
77
- function encodeAscii(str) {
78
- const buf = new Uint8Array(str.length);
79
- for (let i = 0; i < str.length; i++) {
80
- buf[i] = str.charCodeAt(i) & 0x7F;
81
- }
82
- return buf;
83
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * Base error class for all OPFS-related errors
3
- */
4
- export class OPFSError extends Error {
5
- code;
6
- path;
7
- constructor(message, code, path) {
8
- super(message);
9
- this.code = code;
10
- this.path = path;
11
- this.name = 'OPFSError';
12
- }
13
- }
14
- /**
15
- * Error thrown when OPFS is not supported in the current browser
16
- */
17
- export class OPFSNotSupportedError extends OPFSError {
18
- constructor() {
19
- super('OPFS is not supported in this browser', 'OPFS_NOT_SUPPORTED');
20
- }
21
- }
22
- /**
23
- * Error thrown when OPFS is not mounted
24
- */
25
- export class OPFSNotMountedError extends OPFSError {
26
- constructor() {
27
- super('OPFS is not mounted', 'OPFS_NOT_MOUNTED');
28
- }
29
- }
30
- /**
31
- * Error thrown for invalid paths or path traversal attempts
32
- */
33
- export class PathError extends OPFSError {
34
- constructor(message, path) {
35
- super(message, 'INVALID_PATH', path);
36
- }
37
- }
38
- /**
39
- * Error thrown when a requested file doesn't exist
40
- */
41
- export class FileNotFoundError extends OPFSError {
42
- constructor(path) {
43
- super(`File not found: ${path}`, 'FILE_NOT_FOUND', path);
44
- }
45
- }
46
- /**
47
- * Error thrown when a requested directory doesn't exist
48
- */
49
- export class DirectoryNotFoundError extends OPFSError {
50
- constructor(path) {
51
- super(`Directory not found: ${path}`, 'DIRECTORY_NOT_FOUND', path);
52
- }
53
- }
54
- /**
55
- * Error thrown when permission is denied for an operation
56
- */
57
- export class PermissionError extends OPFSError {
58
- constructor(path, operation) {
59
- super(`Permission denied for ${operation} on: ${path}`, 'PERMISSION_DENIED', path);
60
- }
61
- }
62
- /**
63
- * Error thrown when an operation fails due to insufficient storage
64
- */
65
- export class StorageError extends OPFSError {
66
- constructor(message, path) {
67
- super(message, 'STORAGE_ERROR', path);
68
- }
69
- }
70
- /**
71
- * Error thrown when an operation times out
72
- */
73
- export class TimeoutError extends OPFSError {
74
- constructor(operation, path) {
75
- super(`Operation timed out: ${operation}`, 'TIMEOUT_ERROR', path);
76
- }
77
- }
@@ -1,246 +0,0 @@
1
- import { encodeString } from './encoder';
2
- import { OPFSError, OPFSNotSupportedError } from './errors';
3
- /**
4
- * Check if the browser supports the OPFS API
5
- *
6
- * @throws {OPFSNotSupportedError} If the browser does not support the OPFS API
7
- */
8
- export function checkOPFSSupport() {
9
- if (!('storage' in navigator) || !('getDirectory' in navigator.storage)) {
10
- throw new OPFSNotSupportedError();
11
- }
12
- }
13
- /**
14
- * Split a path into an array of segments
15
- *
16
- * @param path - The path to split
17
- * @returns The array of segments
18
- */
19
- export function splitPath(path) {
20
- if (Array.isArray(path)) {
21
- return path;
22
- }
23
- return path.split('/').filter(Boolean);
24
- }
25
- /**
26
- * Join an array of path segments into a single path
27
- *
28
- * @param segments - The array of path segments
29
- * @returns The joined path
30
- */
31
- export function joinPath(segments) {
32
- return typeof segments === 'string'
33
- ? (segments ?? '/')
34
- : `/${segments.join('/')}`;
35
- }
36
- /**
37
- * Extract the filename from a path
38
- *
39
- * @param path - The file path
40
- * @returns The filename without the directory path
41
- *
42
- * @example
43
- * ```typescript
44
- * basename('/path/to/file.txt'); // 'file.txt'
45
- * basename('/path/to/directory/'); // ''
46
- * basename('file.txt'); // 'file.txt'
47
- * ```
48
- */
49
- export function basename(path) {
50
- const segments = splitPath(path);
51
- return segments[segments.length - 1] || '';
52
- }
53
- /**
54
- * Extract the directory path from a file path
55
- *
56
- * @param path - The file path
57
- * @returns The directory path without the filename
58
- *
59
- * @example
60
- * ```typescript
61
- * dirname('/path/to/file.txt'); // '/path/to'
62
- * dirname('/path/to/directory/'); // '/path/to/directory'
63
- * dirname('file.txt'); // '/'
64
- * ```
65
- */
66
- export function dirname(path) {
67
- const segments = splitPath(path);
68
- segments.pop();
69
- return joinPath(segments);
70
- }
71
- /**
72
- * Normalize a path to ensure it starts with '/'
73
- *
74
- * @param path - The path to normalize
75
- * @returns The normalized path
76
- *
77
- * @example
78
- * ```typescript
79
- * normalizePath('path/to/file'); // '/path/to/file'
80
- * normalizePath('/path/to/file'); // '/path/to/file'
81
- * normalizePath(''); // '/'
82
- * ```
83
- */
84
- export function normalizePath(path) {
85
- if (!path || path === '/') {
86
- return '/';
87
- }
88
- return path.startsWith('/') ? path : `/${path}`;
89
- }
90
- /**
91
- * Resolve a path to an absolute path, handling relative segments
92
- *
93
- * @param path - The path to resolve
94
- * @returns The resolved absolute path
95
- *
96
- * @example
97
- * ```typescript
98
- * resolvePath('./config/../data/file.txt'); // '/data/file.txt'
99
- * resolvePath('/path/to/../file.txt'); // '/path/file.txt'
100
- * resolvePath('../../file.txt'); // '/file.txt' (truncated to root)
101
- * ```
102
- */
103
- export function resolvePath(path) {
104
- const segments = splitPath(path);
105
- const normalizedSegments = [];
106
- for (const segment of segments) {
107
- if (segment === '.' || segment === '') {
108
- // Skip current directory references and empty segments
109
- continue;
110
- }
111
- else if (segment === '..') {
112
- if (normalizedSegments.length === 0) {
113
- // Path escapes root, keep at root level
114
- continue;
115
- }
116
- // Go up one directory
117
- normalizedSegments.pop();
118
- }
119
- else {
120
- normalizedSegments.push(segment);
121
- }
122
- }
123
- return joinPath(normalizedSegments);
124
- }
125
- /**
126
- * Get the file extension from a path
127
- *
128
- * @param path - The file path
129
- * @returns The file extension including the dot, or empty string if no extension
130
- *
131
- * @example
132
- * ```typescript
133
- * extname('/path/to/file.txt'); // '.txt'
134
- * extname('/path/to/file'); // ''
135
- * extname('/path/to/file.name.ext'); // '.ext'
136
- * extname('/path/to/.hidden'); // ''
137
- * ```
138
- */
139
- export function extname(path) {
140
- const filename = basename(path);
141
- const lastDotIndex = filename.lastIndexOf('.');
142
- if (lastDotIndex <= 0 || lastDotIndex === filename.length - 1) {
143
- return '';
144
- }
145
- return filename.slice(lastDotIndex);
146
- }
147
- export function createBuffer(data, encoding = 'utf-8') {
148
- if (typeof data === 'string') {
149
- return encodeString(data, encoding);
150
- }
151
- return data instanceof Uint8Array ? data : new Uint8Array(data);
152
- }
153
- /**
154
- * Read raw binary data from a file using a file handle
155
- *
156
- * @param fileHandle - The file handle to read from
157
- * @returns The raw binary data as Uint8Array
158
- */
159
- export async function readFileData(fileHandle) {
160
- const handle = await fileHandle.createSyncAccessHandle();
161
- try {
162
- const size = handle.getSize();
163
- const buffer = new Uint8Array(size);
164
- handle.read(buffer, { at: 0 });
165
- return buffer;
166
- }
167
- finally {
168
- handle.close();
169
- }
170
- }
171
- /**
172
- * Write data to a file using a file handle
173
- *
174
- * @param fileHandle - The file handle to write to
175
- * @param data - The data to write to the file
176
- * @param encoding - The encoding to use
177
- * @param options - Write options (truncate or append)
178
- */
179
- export async function writeFileData(fileHandle, data, encoding, options = {}) {
180
- let handle = null;
181
- try {
182
- handle = await fileHandle.createSyncAccessHandle();
183
- const buffer = createBuffer(data, encoding);
184
- const writeOffset = options.append ? handle.getSize() : 0;
185
- handle.write(buffer, { at: writeOffset });
186
- if (options.truncate && !options.append) {
187
- handle.truncate(buffer.byteLength);
188
- }
189
- handle.flush();
190
- }
191
- catch (error) {
192
- console.error(error);
193
- const operation = options.append ? 'append' : 'write';
194
- throw new OPFSError(`Failed to ${operation} file`, `${operation.toUpperCase()}_FAILED`);
195
- }
196
- finally {
197
- if (handle) {
198
- try {
199
- handle.close();
200
- }
201
- catch { /* ~ */ }
202
- }
203
- }
204
- }
205
- /**
206
- * Calculate file hash using Web Crypto API
207
- *
208
- * @param buffer - The file content as Uint8Array
209
- * @param algorithm - Hash algorithm to use (default: 'SHA-1')
210
- * @returns Promise that resolves to the hash string
211
- */
212
- export async function calculateFileHash(buffer, algorithm = 'SHA-1') {
213
- try {
214
- const bufferSource = new Uint8Array(buffer);
215
- const hashBuffer = await crypto.subtle.digest(algorithm, bufferSource);
216
- const hashArray = Array.from(new Uint8Array(hashBuffer));
217
- return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
218
- }
219
- catch (error) {
220
- console.warn(`Failed to calculate ${algorithm} hash:`, error);
221
- throw error;
222
- }
223
- }
224
- /**
225
- * Convert a Blob to Uint8Array
226
- *
227
- * This function converts a Blob object to a Uint8Array for use with file operations.
228
- * It's useful when working with file uploads or other Blob data sources.
229
- *
230
- * @param blob - The Blob to convert
231
- * @returns Promise that resolves to the Uint8Array representation of the Blob
232
- *
233
- * @example
234
- * ```typescript
235
- * const fileInput = document.getElementById('file') as HTMLInputElement;
236
- * const file = fileInput.files?.[0];
237
- * if (file) {
238
- * const data = await convertBlobToUint8Array(file);
239
- * await fs.writeFile('/uploaded-file', data);
240
- * }
241
- * ```
242
- */
243
- export async function convertBlobToUint8Array(blob) {
244
- const arrayBuffer = await blob.arrayBuffer();
245
- return new Uint8Array(arrayBuffer);
246
- }