@statewalker/webrun-files 0.5.0 → 0.7.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 (42) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +93 -113
  3. package/dist/cjs/index.cjs +84 -526
  4. package/dist/esm/index.js +78 -517
  5. package/dist/file-utils.d.ts +47 -0
  6. package/dist/file-utils.d.ts.map +1 -0
  7. package/dist/index.d.ts +14 -8
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/{utils/path-utils.d.ts → path-utils.d.ts} +10 -1
  10. package/dist/path-utils.d.ts.map +1 -0
  11. package/dist/types.d.ts +90 -54
  12. package/dist/types.d.ts.map +1 -1
  13. package/package.json +4 -7
  14. package/src/file-utils.ts +106 -0
  15. package/src/index.ts +33 -27
  16. package/src/{utils/path-utils.ts → path-utils.ts} +16 -2
  17. package/src/types.ts +90 -53
  18. package/dist/files-api.d.ts +0 -44
  19. package/dist/files-api.d.ts.map +0 -1
  20. package/dist/impl/index.d.ts +0 -6
  21. package/dist/impl/index.d.ts.map +0 -1
  22. package/dist/impl/mem-files-api.d.ts +0 -13
  23. package/dist/impl/mem-files-api.d.ts.map +0 -1
  24. package/dist/impl/node-files-api.d.ts +0 -24
  25. package/dist/impl/node-files-api.d.ts.map +0 -1
  26. package/dist/utils/collect-stream.d.ts +0 -16
  27. package/dist/utils/collect-stream.d.ts.map +0 -1
  28. package/dist/utils/file-info-utils.d.ts +0 -10
  29. package/dist/utils/file-info-utils.d.ts.map +0 -1
  30. package/dist/utils/index.d.ts +0 -8
  31. package/dist/utils/index.d.ts.map +0 -1
  32. package/dist/utils/normalize-path.d.ts +0 -21
  33. package/dist/utils/normalize-path.d.ts.map +0 -1
  34. package/dist/utils/path-utils.d.ts.map +0 -1
  35. package/src/files-api.ts +0 -168
  36. package/src/impl/index.ts +0 -6
  37. package/src/impl/mem-files-api.ts +0 -323
  38. package/src/impl/node-files-api.ts +0 -271
  39. package/src/utils/collect-stream.ts +0 -35
  40. package/src/utils/file-info-utils.ts +0 -15
  41. package/src/utils/index.ts +0 -8
  42. package/src/utils/normalize-path.ts +0 -34
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 statewalker
3
+ Copyright (c) 2022-2026 statewalker
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,78 +1,125 @@
1
1
  # @statewalker/webrun-files
2
2
 
3
- The core filesystem API for cross-platform file operations. This package provides a clean, minimal interface for reading, writing, and managing files that works consistently whether you're running in Node.js or need an in-memory implementation for browsers and testing.
3
+ Core types and utilities for cross-platform file operations. This package defines the `FilesApi` interface that all storage backends implement, plus utility functions for common file operations.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
8
  npm install @statewalker/webrun-files
9
- # or
10
- pnpm add @statewalker/webrun-files
11
9
  ```
12
10
 
13
- ## Quick Start
11
+ For actual filesystem implementations, install one of these packages:
12
+ - `@statewalker/webrun-files-mem` - In-memory storage
13
+ - `@statewalker/webrun-files-node` - Node.js filesystem
14
+ - `@statewalker/webrun-files-browser` - Browser File System Access API
15
+ - `@statewalker/webrun-files-s3` - AWS S3 / S3-compatible storage
16
+
17
+ ## The FilesApi Interface
14
18
 
15
- The fastest way to get going is to create a `FilesApi` instance with one of the built-in backends. Here's an example using the in-memory implementation:
19
+ All implementations provide this interface:
16
20
 
17
21
  ```typescript
18
- import { FilesApi, MemFilesApi } from '@statewalker/webrun-files';
22
+ interface FilesApi {
23
+ // Read file content as async iterable of chunks
24
+ read(path: string, options?: ReadOptions): AsyncIterable<Uint8Array>;
25
+
26
+ // Write content to file (creates parent directories)
27
+ write(path: string, content: Iterable<Uint8Array> | AsyncIterable<Uint8Array>): Promise<void>;
28
+
29
+ // Create directory (and parents)
30
+ mkdir(path: string): Promise<void>;
31
+
32
+ // List directory contents
33
+ list(path: string, options?: ListOptions): AsyncIterable<FileInfo>;
34
+
35
+ // Get file/directory metadata
36
+ stats(path: string): Promise<FileStats | undefined>;
37
+
38
+ // Check if path exists
39
+ exists(path: string): Promise<boolean>;
40
+
41
+ // Remove file or directory (recursively)
42
+ remove(path: string): Promise<boolean>;
19
43
 
20
- const files = new FilesApi(new MemFilesApi());
44
+ // Move/rename file or directory
45
+ move(source: string, target: string): Promise<boolean>;
46
+
47
+ // Copy file or directory (recursively)
48
+ copy(source: string, target: string): Promise<boolean>;
49
+ }
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ```typescript
55
+ import { MemFilesApi } from '@statewalker/webrun-files-mem';
56
+ import { readFile, writeText } from '@statewalker/webrun-files';
57
+
58
+ const files = new MemFilesApi();
21
59
 
22
60
  // Write a file
23
- await files.write('/documents/notes.txt', [
24
- new TextEncoder().encode('Remember to water the plants')
25
- ]);
61
+ await writeText(files, '/documents/notes.txt', 'Remember to water the plants');
26
62
 
27
63
  // Read it back
28
- const content = await files.readFile('/documents/notes.txt');
64
+ const content = await readFile(files, '/documents/notes.txt');
29
65
  console.log(new TextDecoder().decode(content));
30
66
  ```
31
67
 
32
- For Node.js applications that need to work with the actual filesystem, switch to `NodeFilesApi`:
68
+ ## Utility Functions
69
+
70
+ ### Reading Files
33
71
 
34
72
  ```typescript
35
- import { FilesApi, NodeFilesApi } from '@statewalker/webrun-files';
73
+ import { readFile, readText, tryReadFile, tryReadText, readRange, readAt } from '@statewalker/webrun-files';
36
74
 
37
- const files = new FilesApi(new NodeFilesApi('/path/to/root'));
38
- ```
75
+ // Read entire file as Uint8Array
76
+ const content = await readFile(files, '/data.bin');
39
77
 
40
- The `NodeFilesApi` constructor accepts an optional root directory path. When provided, all file operations will be relative to this directory, effectively sandboxing the API.
78
+ // Read as UTF-8 string
79
+ const text = await readText(files, '/config.json');
41
80
 
42
- ## Working with Files
81
+ // Read with undefined return for missing files
82
+ const maybeContent = await tryReadFile(files, '/optional.txt');
83
+ const maybeText = await tryReadText(files, '/optional.txt');
43
84
 
44
- The `FilesApi` class provides everything you need for common file operations. Writing content is straightforward—just pass an array of `Uint8Array` chunks:
85
+ // Read a specific byte range
86
+ const chunk = await readRange(files, '/large.bin', 1000, 500); // 500 bytes starting at position 1000
45
87
 
46
- ```typescript
47
- const text = new TextEncoder().encode('Hello, World!');
48
- await files.write('/greeting.txt', [text]);
88
+ // Read into a buffer at specific offset (like fs.read)
89
+ const buffer = new Uint8Array(100);
90
+ const bytesRead = await readAt(files, '/data.bin', buffer, 0, 100, 500);
49
91
  ```
50
92
 
51
- Reading comes in two flavors. Use `readFile` when you want the entire contents as a single buffer:
93
+ ### Writing Files
52
94
 
53
95
  ```typescript
54
- const content = await files.readFile('/greeting.txt');
55
- ```
96
+ import { writeText } from '@statewalker/webrun-files';
56
97
 
57
- For large files, `read` returns an async generator that yields chunks as they come in, keeping memory usage under control:
98
+ // Write string content as UTF-8
99
+ await writeText(files, '/greeting.txt', 'Hello, World!');
58
100
 
59
- ```typescript
60
- for await (const chunk of files.read('/large-file.bin')) {
61
- process.stdout.write(chunk);
62
- }
101
+ // Write binary content directly
102
+ await files.write('/data.bin', [new Uint8Array([1, 2, 3, 4])]);
103
+
104
+ // Write from multiple chunks
105
+ await files.write('/large.bin', generateChunks());
63
106
  ```
64
107
 
65
- You can also read specific portions of a file by specifying start and end positions:
108
+ ### Path Utilities
66
109
 
67
110
  ```typescript
68
- for await (const chunk of files.read('/data.bin', { start: 100, end: 200 })) {
69
- // Only bytes 100-199
70
- }
111
+ import { normalizePath, joinPath, dirname, basename, extname } from '@statewalker/webrun-files';
112
+
113
+ normalizePath('//foo/../bar/./baz/'); // '/bar/baz'
114
+ joinPath('/foo', 'bar', 'baz.txt'); // '/foo/bar/baz.txt'
115
+ dirname('/foo/bar/baz.txt'); // '/foo/bar'
116
+ basename('/foo/bar/baz.txt'); // 'baz.txt'
117
+ extname('/foo/bar/baz.txt'); // '.txt'
71
118
  ```
72
119
 
73
- ## Managing Files and Directories
120
+ ## Working with Files
74
121
 
75
- Checking whether a file or directory exists is simple:
122
+ ### Checking Existence
76
123
 
77
124
  ```typescript
78
125
  if (await files.exists('/config.json')) {
@@ -80,7 +127,7 @@ if (await files.exists('/config.json')) {
80
127
  }
81
128
  ```
82
129
 
83
- To get detailed information about a file, use `stats`:
130
+ ### Getting Metadata
84
131
 
85
132
  ```typescript
86
133
  const info = await files.stats('/photo.jpg');
@@ -91,23 +138,21 @@ if (info) {
91
138
  }
92
139
  ```
93
140
 
94
- Listing directory contents returns an async generator of file info objects:
141
+ ### Listing Directories
95
142
 
96
143
  ```typescript
144
+ // List direct children
97
145
  for await (const entry of files.list('/documents')) {
98
146
  console.log(`${entry.name} (${entry.kind})`);
99
147
  }
100
- ```
101
148
 
102
- Pass `{ recursive: true }` to traverse subdirectories:
103
-
104
- ```typescript
149
+ // List recursively
105
150
  for await (const entry of files.list('/project', { recursive: true })) {
106
151
  console.log(entry.path);
107
152
  }
108
153
  ```
109
154
 
110
- Creating directories, copying, moving, and deleting all work as you'd expect:
155
+ ### File Management
111
156
 
112
157
  ```typescript
113
158
  await files.mkdir('/archive/2024');
@@ -116,81 +161,16 @@ await files.move('/temp/draft.txt', '/documents/final.txt');
116
161
  await files.remove('/old-stuff'); // Recursively deletes directories
117
162
  ```
118
163
 
119
- ## Low-Level File Handle Operations
120
-
121
- When you need fine-grained control over file operations, work directly with file handles. Opening a file gives you a handle that supports reading, writing, and appending at specific positions:
122
-
123
- ```typescript
124
- const handle = await files.open('/data.bin');
125
-
126
- // Get the current file size
127
- console.log(`File is ${handle.size} bytes`);
128
-
129
- // Read a specific range
130
- for await (const chunk of handle.createReadStream({ start: 0, end: 100 })) {
131
- // First 100 bytes
132
- }
133
-
134
- // Write at a specific position
135
- await handle.createWriteStream([newData], { start: 50 });
136
-
137
- // Append to the end
138
- await handle.appendFile([moreData]);
139
-
140
- // Always close when done
141
- await handle.close();
142
- ```
143
-
144
- ## Building Your Own Backend
145
-
146
- The `IFilesApi` interface defines the contract that all backends must implement. At minimum, you need four methods:
147
-
148
- ```typescript
149
- interface IFilesApi {
150
- list(file: FileRef, options?: ListOptions): AsyncGenerator<FileInfo>;
151
- stats(file: FileRef): Promise<FileInfo | undefined>;
152
- remove(file: FileRef): Promise<boolean>;
153
- open(file: FileRef): Promise<FileHandle>;
154
- }
155
- ```
156
-
157
- The `FilesApi` wrapper class builds convenience methods like `read`, `write`, `copy`, and `move` on top of these primitives. Backends can optionally implement native versions of `move`, `copy`, and `mkdir` for better performance.
158
-
159
- Take a look at `MemFilesApi` or `NodeFilesApi` in the source code for reference implementations.
160
-
161
164
  ## Type Reference
162
165
 
163
- The package exports all the TypeScript types you'll need:
164
-
165
166
  ```typescript
166
167
  import type {
167
- IFilesApi, // Core interface for backends
168
- FilesApi, // Wrapper class with convenience methods
169
- FileInfo, // Metadata returned by stats() and list()
170
- FileHandle, // Low-level file operations
171
- FileRef, // string | { path: string }
172
- FileKind, // "file" | "directory"
173
- BinaryStream, // AsyncIterable<Uint8Array> | Iterable<Uint8Array>
174
- ListOptions, // { recursive?: boolean }
175
- CopyOptions, // { recursive?: boolean }
176
- ReadStreamOptions, // { start?: number, end?: number, signal?: AbortSignal }
177
- WriteStreamOptions, // { start?: number, signal?: AbortSignal }
178
- } from '@statewalker/webrun-files';
179
- ```
180
-
181
- ## Utility Functions
182
-
183
- The package includes helpful utilities for working with paths and streams:
184
-
185
- ```typescript
186
- import {
187
- normalizePath, // Clean up path strings
188
- resolveFileRef, // Convert FileRef to normalized path
189
- joinPath, // Join path segments
190
- dirname, // Get directory portion of path
191
- basename, // Get filename portion
192
- extname, // Get file extension
193
- collectGenerator, // Collect async iterable into array
168
+ FilesApi, // Core interface for backends
169
+ FileInfo, // Metadata from list() - includes name, path, kind, size, lastModified
170
+ FileStats, // Metadata from stats() - kind, size, lastModified
171
+ FileKind, // "file" | "directory"
172
+ ReadOptions, // { start?: number, length?: number, signal?: AbortSignal }
173
+ ListOptions, // { recursive?: boolean }
194
174
  } from '@statewalker/webrun-files';
195
175
  ```
196
176