@statewalker/webrun-files-browser 0.5.0 → 0.7.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.
@@ -1,194 +0,0 @@
1
- /**
2
- * Browser FileHandle implementation using File System Access API.
3
- *
4
- * Provides random access read/write operations for browser files.
5
- * Uses File.slice() for efficient partial reads and FileSystemWritableFileStream
6
- * for writes.
7
- */
8
-
9
- import type {
10
- AppendOptions,
11
- BinaryStream,
12
- FileHandle,
13
- ReadStreamOptions,
14
- WriteStreamOptions,
15
- } from "@statewalker/webrun-files";
16
- import { toBinaryAsyncIterable } from "@statewalker/webrun-files";
17
-
18
- export interface BrowserFileHandleOptions {
19
- fileHandle: FileSystemFileHandle;
20
- initialFile: File;
21
- }
22
-
23
- export class BrowserFileHandle implements FileHandle {
24
- private fileHandle: FileSystemFileHandle;
25
- private file: File | null;
26
- private _size: number;
27
- private closed = false;
28
-
29
- constructor(options: BrowserFileHandleOptions) {
30
- this.fileHandle = options.fileHandle;
31
- this.file = options.initialFile;
32
- this._size = options.initialFile.size;
33
- }
34
-
35
- get size(): number {
36
- return this._size;
37
- }
38
-
39
- async close(): Promise<void> {
40
- this.closed = true;
41
- this.file = null;
42
- }
43
-
44
- /**
45
- * Appends data to the end of the file.
46
- */
47
- async appendFile(data: BinaryStream, options: AppendOptions = {}): Promise<number> {
48
- if (this.closed) {
49
- throw new Error("FileHandle is closed");
50
- }
51
-
52
- const { signal } = options;
53
- const writable = await this.fileHandle.createWritable({ keepExistingData: true });
54
- let bytesWritten = 0;
55
-
56
- try {
57
- // Seek to end of file
58
- await writable.seek(this._size);
59
-
60
- for await (const chunk of toBinaryAsyncIterable(data)) {
61
- if (signal?.aborted) {
62
- throw new Error("Operation aborted");
63
- }
64
- // Cast to Uint8Array<ArrayBuffer> to satisfy FileSystemWritableFileStream.write()
65
- await writable.write(chunk as Uint8Array<ArrayBuffer>);
66
- bytesWritten += chunk.length;
67
- }
68
- } finally {
69
- await writable.close();
70
- }
71
-
72
- // Refresh file reference and size
73
- this.file = await this.fileHandle.getFile();
74
- this._size = this.file.size;
75
-
76
- return bytesWritten;
77
- }
78
-
79
- /**
80
- * Creates a read stream for the file.
81
- * Uses File.slice() for efficient partial reads.
82
- */
83
- async *createReadStream(options: ReadStreamOptions = {}): AsyncGenerator<Uint8Array> {
84
- if (this.closed) {
85
- throw new Error("FileHandle is closed");
86
- }
87
-
88
- // Ensure we have a fresh file reference
89
- if (!this.file) {
90
- this.file = await this.fileHandle.getFile();
91
- this._size = this.file.size;
92
- }
93
-
94
- const { start = 0, end = Infinity, signal } = options;
95
- const bufferSize = 8192;
96
- const actualEnd = Math.min(end, this._size);
97
-
98
- if (start >= actualEnd || this._size === 0) return;
99
-
100
- let position = start;
101
-
102
- while (position < actualEnd) {
103
- if (signal?.aborted) {
104
- throw new Error("Operation aborted");
105
- }
106
-
107
- const remaining = actualEnd - position;
108
- const toRead = Math.min(bufferSize, remaining);
109
-
110
- const slice = this.file.slice(position, position + toRead);
111
- const buffer = await slice.arrayBuffer();
112
-
113
- yield new Uint8Array(buffer);
114
- position += buffer.byteLength;
115
- }
116
- }
117
-
118
- /**
119
- * Writes data to the file starting at the specified position.
120
- * If start > 0, preserves content before start position.
121
- * Truncates file at start position before writing.
122
- */
123
- async createWriteStream(data: BinaryStream, options: WriteStreamOptions = {}): Promise<number> {
124
- if (this.closed) {
125
- throw new Error("FileHandle is closed");
126
- }
127
-
128
- const { start = 0, signal } = options;
129
-
130
- // Create writable stream, keeping existing data if we need to preserve content before start
131
- const writable = await this.fileHandle.createWritable({
132
- keepExistingData: start > 0,
133
- });
134
-
135
- let bytesWritten = 0;
136
-
137
- try {
138
- if (start > 0) {
139
- // Seek to start position
140
- await writable.seek(start);
141
- // Truncate at start position (removes content after start)
142
- await writable.truncate(start);
143
- }
144
-
145
- for await (const chunk of toBinaryAsyncIterable(data)) {
146
- if (signal?.aborted) {
147
- throw new Error("Operation aborted");
148
- }
149
- // Cast to Uint8Array<ArrayBuffer> to satisfy FileSystemWritableFileStream.write()
150
- await writable.write(chunk as Uint8Array<ArrayBuffer>);
151
- bytesWritten += chunk.length;
152
- }
153
- } finally {
154
- await writable.close();
155
- }
156
-
157
- // Refresh file reference and size
158
- this.file = await this.fileHandle.getFile();
159
- this._size = this.file.size;
160
-
161
- return bytesWritten;
162
- }
163
-
164
- /**
165
- * Random access read from file at a specific position.
166
- * Uses File.slice() for efficient partial reads.
167
- */
168
- async read(buffer: Uint8Array, offset: number, length: number, position: number): Promise<number> {
169
- if (this.closed) {
170
- throw new Error("FileHandle is closed");
171
- }
172
-
173
- // Ensure we have a fresh file reference
174
- if (!this.file) {
175
- this.file = await this.fileHandle.getFile();
176
- this._size = this.file.size;
177
- }
178
-
179
- // Calculate actual bytes to read
180
- let len = Math.min(length, buffer.length - offset);
181
- len = Math.min(len, this._size - position);
182
- len = Math.max(0, len);
183
-
184
- if (len === 0) {
185
- return 0;
186
- }
187
-
188
- const slice = this.file.slice(position, position + len);
189
- const arrayBuffer = await slice.arrayBuffer();
190
- const data = new Uint8Array(arrayBuffer);
191
- buffer.set(data, offset);
192
- return len;
193
- }
194
- }