@statewalker/webrun-files 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/cjs/index.cjs +564 -0
  2. package/dist/esm/index.js +551 -0
  3. package/dist/files-api.d.ts +44 -0
  4. package/dist/files-api.d.ts.map +1 -0
  5. package/dist/impl/index.d.ts +6 -0
  6. package/dist/impl/index.d.ts.map +1 -0
  7. package/dist/impl/mem-files-api.d.ts +13 -0
  8. package/dist/impl/mem-files-api.d.ts.map +1 -0
  9. package/dist/impl/node-files-api.d.ts +24 -0
  10. package/dist/impl/node-files-api.d.ts.map +1 -0
  11. package/dist/index.d.ts +11 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/types.d.ts +57 -0
  14. package/dist/types.d.ts.map +1 -0
  15. package/dist/utils/collect-stream.d.ts +16 -0
  16. package/dist/utils/collect-stream.d.ts.map +1 -0
  17. package/dist/utils/index.d.ts +7 -0
  18. package/dist/utils/index.d.ts.map +1 -0
  19. package/dist/utils/normalize-path.d.ts +21 -0
  20. package/dist/utils/normalize-path.d.ts.map +1 -0
  21. package/dist/utils/path-utils.d.ts +20 -0
  22. package/dist/utils/path-utils.d.ts.map +1 -0
  23. package/package.json +29 -31
  24. package/src/files-api.ts +167 -0
  25. package/src/impl/index.ts +6 -0
  26. package/src/impl/mem-files-api.ts +306 -0
  27. package/src/impl/node-files-api.ts +261 -0
  28. package/src/index.ts +35 -0
  29. package/src/types.ts +68 -0
  30. package/src/utils/collect-stream.ts +35 -0
  31. package/src/utils/index.ts +7 -0
  32. package/src/utils/normalize-path.ts +34 -0
  33. package/src/utils/path-utils.ts +46 -0
  34. package/CHANGELOG.md +0 -2
  35. package/README.md +0 -17
  36. package/dist/index-umd.js +0 -1053
  37. package/dist/index-umd.min.js +0 -2
  38. package/dist/index.js +0 -1032
  39. package/dist/index.min.js +0 -2
  40. package/index.js +0 -1
  41. package/src/BrowserFilesApi.js +0 -335
  42. package/src/FilesApi.js +0 -133
  43. package/src/MemFilesApi.js +0 -129
  44. package/src/NodeFilesApi.js +0 -222
  45. package/src/addFilesApiLogger.js +0 -25
  46. package/src/getMimeType.js +0 -19
  47. package/src/index.js +0 -10
  48. package/src/mimeTypes.js +0 -96
  49. package/src/normalizePath.js +0 -8
  50. package/src/notImplemented.js +0 -3
  51. package/src/openBrowserFilesApi.js +0 -67
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Type definitions for IFilesApi
3
+ */
4
+ export type FileKind = "file" | "directory";
5
+ export interface FileInfo {
6
+ kind: FileKind;
7
+ name: string;
8
+ path: string;
9
+ type?: string;
10
+ size?: number;
11
+ lastModified: number;
12
+ }
13
+ export type FileRef = string | {
14
+ path: string;
15
+ };
16
+ export interface ListOptions {
17
+ recursive?: boolean;
18
+ }
19
+ export interface CopyOptions {
20
+ recursive?: boolean;
21
+ }
22
+ export interface AppendOptions {
23
+ signal?: AbortSignal;
24
+ }
25
+ export interface ReadStreamOptions {
26
+ start?: number;
27
+ end?: number;
28
+ signal?: AbortSignal;
29
+ }
30
+ export interface WriteStreamOptions {
31
+ start?: number;
32
+ signal?: AbortSignal;
33
+ }
34
+ export type BinaryStream = AsyncIterable<Uint8Array> | Iterable<Uint8Array>;
35
+ export interface FileHandle {
36
+ readonly size: number;
37
+ close(): Promise<void>;
38
+ appendFile(data: BinaryStream, options?: AppendOptions): Promise<number>;
39
+ createReadStream(options?: ReadStreamOptions): AsyncGenerator<Uint8Array>;
40
+ createWriteStream(data: BinaryStream, options?: WriteStreamOptions): Promise<number>;
41
+ chmod?(mode: number): Promise<void>;
42
+ chown?(uid: number, gid: number): Promise<void>;
43
+ }
44
+ /**
45
+ * Core filesystem interface - minimal contract for implementations.
46
+ * Platform-specific implementations should implement this interface.
47
+ */
48
+ export interface IFilesApi {
49
+ list(file: FileRef, options?: ListOptions): AsyncGenerator<FileInfo>;
50
+ stats(file: FileRef): Promise<FileInfo | undefined>;
51
+ remove(file: FileRef): Promise<boolean>;
52
+ open(file: FileRef): Promise<FileHandle>;
53
+ move?(source: FileRef, target: FileRef): Promise<boolean>;
54
+ copy?(source: FileRef, target: FileRef, options?: CopyOptions): Promise<boolean>;
55
+ mkdir?(file: FileRef): Promise<void>;
56
+ }
57
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE5C,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;AAE5E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,gBAAgB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC1E,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAErF,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrE,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IACpD,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjF,KAAK,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Stream collection utilities
3
+ */
4
+ /**
5
+ * Collects all values from an async generator into an array.
6
+ */
7
+ export declare function collectGenerator<T>(generator: AsyncIterable<T>): Promise<T[]>;
8
+ /**
9
+ * Converts a synchronous iterable to an async iterable.
10
+ */
11
+ export declare function toAsyncIterable<T>(iterable: Iterable<T>): AsyncIterable<T>;
12
+ /**
13
+ * Normalizes BinaryStream to AsyncIterable<Uint8Array>.
14
+ */
15
+ export declare function toBinaryAsyncIterable(data: AsyncIterable<Uint8Array> | Iterable<Uint8Array>): AsyncIterable<Uint8Array>;
16
+ //# sourceMappingURL=collect-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-stream.d.ts","sourceRoot":"","sources":["../../src/utils/collect-stream.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAMnF;AAED;;GAEG;AACH,wBAAuB,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAIjF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,GACrD,aAAa,CAAC,UAAU,CAAC,CAK3B"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Utility functions
3
+ */
4
+ export * from "./collect-stream.js";
5
+ export * from "./normalize-path.js";
6
+ export * from "./path-utils.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Path normalization utilities
3
+ */
4
+ import type { FileRef } from "../types.js";
5
+ /**
6
+ * Normalizes a file path to a consistent format.
7
+ * - Adds leading slash if missing
8
+ * - Removes trailing slash
9
+ * - Removes `.` segments
10
+ * - Collapses multiple slashes
11
+ */
12
+ export declare function normalizePath(filePath: string): string;
13
+ /**
14
+ * Extracts path string from FileRef.
15
+ */
16
+ export declare function toPath(file: FileRef): string;
17
+ /**
18
+ * Normalizes FileRef to a path string.
19
+ */
20
+ export declare function resolveFileRef(file: FileRef): string;
21
+ //# sourceMappingURL=normalize-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-path.d.ts","sourceRoot":"","sources":["../../src/utils/normalize-path.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Path manipulation utilities
3
+ */
4
+ /**
5
+ * Joins path segments with proper normalization.
6
+ */
7
+ export declare function joinPath(...segments: string[]): string;
8
+ /**
9
+ * Returns the directory portion of a path.
10
+ */
11
+ export declare function dirname(path: string): string;
12
+ /**
13
+ * Returns the filename portion of a path.
14
+ */
15
+ export declare function basename(path: string, ext?: string): string;
16
+ /**
17
+ * Returns the file extension including the dot.
18
+ */
19
+ export declare function extname(path: string): string;
20
+ //# sourceMappingURL=path-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/utils/path-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C"}
package/package.json CHANGED
@@ -1,49 +1,47 @@
1
1
  {
2
2
  "name": "@statewalker/webrun-files",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
+ "private": false,
5
+ "type": "module",
4
6
  "description": "Basic files API",
5
- "keywords": [],
6
7
  "homepage": "https://github.com/statewalker/webrun-files",
7
8
  "author": {
8
9
  "name": "Mikhail Kotelnikov",
9
10
  "email": "mikhail.kotelnikov@gmail.com"
10
11
  },
11
12
  "license": "MIT",
12
- "type": "module",
13
- "files": [
14
- "dist/**/package.json",
15
- "dist/**/*.js",
16
- "src/**/*.js",
17
- "index.js"
18
- ],
19
- "module": "src/index.js",
20
- "main": "src/index.js",
21
- "jsdelivr": "dist/index.js",
22
- "unpkg": "dist/index.js",
23
- "exports": {
24
- "umd": "./dist/index-umd.min.js",
25
- "default": "./src/index.js"
26
- },
27
- "devDependencies": {
28
- "@statewalker/rollup": "^0.1.6",
29
- "@statewalker/webrun-files-tests": "^0.1.0",
30
- "eslint": "^8",
31
- "expect.js": "^0.3",
32
- "mocha": "^10",
33
- "rollup": "^3"
34
- },
35
13
  "repository": {
36
14
  "type": "git",
37
15
  "url": "git@github.com:statewalker/webrun-files.git"
38
16
  },
39
- "scripts": {
40
- "eslint": "eslint src",
41
- "rollup": "rollup -c",
42
- "test": "mocha -R spec ./test/index.js",
43
- "prepublishOnly": "rm -rf dist && yarn test && yarn rollup"
17
+ "main": "./dist/cjs/index.cjs",
18
+ "module": "./dist/esm/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/esm/index.js",
24
+ "require": "./dist/cjs/index.cjs"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "src"
30
+ ],
31
+ "devDependencies": {
32
+ "@types/node": "^24.10.1",
33
+ "rimraf": "^6.1.2",
34
+ "rolldown": "^1.0.0-beta.53",
35
+ "typescript": "^5.6.0",
36
+ "vitest": "^4.0.12"
44
37
  },
45
38
  "sideEffects": false,
46
39
  "publishConfig": {
47
40
  "access": "public"
41
+ },
42
+ "scripts": {
43
+ "build": "rimraf dist && rolldown -c && tsc --emitDeclarationOnly --declaration",
44
+ "test": "vitest run",
45
+ "lint": "biome lint src tests"
48
46
  }
49
- }
47
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * FilesApi wrapper class providing convenience methods on top of IFilesApi
3
+ */
4
+
5
+ import type {
6
+ BinaryStream,
7
+ CopyOptions,
8
+ FileHandle,
9
+ FileInfo,
10
+ FileRef,
11
+ IFilesApi,
12
+ ListOptions,
13
+ ReadStreamOptions,
14
+ } from "./types.js";
15
+ import { toBinaryAsyncIterable } from "./utils/collect-stream.js";
16
+ import { joinPath, resolveFileRef } from "./utils/index.js";
17
+
18
+ const IGNORE_FILE = ".gitkeep";
19
+
20
+ export class FilesApi implements IFilesApi {
21
+ constructor(private fs: IFilesApi) {}
22
+
23
+ // ========================================
24
+ // IFilesApi delegation
25
+ // ========================================
26
+
27
+ list(file: FileRef, options?: ListOptions): AsyncGenerator<FileInfo> {
28
+ return this.fs.list(file, options);
29
+ }
30
+
31
+ stats(file: FileRef): Promise<FileInfo | undefined> {
32
+ return this.fs.stats(file);
33
+ }
34
+
35
+ remove(file: FileRef): Promise<boolean> {
36
+ return this.fs.remove(file);
37
+ }
38
+
39
+ open(file: FileRef): Promise<FileHandle> {
40
+ return this.fs.open(file);
41
+ }
42
+
43
+ // ========================================
44
+ // Convenience methods built on core API
45
+ // ========================================
46
+
47
+ /**
48
+ * Checks if a file or directory exists.
49
+ */
50
+ async exists(file: FileRef): Promise<boolean> {
51
+ const info = await this.stats(file);
52
+ return info !== undefined;
53
+ }
54
+
55
+ /**
56
+ * Reads file content as a stream.
57
+ */
58
+ async *read(file: FileRef, options: ReadStreamOptions = {}): AsyncGenerator<Uint8Array> {
59
+ const handle = await this.open(file);
60
+ try {
61
+ yield* handle.createReadStream(options);
62
+ } finally {
63
+ await handle.close();
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Writes content to a file.
69
+ */
70
+ async write(file: FileRef, content: BinaryStream): Promise<void> {
71
+ const handle = await this.open(file);
72
+ try {
73
+ await handle.createWriteStream(toBinaryAsyncIterable(content));
74
+ } finally {
75
+ await handle.close();
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Reads entire file into a single buffer.
81
+ */
82
+ async readFile(file: FileRef): Promise<Uint8Array> {
83
+ const chunks: Uint8Array[] = [];
84
+ let totalLength = 0;
85
+
86
+ for await (const chunk of this.read(file)) {
87
+ chunks.push(chunk);
88
+ totalLength += chunk.length;
89
+ }
90
+
91
+ if (chunks.length === 0) return new Uint8Array(0);
92
+ if (chunks.length === 1) return chunks[0];
93
+
94
+ const result = new Uint8Array(totalLength);
95
+ let offset = 0;
96
+ for (const chunk of chunks) {
97
+ result.set(chunk, offset);
98
+ offset += chunk.length;
99
+ }
100
+ return result;
101
+ }
102
+
103
+ /**
104
+ * Copies file or directory.
105
+ * Uses native implementation if available, otherwise falls back to read/write.
106
+ */
107
+ async copy(source: FileRef, target: FileRef, options: CopyOptions = {}): Promise<boolean> {
108
+ // Try native implementation first
109
+ if (this.fs.copy) {
110
+ return this.fs.copy(source, target, options);
111
+ }
112
+
113
+ // Fallback: read from source, write to target
114
+ const sourceInfo = await this.stats(source);
115
+ if (!sourceInfo) return false;
116
+
117
+ const sourcePath = resolveFileRef(source);
118
+ const targetPath = resolveFileRef(target);
119
+ const recursive = options.recursive ?? true;
120
+
121
+ if (sourceInfo.kind === "directory") {
122
+ for await (const entry of this.list(source, { recursive })) {
123
+ if (entry.kind === "directory") continue;
124
+ const suffix = entry.path.substring(sourcePath.length);
125
+ const newTargetPath = targetPath + suffix;
126
+ await this.write(newTargetPath, this.read(entry.path));
127
+ }
128
+ } else {
129
+ await this.write(targetPath, this.read(sourcePath));
130
+ }
131
+
132
+ return true;
133
+ }
134
+
135
+ /**
136
+ * Moves file or directory.
137
+ * Uses native implementation if available, otherwise falls back to copy+delete.
138
+ */
139
+ async move(source: FileRef, target: FileRef): Promise<boolean> {
140
+ // Try native implementation first
141
+ if (this.fs.move) {
142
+ return this.fs.move(source, target);
143
+ }
144
+
145
+ // Fallback: copy then delete
146
+ const copied = await this.copy(source, target);
147
+ if (!copied) return false;
148
+ await this.remove(source);
149
+ return true;
150
+ }
151
+
152
+ /**
153
+ * Creates a directory.
154
+ * Uses native implementation if available, otherwise creates a placeholder file.
155
+ */
156
+ async mkdir(file: FileRef): Promise<void> {
157
+ // Try native implementation first
158
+ if (this.fs.mkdir) {
159
+ return this.fs.mkdir(file);
160
+ }
161
+
162
+ // Fallback: create a placeholder file to establish the directory
163
+ const dirPath = resolveFileRef(file);
164
+ const placeholderPath = joinPath(dirPath, IGNORE_FILE);
165
+ await this.write(placeholderPath, [new Uint8Array(0)]);
166
+ }
167
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * IFilesApi implementations
3
+ */
4
+
5
+ export * from "./mem-files-api.js";
6
+ export * from "./node-files-api.js";