opfs-worker 0.1.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.
@@ -0,0 +1,19 @@
1
+ export type Kind = 'file' | 'directory';
2
+ export interface FileStat {
3
+ kind: Kind;
4
+ size: number;
5
+ mtime: string;
6
+ ctime: string;
7
+ isFile: boolean;
8
+ isDirectory: boolean;
9
+ /** Hash of file content (only for files, undefined for directories) */
10
+ hash?: string;
11
+ }
12
+ export interface DirentData {
13
+ name: string;
14
+ kind: 'file' | 'directory';
15
+ isFile: boolean;
16
+ isDirectory: boolean;
17
+ }
18
+ export type * from './opfs.worker';
19
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,WAAW,CAAC;AAExC,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACxB;AAED,mBAAmB,eAAe,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { BufferEncoding } from 'typescript';
2
+ export declare function encodeString(data: string, encoding?: BufferEncoding): Uint8Array;
3
+ export declare function decodeBuffer(buffer: Uint8Array, encoding?: BufferEncoding): string;
4
+ //# sourceMappingURL=encoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../src/utils/encoder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,cAAwB,GAAG,UAAU,CAqCzF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,GAAE,cAAwB,GAAG,MAAM,CAgC3F"}
@@ -0,0 +1,86 @@
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
+ // For binary encoding, treat the string as raw bytes
17
+ // This assumes the string contains raw byte values
18
+ return Uint8Array.from(data, char => char.charCodeAt(0));
19
+ case 'base64':
20
+ return Uint8Array.from(atob(data), c => c.charCodeAt(0));
21
+ case 'hex':
22
+ if (!/^[\da-f]+$/i.test(data) || data.length % 2 !== 0) {
23
+ throw new OPFSError('Invalid hex string', 'INVALID_HEX_FORMAT');
24
+ }
25
+ return Uint8Array.from(data.match(/.{1,2}/g).map(b => parseInt(b, 16)));
26
+ default:
27
+ console.warn('Encoding not supported, falling back to UTF-8');
28
+ return new TextEncoder().encode(data);
29
+ }
30
+ }
31
+ export function decodeBuffer(buffer, encoding = 'utf-8') {
32
+ switch (encoding) {
33
+ case 'utf8':
34
+ case 'utf-8':
35
+ return new TextDecoder().decode(buffer);
36
+ case 'utf16le':
37
+ case 'ucs2':
38
+ case 'ucs-2':
39
+ return decodeUtf16LE(buffer);
40
+ case 'latin1':
41
+ return String.fromCharCode(...buffer);
42
+ case 'binary':
43
+ // For binary encoding, return raw byte values as string
44
+ return String.fromCharCode(...buffer);
45
+ case 'ascii':
46
+ return String.fromCharCode(...buffer.map(b => b & 0x7F));
47
+ case 'base64':
48
+ return btoa(String.fromCharCode(...buffer));
49
+ case 'hex':
50
+ return Array.from(buffer).map(b => b.toString(16).padStart(2, '0')).join('');
51
+ default:
52
+ console.warn('Unsupported encoding, falling back to UTF-8');
53
+ return new TextDecoder().decode(buffer);
54
+ }
55
+ }
56
+ function encodeUtf16LE(str) {
57
+ const buf = new Uint8Array(str.length * 2);
58
+ for (let i = 0; i < str.length; i++) {
59
+ const code = str.charCodeAt(i);
60
+ buf[(i * 2)] = code & 0xFF;
61
+ buf[(i * 2) + 1] = code >> 8;
62
+ }
63
+ return buf;
64
+ }
65
+ function decodeUtf16LE(buf) {
66
+ if (buf.length % 2 !== 0) {
67
+ console.warn('Invalid UTF-16LE buffer length, truncating last byte');
68
+ buf = buf.slice(0, buf.length - 1);
69
+ }
70
+ const codeUnits = new Uint16Array(buf.buffer, buf.byteOffset, buf.byteLength / 2);
71
+ return String.fromCharCode(...codeUnits);
72
+ }
73
+ function encodeLatin1(str) {
74
+ const buf = new Uint8Array(str.length);
75
+ for (let i = 0; i < str.length; i++) {
76
+ buf[i] = str.charCodeAt(i) & 0xFF;
77
+ }
78
+ return buf;
79
+ }
80
+ function encodeAscii(str) {
81
+ const buf = new Uint8Array(str.length);
82
+ for (let i = 0; i < str.length; i++) {
83
+ buf[i] = str.charCodeAt(i) & 0x7F;
84
+ }
85
+ return buf;
86
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Base error class for all OPFS-related errors
3
+ */
4
+ export declare class OPFSError extends Error {
5
+ readonly code: string;
6
+ readonly path?: string | undefined;
7
+ constructor(message: string, code: string, path?: string | undefined);
8
+ }
9
+ /**
10
+ * Error thrown when OPFS is not supported in the current browser
11
+ */
12
+ export declare class OPFSNotSupportedError extends OPFSError {
13
+ constructor();
14
+ }
15
+ /**
16
+ * Error thrown when OPFS is not mounted
17
+ */
18
+ export declare class OPFSNotMountedError extends OPFSError {
19
+ constructor();
20
+ }
21
+ /**
22
+ * Error thrown for invalid paths or path traversal attempts
23
+ */
24
+ export declare class PathError extends OPFSError {
25
+ constructor(message: string, path: string);
26
+ }
27
+ /**
28
+ * Error thrown when a requested file doesn't exist
29
+ */
30
+ export declare class FileNotFoundError extends OPFSError {
31
+ constructor(path: string);
32
+ }
33
+ /**
34
+ * Error thrown when a requested directory doesn't exist
35
+ */
36
+ export declare class DirectoryNotFoundError extends OPFSError {
37
+ constructor(path: string);
38
+ }
39
+ /**
40
+ * Error thrown when permission is denied for an operation
41
+ */
42
+ export declare class PermissionError extends OPFSError {
43
+ constructor(path: string, operation: string);
44
+ }
45
+ /**
46
+ * Error thrown when an operation fails due to insufficient storage
47
+ */
48
+ export declare class StorageError extends OPFSError {
49
+ constructor(message: string, path?: string);
50
+ }
51
+ /**
52
+ * Error thrown when an operation times out
53
+ */
54
+ export declare class TimeoutError extends OPFSError {
55
+ constructor(operation: string, path?: string);
56
+ }
57
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,SAAU,SAAQ,KAAK;aACa,IAAI,EAAE,MAAM;aAAkB,IAAI,CAAC,EAAE,MAAM;gBAA5E,OAAO,EAAE,MAAM,EAAkB,IAAI,EAAE,MAAM,EAAkB,IAAI,CAAC,EAAE,MAAM,YAAA;CAI3F;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,SAAS;;CAInD;AAGD;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;;CAIjD;AAED;;GAEG;AACH,qBAAa,SAAU,SAAQ,SAAS;gBACxB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAG5C;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,SAAS;gBAChC,IAAI,EAAE,MAAM;CAG3B;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;gBACrC,IAAI,EAAE,MAAM;CAG3B;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,SAAS;gBAC9B,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAG9C;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,SAAS;gBAC3B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAG7C;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,SAAS;gBAC3B,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAG/C"}
@@ -0,0 +1,77 @@
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
+ }
@@ -0,0 +1,33 @@
1
+ import type { BufferEncoding } from 'typescript';
2
+ export declare function checkOPFSSupport(): void;
3
+ export declare function splitPath(path: string | string[]): string[];
4
+ export declare function joinPath(segments: string[] | string): string;
5
+ export declare function createBuffer(data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding): Uint8Array;
6
+ /**
7
+ * Read raw binary data from a file using a file handle
8
+ *
9
+ * @param fileHandle - The file handle to read from
10
+ * @returns The raw binary data as Uint8Array
11
+ */
12
+ export declare function readFileData(fileHandle: FileSystemFileHandle): Promise<Uint8Array>;
13
+ /**
14
+ * Write data to a file using a file handle
15
+ *
16
+ * @param fileHandle - The file handle to write to
17
+ * @param data - The data to write to the file
18
+ * @param encoding - The encoding to use
19
+ * @param options - Write options (truncate or append)
20
+ */
21
+ export declare function writeFileData(fileHandle: FileSystemFileHandle, data: string | Uint8Array | ArrayBuffer, encoding?: BufferEncoding, options?: {
22
+ truncate?: boolean;
23
+ append?: boolean;
24
+ }): Promise<void>;
25
+ /**
26
+ * Calculate file hash using Web Crypto API
27
+ *
28
+ * @param buffer - The file content as Uint8Array
29
+ * @param algorithm - Hash algorithm to use (default: 'SHA-1')
30
+ * @returns Promise that resolves to the hash string
31
+ */
32
+ export declare function calculateFileHash(buffer: Uint8Array, algorithm?: string): Promise<string>;
33
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAM3D;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,CAI5D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,EAAE,QAAQ,GAAE,cAAwB,GAAG,UAAU,CAMpH;AAGD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,CAcxF;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAC/B,UAAU,EAAE,oBAAoB,EAChC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,EACvC,QAAQ,CAAC,EAAE,cAAc,EACzB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACvD,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,GAAE,MAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAcxG"}
@@ -0,0 +1,96 @@
1
+ import { encodeString } from './encoder';
2
+ import { OPFSError, OPFSNotSupportedError } from './errors';
3
+ export function checkOPFSSupport() {
4
+ if (!('storage' in navigator) || !('getDirectory' in navigator.storage)) {
5
+ throw new OPFSNotSupportedError();
6
+ }
7
+ }
8
+ export function splitPath(path) {
9
+ if (Array.isArray(path)) {
10
+ return path;
11
+ }
12
+ return path.split('/').filter(Boolean);
13
+ }
14
+ export function joinPath(segments) {
15
+ return typeof segments === 'string'
16
+ ? (segments ?? '/')
17
+ : `/${segments.join('/')}`;
18
+ }
19
+ export function createBuffer(data, encoding = 'utf-8') {
20
+ if (typeof data === 'string') {
21
+ return encodeString(data, encoding);
22
+ }
23
+ return data instanceof Uint8Array ? data : new Uint8Array(data);
24
+ }
25
+ /**
26
+ * Read raw binary data from a file using a file handle
27
+ *
28
+ * @param fileHandle - The file handle to read from
29
+ * @returns The raw binary data as Uint8Array
30
+ */
31
+ export async function readFileData(fileHandle) {
32
+ const handle = await fileHandle.createSyncAccessHandle();
33
+ try {
34
+ const size = handle.getSize();
35
+ const buffer = new Uint8Array(size);
36
+ handle.read(buffer, { at: 0 });
37
+ return buffer;
38
+ }
39
+ finally {
40
+ handle.close();
41
+ }
42
+ }
43
+ /**
44
+ * Write data to a file using a file handle
45
+ *
46
+ * @param fileHandle - The file handle to write to
47
+ * @param data - The data to write to the file
48
+ * @param encoding - The encoding to use
49
+ * @param options - Write options (truncate or append)
50
+ */
51
+ export async function writeFileData(fileHandle, data, encoding, options = {}) {
52
+ let handle = null;
53
+ try {
54
+ handle = await fileHandle.createSyncAccessHandle();
55
+ const buffer = createBuffer(data, encoding);
56
+ const writeOffset = options.append ? handle.getSize() : 0;
57
+ handle.write(buffer, { at: writeOffset });
58
+ if (options.truncate && !options.append) {
59
+ handle.truncate(buffer.byteLength);
60
+ }
61
+ handle.flush();
62
+ }
63
+ catch (error) {
64
+ console.error(error);
65
+ const operation = options.append ? 'append' : 'write';
66
+ throw new OPFSError(`Failed to ${operation} file`, `${operation.toUpperCase()}_FAILED`);
67
+ }
68
+ finally {
69
+ if (handle) {
70
+ try {
71
+ handle.close();
72
+ }
73
+ catch { /* ~ */ }
74
+ }
75
+ }
76
+ }
77
+ /**
78
+ * Calculate file hash using Web Crypto API
79
+ *
80
+ * @param buffer - The file content as Uint8Array
81
+ * @param algorithm - Hash algorithm to use (default: 'SHA-1')
82
+ * @returns Promise that resolves to the hash string
83
+ */
84
+ export async function calculateFileHash(buffer, algorithm = 'SHA-1') {
85
+ try {
86
+ // Ensure buffer is properly typed for crypto.subtle.digest
87
+ const bufferSource = new Uint8Array(buffer);
88
+ const hashBuffer = await crypto.subtle.digest(algorithm, bufferSource);
89
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
90
+ return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
91
+ }
92
+ catch (error) {
93
+ console.warn(`Failed to calculate ${algorithm} hash:`, error);
94
+ throw error;
95
+ }
96
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "opfs-worker",
3
+ "version": "0.1.1",
4
+ "description": "A robust TypeScript library for working with Origin Private File System (OPFS) through Web Workers",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/types.d.ts",
11
+ "import": "./dist/opfs-worker.js",
12
+ "require": "./dist/opfs-worker.cjs"
13
+ },
14
+ "./inline": {
15
+ "types": "./dist/worker/inline-worker.d.ts",
16
+ "import": "./dist/opfs-worker-inline.js",
17
+ "require": "./dist/opfs-worker-inline.cjs"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist/**/*",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "build": "vite build && tsc -p tsconfig.build.json",
27
+ "dev": "vite serve demo",
28
+ "preview": "vite preview demo",
29
+ "type-check": "tsc --noEmit",
30
+ "lint": "eslint src --ext .ts,.tsx",
31
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
32
+ "test": "vitest",
33
+ "test:coverage": "vitest --coverage",
34
+ "prepublishOnly": "npm run build",
35
+ "version": "changeset && changeset version",
36
+ "release": "changeset publish"
37
+ },
38
+ "keywords": [
39
+ "opfs",
40
+ "origin-private-file-system",
41
+ "web-worker",
42
+ "filesystem",
43
+ "browser-storage",
44
+ "file-api",
45
+ "typescript"
46
+ ],
47
+ "author": "Your Name <your.email@example.com>",
48
+ "license": "MIT",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/your-username/opfs-worker.git"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/your-username/opfs-worker/issues"
55
+ },
56
+ "homepage": "https://github.com/your-username/opfs-worker#readme",
57
+ "devDependencies": {
58
+ "@changesets/cli": "^2.29.5",
59
+ "@flexbe/eslint-config": "^1.0.11",
60
+ "@types/node": "^20.19.9",
61
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
62
+ "@typescript-eslint/parser": "^7.18.0",
63
+ "@vitest/coverage-v8": "^1.6.1",
64
+ "eslint": "^8.57.1",
65
+ "happy-dom": "^13.10.1",
66
+ "typescript": "^5.9.2",
67
+ "vite": "^7.0.6",
68
+ "vitest": "^1.6.1"
69
+ },
70
+ "engines": {
71
+ "node": ">=20.0.0"
72
+ },
73
+ "peerDependencies": {
74
+ "typescript": ">=5.0.0"
75
+ },
76
+ "packageManager": "bun@1.1.29",
77
+ "dependencies": {
78
+ "comlink": "^4.4.2"
79
+ },
80
+ "publishConfig": {
81
+ "access": "public"
82
+ }
83
+ }