@ricsam/quickjs-fs 0.2.7 → 0.2.9
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.
- package/dist/cjs/bun-adapter.cjs +342 -0
- package/dist/cjs/bun-adapter.cjs.map +10 -0
- package/dist/cjs/node-adapter.cjs +8 -4
- package/dist/cjs/node-adapter.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/bun-adapter.mjs +310 -0
- package/dist/mjs/bun-adapter.mjs.map +10 -0
- package/dist/mjs/node-adapter.mjs +9 -4
- package/dist/mjs/node-adapter.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/types/bun-adapter.d.ts +93 -0
- package/dist/types/node-adapter.d.ts +24 -1
- package/package.json +2 -2
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
// @bun @bun-cjs
|
|
2
|
+
(function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// packages/fs/src/bun-adapter.ts
|
|
31
|
+
var exports_bun_adapter = {};
|
|
32
|
+
__export(exports_bun_adapter, {
|
|
33
|
+
createBunDirectoryHandle: () => createBunDirectoryHandle,
|
|
34
|
+
BunWritableFileStream: () => BunWritableFileStream,
|
|
35
|
+
BunFileHandle: () => BunFileHandle,
|
|
36
|
+
BunDirectoryHandle: () => BunDirectoryHandle
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(exports_bun_adapter);
|
|
39
|
+
|
|
40
|
+
class BunFileHandle {
|
|
41
|
+
name;
|
|
42
|
+
fullPath;
|
|
43
|
+
kind = "file";
|
|
44
|
+
constructor(name, fullPath) {
|
|
45
|
+
this.name = name;
|
|
46
|
+
this.fullPath = fullPath;
|
|
47
|
+
}
|
|
48
|
+
async getFile() {
|
|
49
|
+
const file = Bun.file(this.fullPath);
|
|
50
|
+
const buffer = await file.arrayBuffer();
|
|
51
|
+
const stat = await Bun.file(this.fullPath).stat();
|
|
52
|
+
return new File([buffer], this.name, {
|
|
53
|
+
type: file.type,
|
|
54
|
+
lastModified: stat?.mtime?.getTime() ?? Date.now()
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async createWritable(options) {
|
|
58
|
+
return new BunWritableFileStream(this.fullPath, options?.keepExistingData ?? false);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class BunWritableFileStream {
|
|
63
|
+
sink = null;
|
|
64
|
+
buffer = null;
|
|
65
|
+
position = 0;
|
|
66
|
+
fileSize = 0;
|
|
67
|
+
streamingMode;
|
|
68
|
+
closed = false;
|
|
69
|
+
path;
|
|
70
|
+
initPromise = null;
|
|
71
|
+
constructor(path, keepExistingData) {
|
|
72
|
+
this.path = path;
|
|
73
|
+
if (keepExistingData) {
|
|
74
|
+
this.streamingMode = false;
|
|
75
|
+
this.initPromise = this.initBufferedModeWithExisting();
|
|
76
|
+
} else {
|
|
77
|
+
this.streamingMode = true;
|
|
78
|
+
this.initPromise = this.initStreamingMode();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async initStreamingMode() {
|
|
82
|
+
await Bun.write(this.path, "");
|
|
83
|
+
const file = Bun.file(this.path);
|
|
84
|
+
this.sink = file.writer({ highWaterMark: 64 * 1024 });
|
|
85
|
+
this.streamingMode = true;
|
|
86
|
+
this.buffer = null;
|
|
87
|
+
}
|
|
88
|
+
async initBufferedModeWithExisting() {
|
|
89
|
+
this.streamingMode = false;
|
|
90
|
+
try {
|
|
91
|
+
const file = Bun.file(this.path);
|
|
92
|
+
const exists = await file.exists();
|
|
93
|
+
if (exists) {
|
|
94
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
95
|
+
this.buffer = new Uint8Array(arrayBuffer);
|
|
96
|
+
this.fileSize = this.buffer.length;
|
|
97
|
+
this.position = this.buffer.length;
|
|
98
|
+
} else {
|
|
99
|
+
this.buffer = new Uint8Array(0);
|
|
100
|
+
this.fileSize = 0;
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
this.buffer = new Uint8Array(0);
|
|
104
|
+
this.fileSize = 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async switchToBufferedMode() {
|
|
108
|
+
if (!this.streamingMode)
|
|
109
|
+
return;
|
|
110
|
+
if (this.initPromise) {
|
|
111
|
+
await this.initPromise;
|
|
112
|
+
this.initPromise = null;
|
|
113
|
+
}
|
|
114
|
+
this.streamingMode = false;
|
|
115
|
+
if (this.sink) {
|
|
116
|
+
await this.sink.end();
|
|
117
|
+
this.sink = null;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const file = Bun.file(this.path);
|
|
121
|
+
const exists = await file.exists();
|
|
122
|
+
if (exists) {
|
|
123
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
124
|
+
this.buffer = new Uint8Array(arrayBuffer);
|
|
125
|
+
this.fileSize = this.buffer.length;
|
|
126
|
+
} else {
|
|
127
|
+
this.buffer = new Uint8Array(0);
|
|
128
|
+
this.fileSize = 0;
|
|
129
|
+
}
|
|
130
|
+
} catch {
|
|
131
|
+
this.buffer = new Uint8Array(0);
|
|
132
|
+
this.fileSize = 0;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async write(data) {
|
|
136
|
+
if (this.initPromise) {
|
|
137
|
+
await this.initPromise;
|
|
138
|
+
this.initPromise = null;
|
|
139
|
+
}
|
|
140
|
+
if (this.closed) {
|
|
141
|
+
throw new TypeError("Cannot write to a closed stream");
|
|
142
|
+
}
|
|
143
|
+
let bytes;
|
|
144
|
+
if (typeof data === "string") {
|
|
145
|
+
bytes = new TextEncoder().encode(data);
|
|
146
|
+
} else if (data instanceof ArrayBuffer) {
|
|
147
|
+
bytes = new Uint8Array(data);
|
|
148
|
+
} else if (data instanceof Uint8Array) {
|
|
149
|
+
bytes = data;
|
|
150
|
+
} else if (data instanceof Blob) {
|
|
151
|
+
bytes = new Uint8Array(await data.arrayBuffer());
|
|
152
|
+
} else {
|
|
153
|
+
if (data.type === "seek") {
|
|
154
|
+
await this.seek(data.position ?? 0);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (data.type === "truncate") {
|
|
158
|
+
await this.truncate(data.size ?? 0);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (data.data === undefined)
|
|
162
|
+
return;
|
|
163
|
+
return this.write(data.data);
|
|
164
|
+
}
|
|
165
|
+
if (this.streamingMode && this.sink) {
|
|
166
|
+
const written = this.sink.write(bytes);
|
|
167
|
+
if (written === 0) {
|
|
168
|
+
await this.sink.flush();
|
|
169
|
+
this.sink.write(bytes);
|
|
170
|
+
}
|
|
171
|
+
this.position += bytes.length;
|
|
172
|
+
this.fileSize = Math.max(this.fileSize, this.position);
|
|
173
|
+
} else {
|
|
174
|
+
if (!this.buffer) {
|
|
175
|
+
this.buffer = new Uint8Array(0);
|
|
176
|
+
}
|
|
177
|
+
const neededSize = this.position + bytes.length;
|
|
178
|
+
if (neededSize > this.buffer.length) {
|
|
179
|
+
const newBuffer = new Uint8Array(neededSize);
|
|
180
|
+
newBuffer.set(this.buffer);
|
|
181
|
+
this.buffer = newBuffer;
|
|
182
|
+
}
|
|
183
|
+
this.buffer.set(bytes, this.position);
|
|
184
|
+
this.position += bytes.length;
|
|
185
|
+
this.fileSize = Math.max(this.fileSize, this.position);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async seek(position) {
|
|
189
|
+
if (this.initPromise) {
|
|
190
|
+
await this.initPromise;
|
|
191
|
+
this.initPromise = null;
|
|
192
|
+
}
|
|
193
|
+
if (this.closed) {
|
|
194
|
+
throw new TypeError("Cannot seek on a closed stream");
|
|
195
|
+
}
|
|
196
|
+
if (this.streamingMode) {
|
|
197
|
+
await this.switchToBufferedMode();
|
|
198
|
+
}
|
|
199
|
+
this.position = position;
|
|
200
|
+
}
|
|
201
|
+
async truncate(size) {
|
|
202
|
+
if (this.initPromise) {
|
|
203
|
+
await this.initPromise;
|
|
204
|
+
this.initPromise = null;
|
|
205
|
+
}
|
|
206
|
+
if (this.closed) {
|
|
207
|
+
throw new TypeError("Cannot truncate a closed stream");
|
|
208
|
+
}
|
|
209
|
+
if (this.streamingMode) {
|
|
210
|
+
await this.switchToBufferedMode();
|
|
211
|
+
}
|
|
212
|
+
if (!this.buffer) {
|
|
213
|
+
this.buffer = new Uint8Array(0);
|
|
214
|
+
}
|
|
215
|
+
if (size < this.buffer.length) {
|
|
216
|
+
this.buffer = this.buffer.slice(0, size);
|
|
217
|
+
} else if (size > this.buffer.length) {
|
|
218
|
+
const newBuffer = new Uint8Array(size);
|
|
219
|
+
newBuffer.set(this.buffer);
|
|
220
|
+
this.buffer = newBuffer;
|
|
221
|
+
}
|
|
222
|
+
this.fileSize = size;
|
|
223
|
+
if (this.position > size) {
|
|
224
|
+
this.position = size;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async close() {
|
|
228
|
+
if (this.initPromise) {
|
|
229
|
+
await this.initPromise;
|
|
230
|
+
this.initPromise = null;
|
|
231
|
+
}
|
|
232
|
+
if (this.closed)
|
|
233
|
+
return;
|
|
234
|
+
this.closed = true;
|
|
235
|
+
if (this.streamingMode && this.sink) {
|
|
236
|
+
await this.sink.end();
|
|
237
|
+
this.sink = null;
|
|
238
|
+
} else if (this.buffer) {
|
|
239
|
+
await Bun.write(this.path, this.buffer);
|
|
240
|
+
this.buffer = null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async abort(_reason) {
|
|
244
|
+
if (this.initPromise) {
|
|
245
|
+
this.initPromise = null;
|
|
246
|
+
}
|
|
247
|
+
if (this.closed)
|
|
248
|
+
return;
|
|
249
|
+
this.closed = true;
|
|
250
|
+
if (this.sink) {
|
|
251
|
+
await this.sink.end();
|
|
252
|
+
this.sink = null;
|
|
253
|
+
}
|
|
254
|
+
this.buffer = null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class BunDirectoryHandle {
|
|
259
|
+
name;
|
|
260
|
+
fullPath;
|
|
261
|
+
kind = "directory";
|
|
262
|
+
constructor(name, fullPath) {
|
|
263
|
+
this.name = name;
|
|
264
|
+
this.fullPath = fullPath;
|
|
265
|
+
}
|
|
266
|
+
async getFileHandle(name, options) {
|
|
267
|
+
const path = `${this.fullPath}/${name}`;
|
|
268
|
+
const file = Bun.file(path);
|
|
269
|
+
const exists = await file.exists();
|
|
270
|
+
if (!exists && !options?.create) {
|
|
271
|
+
throw new DOMException(`File not found: ${name}`, "NotFoundError");
|
|
272
|
+
}
|
|
273
|
+
if (!exists && options?.create) {
|
|
274
|
+
await Bun.write(path, "");
|
|
275
|
+
}
|
|
276
|
+
return new BunFileHandle(name, path);
|
|
277
|
+
}
|
|
278
|
+
async getDirectoryHandle(name, options) {
|
|
279
|
+
const path = `${this.fullPath}/${name}`;
|
|
280
|
+
try {
|
|
281
|
+
const stat = await Bun.file(path).stat();
|
|
282
|
+
if (!stat) {
|
|
283
|
+
throw new Error("Not found");
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
if (!options?.create) {
|
|
287
|
+
throw new DOMException(`Directory not found: ${name}`, "NotFoundError");
|
|
288
|
+
}
|
|
289
|
+
await Bun.$`mkdir -p ${path}`;
|
|
290
|
+
}
|
|
291
|
+
return new BunDirectoryHandle(name, path);
|
|
292
|
+
}
|
|
293
|
+
async removeEntry(name, options) {
|
|
294
|
+
const path = `${this.fullPath}/${name}`;
|
|
295
|
+
if (options?.recursive) {
|
|
296
|
+
await Bun.$`rm -rf ${path}`;
|
|
297
|
+
} else {
|
|
298
|
+
await Bun.$`rm ${path}`;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async resolve(possibleDescendant) {
|
|
302
|
+
const descendantPath = possibleDescendant instanceof BunFileHandle || possibleDescendant instanceof BunDirectoryHandle ? possibleDescendant.fullPath : null;
|
|
303
|
+
if (!descendantPath || !descendantPath.startsWith(this.fullPath)) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const relativePath = descendantPath.slice(this.fullPath.length);
|
|
307
|
+
return relativePath.split("/").filter(Boolean);
|
|
308
|
+
}
|
|
309
|
+
async* entries() {
|
|
310
|
+
const result = await Bun.$`ls -1 ${this.fullPath}`.text();
|
|
311
|
+
const names = result.trim().split(`
|
|
312
|
+
`).filter(Boolean);
|
|
313
|
+
for (const name of names) {
|
|
314
|
+
const path = `${this.fullPath}/${name}`;
|
|
315
|
+
try {
|
|
316
|
+
const stat = await Bun.file(path).stat();
|
|
317
|
+
if (stat && typeof stat.isDirectory === "function" && stat.isDirectory()) {
|
|
318
|
+
yield [name, new BunDirectoryHandle(name, path)];
|
|
319
|
+
} else {
|
|
320
|
+
yield [name, new BunFileHandle(name, path)];
|
|
321
|
+
}
|
|
322
|
+
} catch {}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async* keys() {
|
|
326
|
+
for await (const [name] of this.entries()) {
|
|
327
|
+
yield name;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async* values() {
|
|
331
|
+
for await (const [, handle] of this.entries()) {
|
|
332
|
+
yield handle;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function createBunDirectoryHandle(rootPath) {
|
|
337
|
+
const name = rootPath.split("/").pop() || "";
|
|
338
|
+
return new BunDirectoryHandle(name, rootPath);
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
//# debugId=9762BCD3EE61B90D64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/bun-adapter.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n HostDirectoryHandle,\n HostFileHandle,\n HostWritableFileStream,\n WriteParams,\n} from \"./types.cjs\";\n\n/**\n * Bun file handle implementation with true streaming writes\n */\nclass BunFileHandle implements HostFileHandle {\n readonly kind = \"file\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFile(): Promise<File> {\n const file = Bun.file(this.fullPath);\n const buffer = await file.arrayBuffer();\n const stat = await Bun.file(this.fullPath).stat();\n\n return new File([buffer], this.name, {\n type: file.type,\n lastModified: stat?.mtime?.getTime() ?? Date.now(),\n });\n }\n\n async createWritable(\n options?: { keepExistingData?: boolean }\n ): Promise<HostWritableFileStream> {\n return new BunWritableFileStream(\n this.fullPath,\n options?.keepExistingData ?? false\n );\n }\n}\n\n/**\n * Streaming writable file stream using Bun's FileSink\n *\n * Uses a hybrid approach:\n * - Sequential writes use FileSink for true streaming (no full buffering)\n * - Falls back to buffered mode if seek/truncate is called\n *\n * This enables streaming large files to disk without memory issues\n * while maintaining full API compatibility.\n */\nclass BunWritableFileStream implements HostWritableFileStream {\n private sink: ReturnType<ReturnType<typeof Bun.file>[\"writer\"]> | null = null;\n private buffer: Uint8Array | null = null;\n private position = 0;\n private fileSize = 0;\n private streamingMode: boolean;\n private closed = false;\n private readonly path: string;\n private initPromise: Promise<void> | null = null;\n\n constructor(path: string, keepExistingData: boolean) {\n this.path = path;\n\n if (keepExistingData) {\n // Need to read existing data first - can't use streaming for this case\n this.streamingMode = false;\n // Start async initialization\n this.initPromise = this.initBufferedModeWithExisting();\n } else {\n // Start fresh - truncate and use streaming\n this.streamingMode = true;\n // Truncate the file first, then open the writer\n this.initPromise = this.initStreamingMode();\n }\n }\n\n private async initStreamingMode(): Promise<void> {\n // Truncate the file first to ensure clean slate\n await Bun.write(this.path, \"\");\n const file = Bun.file(this.path);\n this.sink = file.writer({ highWaterMark: 64 * 1024 }); // 64KB buffer\n this.streamingMode = true;\n this.buffer = null;\n }\n\n private async initBufferedModeWithExisting(): Promise<void> {\n this.streamingMode = false;\n\n try {\n const file = Bun.file(this.path);\n const exists = await file.exists();\n if (exists) {\n const arrayBuffer = await file.arrayBuffer();\n this.buffer = new Uint8Array(arrayBuffer);\n this.fileSize = this.buffer.length;\n this.position = this.buffer.length; // Position at end\n } else {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n } catch {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n }\n\n private async switchToBufferedMode(): Promise<void> {\n if (!this.streamingMode) return;\n\n // Ensure any pending init is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n this.streamingMode = false;\n\n if (this.sink) {\n // Flush and close the sink before switching modes\n await this.sink.end();\n this.sink = null;\n }\n\n // Read what we've written so far\n try {\n const file = Bun.file(this.path);\n const exists = await file.exists();\n if (exists) {\n const arrayBuffer = await file.arrayBuffer();\n this.buffer = new Uint8Array(arrayBuffer);\n this.fileSize = this.buffer.length;\n } else {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n } catch {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n }\n\n async write(\n data: string | ArrayBuffer | Uint8Array | Blob | WriteParams\n ): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) {\n throw new TypeError(\"Cannot write to a closed stream\");\n }\n\n let bytes: Uint8Array;\n\n if (typeof data === \"string\") {\n bytes = new TextEncoder().encode(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else if (data instanceof Uint8Array) {\n bytes = data;\n } else if (data instanceof Blob) {\n bytes = new Uint8Array(await data.arrayBuffer());\n } else {\n // WriteParams\n if (data.type === \"seek\") {\n await this.seek(data.position ?? 0);\n return;\n }\n if (data.type === \"truncate\") {\n await this.truncate(data.size ?? 0);\n return;\n }\n // type === \"write\"\n if (data.data === undefined) return;\n return this.write(data.data);\n }\n\n if (this.streamingMode && this.sink) {\n // True streaming mode - write directly to FileSink\n const written = this.sink.write(bytes);\n if (written === 0) {\n // Backpressure: buffer is full, need to flush\n await this.sink.flush();\n this.sink.write(bytes);\n }\n this.position += bytes.length;\n this.fileSize = Math.max(this.fileSize, this.position);\n } else {\n // Buffered mode - expand buffer as needed\n if (!this.buffer) {\n this.buffer = new Uint8Array(0);\n }\n\n const neededSize = this.position + bytes.length;\n if (neededSize > this.buffer.length) {\n const newBuffer = new Uint8Array(neededSize);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n\n this.buffer.set(bytes, this.position);\n this.position += bytes.length;\n this.fileSize = Math.max(this.fileSize, this.position);\n }\n }\n\n async seek(position: number): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) {\n throw new TypeError(\"Cannot seek on a closed stream\");\n }\n\n // Seeking requires buffered mode since FileSink doesn't support it\n if (this.streamingMode) {\n await this.switchToBufferedMode();\n }\n\n this.position = position;\n }\n\n async truncate(size: number): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) {\n throw new TypeError(\"Cannot truncate a closed stream\");\n }\n\n // Truncate requires buffered mode since FileSink doesn't support it\n if (this.streamingMode) {\n await this.switchToBufferedMode();\n }\n\n if (!this.buffer) {\n this.buffer = new Uint8Array(0);\n }\n\n if (size < this.buffer.length) {\n this.buffer = this.buffer.slice(0, size);\n } else if (size > this.buffer.length) {\n const newBuffer = new Uint8Array(size);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n\n this.fileSize = size;\n if (this.position > size) {\n this.position = size;\n }\n }\n\n async close(): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) return;\n this.closed = true;\n\n if (this.streamingMode && this.sink) {\n // Streaming mode - just end the sink\n await this.sink.end();\n this.sink = null;\n } else if (this.buffer) {\n // Buffered mode - write buffer to disk\n await Bun.write(this.path, this.buffer);\n this.buffer = null;\n }\n }\n\n async abort(_reason?: unknown): Promise<void> {\n // Cancel any pending initialization\n if (this.initPromise) {\n // Don't await - we're aborting\n this.initPromise = null;\n }\n\n if (this.closed) return;\n this.closed = true;\n\n if (this.sink) {\n await this.sink.end();\n this.sink = null;\n }\n this.buffer = null;\n\n // Optionally delete the partially written file\n // For now, we leave it as-is to match the existing behavior\n }\n}\n\n/**\n * Bun directory handle implementation\n */\nclass BunDirectoryHandle implements HostDirectoryHandle {\n readonly kind = \"directory\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFileHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostFileHandle> {\n const path = `${this.fullPath}/${name}`;\n const file = Bun.file(path);\n const exists = await file.exists();\n\n if (!exists && !options?.create) {\n throw new DOMException(`File not found: ${name}`, \"NotFoundError\");\n }\n\n if (!exists && options?.create) {\n await Bun.write(path, \"\");\n }\n\n return new BunFileHandle(name, path);\n }\n\n async getDirectoryHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostDirectoryHandle> {\n const path = `${this.fullPath}/${name}`;\n\n try {\n const stat = await Bun.file(path).stat();\n if (!stat) {\n throw new Error(\"Not found\");\n }\n } catch {\n if (!options?.create) {\n throw new DOMException(`Directory not found: ${name}`, \"NotFoundError\");\n }\n // Create directory\n await Bun.$`mkdir -p ${path}`;\n }\n\n return new BunDirectoryHandle(name, path);\n }\n\n async removeEntry(\n name: string,\n options?: { recursive?: boolean }\n ): Promise<void> {\n const path = `${this.fullPath}/${name}`;\n\n if (options?.recursive) {\n await Bun.$`rm -rf ${path}`;\n } else {\n await Bun.$`rm ${path}`;\n }\n }\n\n async resolve(\n possibleDescendant: HostFileHandle | HostDirectoryHandle\n ): Promise<string[] | null> {\n // Get the paths and compare\n const descendantPath =\n possibleDescendant instanceof BunFileHandle ||\n possibleDescendant instanceof BunDirectoryHandle\n ? (possibleDescendant as unknown as { fullPath: string }).fullPath\n : null;\n\n if (!descendantPath || !descendantPath.startsWith(this.fullPath)) {\n return null;\n }\n\n const relativePath = descendantPath.slice(this.fullPath.length);\n return relativePath.split(\"/\").filter(Boolean);\n }\n\n async *entries(): AsyncIterable<\n [string, HostFileHandle | HostDirectoryHandle]\n > {\n const result = await Bun.$`ls -1 ${this.fullPath}`.text();\n const names = result.trim().split(\"\\n\").filter(Boolean);\n\n for (const name of names) {\n const path = `${this.fullPath}/${name}`;\n try {\n const stat = await Bun.file(path).stat();\n if (stat && typeof stat.isDirectory === 'function' && stat.isDirectory()) {\n yield [name, new BunDirectoryHandle(name, path)];\n } else {\n yield [name, new BunFileHandle(name, path)];\n }\n } catch {\n // Skip entries we can't stat\n }\n }\n }\n\n async *keys(): AsyncIterable<string> {\n for await (const [name] of this.entries()) {\n yield name;\n }\n }\n\n async *values(): AsyncIterable<HostFileHandle | HostDirectoryHandle> {\n for await (const [, handle] of this.entries()) {\n yield handle;\n }\n }\n}\n\n/**\n * Create a directory handle backed by Bun's file APIs with streaming support\n *\n * This implementation uses Bun's FileSink for streaming writes, which enables\n * writing large files without buffering them entirely in memory.\n *\n * @param rootPath - Absolute path to the directory on disk\n * @returns A HostDirectoryHandle implementation with streaming support\n *\n * @example\n * import { createBunDirectoryHandle } from \"@ricsam/quickjs-fs\";\n *\n * const handle = setupFs(context, {\n * getDirectory: async (path) => {\n * // Only allow access to /sandbox\n * if (!path.startsWith(\"/sandbox\")) {\n * throw new Error(\"Access denied\");\n * }\n * const realPath = path.replace(\"/sandbox\", \"/var/app/sandbox\");\n * return createBunDirectoryHandle(realPath);\n * }\n * });\n */\nexport function createBunDirectoryHandle(\n rootPath: string\n): HostDirectoryHandle {\n const name = rootPath.split(\"/\").pop() || \"\";\n return new BunDirectoryHandle(name, rootPath);\n}\n\n// Re-export for convenience\nexport { BunWritableFileStream, BunFileHandle, BunDirectoryHandle };\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "9762BCD3EE61B90D64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -30,9 +30,13 @@ var __export = (target, all) => {
|
|
|
30
30
|
// packages/fs/src/node-adapter.ts
|
|
31
31
|
var exports_node_adapter = {};
|
|
32
32
|
__export(exports_node_adapter, {
|
|
33
|
-
createNodeDirectoryHandle: () => createNodeDirectoryHandle
|
|
33
|
+
createNodeDirectoryHandle: () => createNodeDirectoryHandle,
|
|
34
|
+
BunWritableFileStream: () => import_bun_adapter2.BunWritableFileStream,
|
|
35
|
+
BufferedWritableFileStream: () => BufferedWritableFileStream
|
|
34
36
|
});
|
|
35
37
|
module.exports = __toCommonJS(exports_node_adapter);
|
|
38
|
+
var import_bun_adapter = require("./bun-adapter.cjs");
|
|
39
|
+
var import_bun_adapter2 = require("./bun-adapter.cjs");
|
|
36
40
|
|
|
37
41
|
class NodeFileHandle {
|
|
38
42
|
name;
|
|
@@ -52,11 +56,11 @@ class NodeFileHandle {
|
|
|
52
56
|
});
|
|
53
57
|
}
|
|
54
58
|
async createWritable(options) {
|
|
55
|
-
return new
|
|
59
|
+
return new import_bun_adapter.BunWritableFileStream(this.fullPath, options?.keepExistingData ?? false);
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
class
|
|
63
|
+
class BufferedWritableFileStream {
|
|
60
64
|
path;
|
|
61
65
|
buffer;
|
|
62
66
|
position = 0;
|
|
@@ -218,4 +222,4 @@ function createNodeDirectoryHandle(rootPath) {
|
|
|
218
222
|
}
|
|
219
223
|
})
|
|
220
224
|
|
|
221
|
-
//# debugId=
|
|
225
|
+
//# debugId=828E7C8FF060601E64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/node-adapter.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type {\n HostDirectoryHandle,\n HostFileHandle,\n HostWritableFileStream,\n WriteParams,\n} from \"./types.cjs\";\n\n/**\n * Node/Bun file handle implementation\n */\nclass NodeFileHandle implements HostFileHandle {\n readonly kind = \"file\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFile(): Promise<File> {\n const file = Bun.file(this.fullPath);\n const buffer = await file.arrayBuffer();\n const stat = await Bun.file(this.fullPath).stat();\n\n return new File([buffer], this.name, {\n type: file.type,\n lastModified: stat?.mtime?.getTime() ?? Date.now(),\n });\n }\n\n async createWritable(\n options?: { keepExistingData?: boolean }\n ): Promise<HostWritableFileStream> {\n return new
|
|
5
|
+
"import type {\n HostDirectoryHandle,\n HostFileHandle,\n HostWritableFileStream,\n WriteParams,\n} from \"./types.cjs\";\nimport { BunWritableFileStream } from \"./bun-adapter.cjs\";\n\n/**\n * Node/Bun file handle implementation\n *\n * Uses BunWritableFileStream for streaming writes when creating writable streams.\n */\nclass NodeFileHandle implements HostFileHandle {\n readonly kind = \"file\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFile(): Promise<File> {\n const file = Bun.file(this.fullPath);\n const buffer = await file.arrayBuffer();\n const stat = await Bun.file(this.fullPath).stat();\n\n return new File([buffer], this.name, {\n type: file.type,\n lastModified: stat?.mtime?.getTime() ?? Date.now(),\n });\n }\n\n async createWritable(\n options?: { keepExistingData?: boolean }\n ): Promise<HostWritableFileStream> {\n // Use streaming implementation by default\n return new BunWritableFileStream(\n this.fullPath,\n options?.keepExistingData ?? false\n );\n }\n}\n\n/**\n * Buffered writable file stream implementation (legacy)\n *\n * WARNING: This implementation buffers ALL data in memory and only writes\n * to disk on close(). For large files, use BunWritableFileStream instead\n * which provides true streaming writes.\n *\n * @deprecated Use BunWritableFileStream for new code - it has better memory\n * characteristics for large file writes.\n */\nclass BufferedWritableFileStream implements HostWritableFileStream {\n private buffer: Uint8Array;\n private position = 0;\n\n constructor(\n private readonly path: string,\n keepExistingData: boolean\n ) {\n if (keepExistingData) {\n // Read existing file synchronously for simplicity\n try {\n const file = Bun.file(this.path);\n // Note: This is simplified; in production you'd want async\n this.buffer = new Uint8Array(0);\n file.arrayBuffer().then((ab) => {\n this.buffer = new Uint8Array(ab);\n });\n } catch {\n this.buffer = new Uint8Array(0);\n }\n } else {\n this.buffer = new Uint8Array(0);\n }\n }\n\n async write(\n data: string | ArrayBuffer | Uint8Array | Blob | WriteParams\n ): Promise<void> {\n let bytes: Uint8Array;\n\n if (typeof data === \"string\") {\n bytes = new TextEncoder().encode(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else if (data instanceof Uint8Array) {\n bytes = data;\n } else if (data instanceof Blob) {\n bytes = new Uint8Array(await data.arrayBuffer());\n } else {\n // WriteParams\n if (data.type === \"seek\") {\n this.position = data.position ?? 0;\n return;\n }\n if (data.type === \"truncate\") {\n this.buffer = this.buffer.slice(0, data.size ?? 0);\n if (this.position > this.buffer.length) {\n this.position = this.buffer.length;\n }\n return;\n }\n // type === \"write\"\n if (data.data === undefined) return;\n return this.write(data.data);\n }\n\n // Expand buffer if needed\n const neededSize = this.position + bytes.length;\n if (neededSize > this.buffer.length) {\n const newBuffer = new Uint8Array(neededSize);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n\n this.buffer.set(bytes, this.position);\n this.position += bytes.length;\n }\n\n async seek(position: number): Promise<void> {\n this.position = position;\n }\n\n async truncate(size: number): Promise<void> {\n if (size < this.buffer.length) {\n this.buffer = this.buffer.slice(0, size);\n } else if (size > this.buffer.length) {\n const newBuffer = new Uint8Array(size);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n if (this.position > size) {\n this.position = size;\n }\n }\n\n async close(): Promise<void> {\n await Bun.write(this.path, this.buffer);\n }\n\n async abort(_reason?: unknown): Promise<void> {\n // Don't write, just discard\n this.buffer = new Uint8Array(0);\n }\n}\n\n/**\n * Node/Bun directory handle implementation\n */\nclass NodeDirectoryHandle implements HostDirectoryHandle {\n readonly kind = \"directory\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFileHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostFileHandle> {\n const path = `${this.fullPath}/${name}`;\n const file = Bun.file(path);\n const exists = await file.exists();\n\n if (!exists && !options?.create) {\n throw new DOMException(`File not found: ${name}`, \"NotFoundError\");\n }\n\n if (!exists && options?.create) {\n await Bun.write(path, \"\");\n }\n\n return new NodeFileHandle(name, path);\n }\n\n async getDirectoryHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostDirectoryHandle> {\n const path = `${this.fullPath}/${name}`;\n\n try {\n const stat = await Bun.file(path).stat();\n if (!stat) {\n throw new Error(\"Not found\");\n }\n } catch {\n if (!options?.create) {\n throw new DOMException(`Directory not found: ${name}`, \"NotFoundError\");\n }\n // Create directory\n await Bun.$`mkdir -p ${path}`;\n }\n\n return new NodeDirectoryHandle(name, path);\n }\n\n async removeEntry(\n name: string,\n options?: { recursive?: boolean }\n ): Promise<void> {\n const path = `${this.fullPath}/${name}`;\n\n if (options?.recursive) {\n await Bun.$`rm -rf ${path}`;\n } else {\n await Bun.$`rm ${path}`;\n }\n }\n\n async resolve(\n possibleDescendant: HostFileHandle | HostDirectoryHandle\n ): Promise<string[] | null> {\n // Get the paths and compare\n const descendantPath =\n possibleDescendant instanceof NodeFileHandle ||\n possibleDescendant instanceof NodeDirectoryHandle\n ? (possibleDescendant as unknown as { fullPath: string }).fullPath\n : null;\n\n if (!descendantPath || !descendantPath.startsWith(this.fullPath)) {\n return null;\n }\n\n const relativePath = descendantPath.slice(this.fullPath.length);\n return relativePath.split(\"/\").filter(Boolean);\n }\n\n async *entries(): AsyncIterable<\n [string, HostFileHandle | HostDirectoryHandle]\n > {\n const result = await Bun.$`ls -1 ${this.fullPath}`.text();\n const names = result.trim().split(\"\\n\").filter(Boolean);\n\n for (const name of names) {\n const path = `${this.fullPath}/${name}`;\n try {\n const stat = await Bun.file(path).stat();\n if (stat && typeof stat.isDirectory === 'function' && stat.isDirectory()) {\n yield [name, new NodeDirectoryHandle(name, path)];\n } else {\n yield [name, new NodeFileHandle(name, path)];\n }\n } catch {\n // Skip entries we can't stat\n }\n }\n }\n\n async *keys(): AsyncIterable<string> {\n for await (const [name] of this.entries()) {\n yield name;\n }\n }\n\n async *values(): AsyncIterable<HostFileHandle | HostDirectoryHandle> {\n for await (const [, handle] of this.entries()) {\n yield handle;\n }\n }\n}\n\n/**\n * Create a directory handle backed by Node.js/Bun fs\n * Useful for server-side implementations\n *\n * @param rootPath - Absolute path to the directory on disk\n * @returns A HostDirectoryHandle implementation\n *\n * @example\n * import { createNodeDirectoryHandle } from \"@ricsam/quickjs-fs\";\n *\n * const handle = setupFs(context, {\n * getDirectory: async (path) => {\n * // Only allow access to /sandbox\n * if (!path.startsWith(\"/sandbox\")) {\n * throw new Error(\"Access denied\");\n * }\n * const realPath = path.replace(\"/sandbox\", \"/var/app/sandbox\");\n * return createNodeDirectoryHandle(realPath);\n * }\n * });\n */\nexport function createNodeDirectoryHandle(\n rootPath: string\n): HostDirectoryHandle {\n const name = rootPath.split(\"/\").pop() || \"\";\n return new NodeDirectoryHandle(name, rootPath);\n}\n\n// Re-export streaming implementation for convenience\nexport { BunWritableFileStream } from \"./bun-adapter.cjs\";\n\n// Export legacy buffered implementation for fallback scenarios\nexport { BufferedWritableFileStream };\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "828E7C8FF060601E64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/fs/src/bun-adapter.ts
|
|
3
|
+
class BunFileHandle {
|
|
4
|
+
name;
|
|
5
|
+
fullPath;
|
|
6
|
+
kind = "file";
|
|
7
|
+
constructor(name, fullPath) {
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.fullPath = fullPath;
|
|
10
|
+
}
|
|
11
|
+
async getFile() {
|
|
12
|
+
const file = Bun.file(this.fullPath);
|
|
13
|
+
const buffer = await file.arrayBuffer();
|
|
14
|
+
const stat = await Bun.file(this.fullPath).stat();
|
|
15
|
+
return new File([buffer], this.name, {
|
|
16
|
+
type: file.type,
|
|
17
|
+
lastModified: stat?.mtime?.getTime() ?? Date.now()
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async createWritable(options) {
|
|
21
|
+
return new BunWritableFileStream(this.fullPath, options?.keepExistingData ?? false);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class BunWritableFileStream {
|
|
26
|
+
sink = null;
|
|
27
|
+
buffer = null;
|
|
28
|
+
position = 0;
|
|
29
|
+
fileSize = 0;
|
|
30
|
+
streamingMode;
|
|
31
|
+
closed = false;
|
|
32
|
+
path;
|
|
33
|
+
initPromise = null;
|
|
34
|
+
constructor(path, keepExistingData) {
|
|
35
|
+
this.path = path;
|
|
36
|
+
if (keepExistingData) {
|
|
37
|
+
this.streamingMode = false;
|
|
38
|
+
this.initPromise = this.initBufferedModeWithExisting();
|
|
39
|
+
} else {
|
|
40
|
+
this.streamingMode = true;
|
|
41
|
+
this.initPromise = this.initStreamingMode();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async initStreamingMode() {
|
|
45
|
+
await Bun.write(this.path, "");
|
|
46
|
+
const file = Bun.file(this.path);
|
|
47
|
+
this.sink = file.writer({ highWaterMark: 64 * 1024 });
|
|
48
|
+
this.streamingMode = true;
|
|
49
|
+
this.buffer = null;
|
|
50
|
+
}
|
|
51
|
+
async initBufferedModeWithExisting() {
|
|
52
|
+
this.streamingMode = false;
|
|
53
|
+
try {
|
|
54
|
+
const file = Bun.file(this.path);
|
|
55
|
+
const exists = await file.exists();
|
|
56
|
+
if (exists) {
|
|
57
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
58
|
+
this.buffer = new Uint8Array(arrayBuffer);
|
|
59
|
+
this.fileSize = this.buffer.length;
|
|
60
|
+
this.position = this.buffer.length;
|
|
61
|
+
} else {
|
|
62
|
+
this.buffer = new Uint8Array(0);
|
|
63
|
+
this.fileSize = 0;
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
this.buffer = new Uint8Array(0);
|
|
67
|
+
this.fileSize = 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async switchToBufferedMode() {
|
|
71
|
+
if (!this.streamingMode)
|
|
72
|
+
return;
|
|
73
|
+
if (this.initPromise) {
|
|
74
|
+
await this.initPromise;
|
|
75
|
+
this.initPromise = null;
|
|
76
|
+
}
|
|
77
|
+
this.streamingMode = false;
|
|
78
|
+
if (this.sink) {
|
|
79
|
+
await this.sink.end();
|
|
80
|
+
this.sink = null;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const file = Bun.file(this.path);
|
|
84
|
+
const exists = await file.exists();
|
|
85
|
+
if (exists) {
|
|
86
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
87
|
+
this.buffer = new Uint8Array(arrayBuffer);
|
|
88
|
+
this.fileSize = this.buffer.length;
|
|
89
|
+
} else {
|
|
90
|
+
this.buffer = new Uint8Array(0);
|
|
91
|
+
this.fileSize = 0;
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
this.buffer = new Uint8Array(0);
|
|
95
|
+
this.fileSize = 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async write(data) {
|
|
99
|
+
if (this.initPromise) {
|
|
100
|
+
await this.initPromise;
|
|
101
|
+
this.initPromise = null;
|
|
102
|
+
}
|
|
103
|
+
if (this.closed) {
|
|
104
|
+
throw new TypeError("Cannot write to a closed stream");
|
|
105
|
+
}
|
|
106
|
+
let bytes;
|
|
107
|
+
if (typeof data === "string") {
|
|
108
|
+
bytes = new TextEncoder().encode(data);
|
|
109
|
+
} else if (data instanceof ArrayBuffer) {
|
|
110
|
+
bytes = new Uint8Array(data);
|
|
111
|
+
} else if (data instanceof Uint8Array) {
|
|
112
|
+
bytes = data;
|
|
113
|
+
} else if (data instanceof Blob) {
|
|
114
|
+
bytes = new Uint8Array(await data.arrayBuffer());
|
|
115
|
+
} else {
|
|
116
|
+
if (data.type === "seek") {
|
|
117
|
+
await this.seek(data.position ?? 0);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (data.type === "truncate") {
|
|
121
|
+
await this.truncate(data.size ?? 0);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (data.data === undefined)
|
|
125
|
+
return;
|
|
126
|
+
return this.write(data.data);
|
|
127
|
+
}
|
|
128
|
+
if (this.streamingMode && this.sink) {
|
|
129
|
+
const written = this.sink.write(bytes);
|
|
130
|
+
if (written === 0) {
|
|
131
|
+
await this.sink.flush();
|
|
132
|
+
this.sink.write(bytes);
|
|
133
|
+
}
|
|
134
|
+
this.position += bytes.length;
|
|
135
|
+
this.fileSize = Math.max(this.fileSize, this.position);
|
|
136
|
+
} else {
|
|
137
|
+
if (!this.buffer) {
|
|
138
|
+
this.buffer = new Uint8Array(0);
|
|
139
|
+
}
|
|
140
|
+
const neededSize = this.position + bytes.length;
|
|
141
|
+
if (neededSize > this.buffer.length) {
|
|
142
|
+
const newBuffer = new Uint8Array(neededSize);
|
|
143
|
+
newBuffer.set(this.buffer);
|
|
144
|
+
this.buffer = newBuffer;
|
|
145
|
+
}
|
|
146
|
+
this.buffer.set(bytes, this.position);
|
|
147
|
+
this.position += bytes.length;
|
|
148
|
+
this.fileSize = Math.max(this.fileSize, this.position);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async seek(position) {
|
|
152
|
+
if (this.initPromise) {
|
|
153
|
+
await this.initPromise;
|
|
154
|
+
this.initPromise = null;
|
|
155
|
+
}
|
|
156
|
+
if (this.closed) {
|
|
157
|
+
throw new TypeError("Cannot seek on a closed stream");
|
|
158
|
+
}
|
|
159
|
+
if (this.streamingMode) {
|
|
160
|
+
await this.switchToBufferedMode();
|
|
161
|
+
}
|
|
162
|
+
this.position = position;
|
|
163
|
+
}
|
|
164
|
+
async truncate(size) {
|
|
165
|
+
if (this.initPromise) {
|
|
166
|
+
await this.initPromise;
|
|
167
|
+
this.initPromise = null;
|
|
168
|
+
}
|
|
169
|
+
if (this.closed) {
|
|
170
|
+
throw new TypeError("Cannot truncate a closed stream");
|
|
171
|
+
}
|
|
172
|
+
if (this.streamingMode) {
|
|
173
|
+
await this.switchToBufferedMode();
|
|
174
|
+
}
|
|
175
|
+
if (!this.buffer) {
|
|
176
|
+
this.buffer = new Uint8Array(0);
|
|
177
|
+
}
|
|
178
|
+
if (size < this.buffer.length) {
|
|
179
|
+
this.buffer = this.buffer.slice(0, size);
|
|
180
|
+
} else if (size > this.buffer.length) {
|
|
181
|
+
const newBuffer = new Uint8Array(size);
|
|
182
|
+
newBuffer.set(this.buffer);
|
|
183
|
+
this.buffer = newBuffer;
|
|
184
|
+
}
|
|
185
|
+
this.fileSize = size;
|
|
186
|
+
if (this.position > size) {
|
|
187
|
+
this.position = size;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async close() {
|
|
191
|
+
if (this.initPromise) {
|
|
192
|
+
await this.initPromise;
|
|
193
|
+
this.initPromise = null;
|
|
194
|
+
}
|
|
195
|
+
if (this.closed)
|
|
196
|
+
return;
|
|
197
|
+
this.closed = true;
|
|
198
|
+
if (this.streamingMode && this.sink) {
|
|
199
|
+
await this.sink.end();
|
|
200
|
+
this.sink = null;
|
|
201
|
+
} else if (this.buffer) {
|
|
202
|
+
await Bun.write(this.path, this.buffer);
|
|
203
|
+
this.buffer = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async abort(_reason) {
|
|
207
|
+
if (this.initPromise) {
|
|
208
|
+
this.initPromise = null;
|
|
209
|
+
}
|
|
210
|
+
if (this.closed)
|
|
211
|
+
return;
|
|
212
|
+
this.closed = true;
|
|
213
|
+
if (this.sink) {
|
|
214
|
+
await this.sink.end();
|
|
215
|
+
this.sink = null;
|
|
216
|
+
}
|
|
217
|
+
this.buffer = null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
class BunDirectoryHandle {
|
|
222
|
+
name;
|
|
223
|
+
fullPath;
|
|
224
|
+
kind = "directory";
|
|
225
|
+
constructor(name, fullPath) {
|
|
226
|
+
this.name = name;
|
|
227
|
+
this.fullPath = fullPath;
|
|
228
|
+
}
|
|
229
|
+
async getFileHandle(name, options) {
|
|
230
|
+
const path = `${this.fullPath}/${name}`;
|
|
231
|
+
const file = Bun.file(path);
|
|
232
|
+
const exists = await file.exists();
|
|
233
|
+
if (!exists && !options?.create) {
|
|
234
|
+
throw new DOMException(`File not found: ${name}`, "NotFoundError");
|
|
235
|
+
}
|
|
236
|
+
if (!exists && options?.create) {
|
|
237
|
+
await Bun.write(path, "");
|
|
238
|
+
}
|
|
239
|
+
return new BunFileHandle(name, path);
|
|
240
|
+
}
|
|
241
|
+
async getDirectoryHandle(name, options) {
|
|
242
|
+
const path = `${this.fullPath}/${name}`;
|
|
243
|
+
try {
|
|
244
|
+
const stat = await Bun.file(path).stat();
|
|
245
|
+
if (!stat) {
|
|
246
|
+
throw new Error("Not found");
|
|
247
|
+
}
|
|
248
|
+
} catch {
|
|
249
|
+
if (!options?.create) {
|
|
250
|
+
throw new DOMException(`Directory not found: ${name}`, "NotFoundError");
|
|
251
|
+
}
|
|
252
|
+
await Bun.$`mkdir -p ${path}`;
|
|
253
|
+
}
|
|
254
|
+
return new BunDirectoryHandle(name, path);
|
|
255
|
+
}
|
|
256
|
+
async removeEntry(name, options) {
|
|
257
|
+
const path = `${this.fullPath}/${name}`;
|
|
258
|
+
if (options?.recursive) {
|
|
259
|
+
await Bun.$`rm -rf ${path}`;
|
|
260
|
+
} else {
|
|
261
|
+
await Bun.$`rm ${path}`;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async resolve(possibleDescendant) {
|
|
265
|
+
const descendantPath = possibleDescendant instanceof BunFileHandle || possibleDescendant instanceof BunDirectoryHandle ? possibleDescendant.fullPath : null;
|
|
266
|
+
if (!descendantPath || !descendantPath.startsWith(this.fullPath)) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
const relativePath = descendantPath.slice(this.fullPath.length);
|
|
270
|
+
return relativePath.split("/").filter(Boolean);
|
|
271
|
+
}
|
|
272
|
+
async* entries() {
|
|
273
|
+
const result = await Bun.$`ls -1 ${this.fullPath}`.text();
|
|
274
|
+
const names = result.trim().split(`
|
|
275
|
+
`).filter(Boolean);
|
|
276
|
+
for (const name of names) {
|
|
277
|
+
const path = `${this.fullPath}/${name}`;
|
|
278
|
+
try {
|
|
279
|
+
const stat = await Bun.file(path).stat();
|
|
280
|
+
if (stat && typeof stat.isDirectory === "function" && stat.isDirectory()) {
|
|
281
|
+
yield [name, new BunDirectoryHandle(name, path)];
|
|
282
|
+
} else {
|
|
283
|
+
yield [name, new BunFileHandle(name, path)];
|
|
284
|
+
}
|
|
285
|
+
} catch {}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
async* keys() {
|
|
289
|
+
for await (const [name] of this.entries()) {
|
|
290
|
+
yield name;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async* values() {
|
|
294
|
+
for await (const [, handle] of this.entries()) {
|
|
295
|
+
yield handle;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function createBunDirectoryHandle(rootPath) {
|
|
300
|
+
const name = rootPath.split("/").pop() || "";
|
|
301
|
+
return new BunDirectoryHandle(name, rootPath);
|
|
302
|
+
}
|
|
303
|
+
export {
|
|
304
|
+
createBunDirectoryHandle,
|
|
305
|
+
BunWritableFileStream,
|
|
306
|
+
BunFileHandle,
|
|
307
|
+
BunDirectoryHandle
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
//# debugId=345EB45BBDB0764164756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/bun-adapter.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type {\n HostDirectoryHandle,\n HostFileHandle,\n HostWritableFileStream,\n WriteParams,\n} from \"./types.mjs\";\n\n/**\n * Bun file handle implementation with true streaming writes\n */\nclass BunFileHandle implements HostFileHandle {\n readonly kind = \"file\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFile(): Promise<File> {\n const file = Bun.file(this.fullPath);\n const buffer = await file.arrayBuffer();\n const stat = await Bun.file(this.fullPath).stat();\n\n return new File([buffer], this.name, {\n type: file.type,\n lastModified: stat?.mtime?.getTime() ?? Date.now(),\n });\n }\n\n async createWritable(\n options?: { keepExistingData?: boolean }\n ): Promise<HostWritableFileStream> {\n return new BunWritableFileStream(\n this.fullPath,\n options?.keepExistingData ?? false\n );\n }\n}\n\n/**\n * Streaming writable file stream using Bun's FileSink\n *\n * Uses a hybrid approach:\n * - Sequential writes use FileSink for true streaming (no full buffering)\n * - Falls back to buffered mode if seek/truncate is called\n *\n * This enables streaming large files to disk without memory issues\n * while maintaining full API compatibility.\n */\nclass BunWritableFileStream implements HostWritableFileStream {\n private sink: ReturnType<ReturnType<typeof Bun.file>[\"writer\"]> | null = null;\n private buffer: Uint8Array | null = null;\n private position = 0;\n private fileSize = 0;\n private streamingMode: boolean;\n private closed = false;\n private readonly path: string;\n private initPromise: Promise<void> | null = null;\n\n constructor(path: string, keepExistingData: boolean) {\n this.path = path;\n\n if (keepExistingData) {\n // Need to read existing data first - can't use streaming for this case\n this.streamingMode = false;\n // Start async initialization\n this.initPromise = this.initBufferedModeWithExisting();\n } else {\n // Start fresh - truncate and use streaming\n this.streamingMode = true;\n // Truncate the file first, then open the writer\n this.initPromise = this.initStreamingMode();\n }\n }\n\n private async initStreamingMode(): Promise<void> {\n // Truncate the file first to ensure clean slate\n await Bun.write(this.path, \"\");\n const file = Bun.file(this.path);\n this.sink = file.writer({ highWaterMark: 64 * 1024 }); // 64KB buffer\n this.streamingMode = true;\n this.buffer = null;\n }\n\n private async initBufferedModeWithExisting(): Promise<void> {\n this.streamingMode = false;\n\n try {\n const file = Bun.file(this.path);\n const exists = await file.exists();\n if (exists) {\n const arrayBuffer = await file.arrayBuffer();\n this.buffer = new Uint8Array(arrayBuffer);\n this.fileSize = this.buffer.length;\n this.position = this.buffer.length; // Position at end\n } else {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n } catch {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n }\n\n private async switchToBufferedMode(): Promise<void> {\n if (!this.streamingMode) return;\n\n // Ensure any pending init is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n this.streamingMode = false;\n\n if (this.sink) {\n // Flush and close the sink before switching modes\n await this.sink.end();\n this.sink = null;\n }\n\n // Read what we've written so far\n try {\n const file = Bun.file(this.path);\n const exists = await file.exists();\n if (exists) {\n const arrayBuffer = await file.arrayBuffer();\n this.buffer = new Uint8Array(arrayBuffer);\n this.fileSize = this.buffer.length;\n } else {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n } catch {\n this.buffer = new Uint8Array(0);\n this.fileSize = 0;\n }\n }\n\n async write(\n data: string | ArrayBuffer | Uint8Array | Blob | WriteParams\n ): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) {\n throw new TypeError(\"Cannot write to a closed stream\");\n }\n\n let bytes: Uint8Array;\n\n if (typeof data === \"string\") {\n bytes = new TextEncoder().encode(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else if (data instanceof Uint8Array) {\n bytes = data;\n } else if (data instanceof Blob) {\n bytes = new Uint8Array(await data.arrayBuffer());\n } else {\n // WriteParams\n if (data.type === \"seek\") {\n await this.seek(data.position ?? 0);\n return;\n }\n if (data.type === \"truncate\") {\n await this.truncate(data.size ?? 0);\n return;\n }\n // type === \"write\"\n if (data.data === undefined) return;\n return this.write(data.data);\n }\n\n if (this.streamingMode && this.sink) {\n // True streaming mode - write directly to FileSink\n const written = this.sink.write(bytes);\n if (written === 0) {\n // Backpressure: buffer is full, need to flush\n await this.sink.flush();\n this.sink.write(bytes);\n }\n this.position += bytes.length;\n this.fileSize = Math.max(this.fileSize, this.position);\n } else {\n // Buffered mode - expand buffer as needed\n if (!this.buffer) {\n this.buffer = new Uint8Array(0);\n }\n\n const neededSize = this.position + bytes.length;\n if (neededSize > this.buffer.length) {\n const newBuffer = new Uint8Array(neededSize);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n\n this.buffer.set(bytes, this.position);\n this.position += bytes.length;\n this.fileSize = Math.max(this.fileSize, this.position);\n }\n }\n\n async seek(position: number): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) {\n throw new TypeError(\"Cannot seek on a closed stream\");\n }\n\n // Seeking requires buffered mode since FileSink doesn't support it\n if (this.streamingMode) {\n await this.switchToBufferedMode();\n }\n\n this.position = position;\n }\n\n async truncate(size: number): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) {\n throw new TypeError(\"Cannot truncate a closed stream\");\n }\n\n // Truncate requires buffered mode since FileSink doesn't support it\n if (this.streamingMode) {\n await this.switchToBufferedMode();\n }\n\n if (!this.buffer) {\n this.buffer = new Uint8Array(0);\n }\n\n if (size < this.buffer.length) {\n this.buffer = this.buffer.slice(0, size);\n } else if (size > this.buffer.length) {\n const newBuffer = new Uint8Array(size);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n\n this.fileSize = size;\n if (this.position > size) {\n this.position = size;\n }\n }\n\n async close(): Promise<void> {\n // Ensure initialization is complete\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n\n if (this.closed) return;\n this.closed = true;\n\n if (this.streamingMode && this.sink) {\n // Streaming mode - just end the sink\n await this.sink.end();\n this.sink = null;\n } else if (this.buffer) {\n // Buffered mode - write buffer to disk\n await Bun.write(this.path, this.buffer);\n this.buffer = null;\n }\n }\n\n async abort(_reason?: unknown): Promise<void> {\n // Cancel any pending initialization\n if (this.initPromise) {\n // Don't await - we're aborting\n this.initPromise = null;\n }\n\n if (this.closed) return;\n this.closed = true;\n\n if (this.sink) {\n await this.sink.end();\n this.sink = null;\n }\n this.buffer = null;\n\n // Optionally delete the partially written file\n // For now, we leave it as-is to match the existing behavior\n }\n}\n\n/**\n * Bun directory handle implementation\n */\nclass BunDirectoryHandle implements HostDirectoryHandle {\n readonly kind = \"directory\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFileHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostFileHandle> {\n const path = `${this.fullPath}/${name}`;\n const file = Bun.file(path);\n const exists = await file.exists();\n\n if (!exists && !options?.create) {\n throw new DOMException(`File not found: ${name}`, \"NotFoundError\");\n }\n\n if (!exists && options?.create) {\n await Bun.write(path, \"\");\n }\n\n return new BunFileHandle(name, path);\n }\n\n async getDirectoryHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostDirectoryHandle> {\n const path = `${this.fullPath}/${name}`;\n\n try {\n const stat = await Bun.file(path).stat();\n if (!stat) {\n throw new Error(\"Not found\");\n }\n } catch {\n if (!options?.create) {\n throw new DOMException(`Directory not found: ${name}`, \"NotFoundError\");\n }\n // Create directory\n await Bun.$`mkdir -p ${path}`;\n }\n\n return new BunDirectoryHandle(name, path);\n }\n\n async removeEntry(\n name: string,\n options?: { recursive?: boolean }\n ): Promise<void> {\n const path = `${this.fullPath}/${name}`;\n\n if (options?.recursive) {\n await Bun.$`rm -rf ${path}`;\n } else {\n await Bun.$`rm ${path}`;\n }\n }\n\n async resolve(\n possibleDescendant: HostFileHandle | HostDirectoryHandle\n ): Promise<string[] | null> {\n // Get the paths and compare\n const descendantPath =\n possibleDescendant instanceof BunFileHandle ||\n possibleDescendant instanceof BunDirectoryHandle\n ? (possibleDescendant as unknown as { fullPath: string }).fullPath\n : null;\n\n if (!descendantPath || !descendantPath.startsWith(this.fullPath)) {\n return null;\n }\n\n const relativePath = descendantPath.slice(this.fullPath.length);\n return relativePath.split(\"/\").filter(Boolean);\n }\n\n async *entries(): AsyncIterable<\n [string, HostFileHandle | HostDirectoryHandle]\n > {\n const result = await Bun.$`ls -1 ${this.fullPath}`.text();\n const names = result.trim().split(\"\\n\").filter(Boolean);\n\n for (const name of names) {\n const path = `${this.fullPath}/${name}`;\n try {\n const stat = await Bun.file(path).stat();\n if (stat && typeof stat.isDirectory === 'function' && stat.isDirectory()) {\n yield [name, new BunDirectoryHandle(name, path)];\n } else {\n yield [name, new BunFileHandle(name, path)];\n }\n } catch {\n // Skip entries we can't stat\n }\n }\n }\n\n async *keys(): AsyncIterable<string> {\n for await (const [name] of this.entries()) {\n yield name;\n }\n }\n\n async *values(): AsyncIterable<HostFileHandle | HostDirectoryHandle> {\n for await (const [, handle] of this.entries()) {\n yield handle;\n }\n }\n}\n\n/**\n * Create a directory handle backed by Bun's file APIs with streaming support\n *\n * This implementation uses Bun's FileSink for streaming writes, which enables\n * writing large files without buffering them entirely in memory.\n *\n * @param rootPath - Absolute path to the directory on disk\n * @returns A HostDirectoryHandle implementation with streaming support\n *\n * @example\n * import { createBunDirectoryHandle } from \"@ricsam/quickjs-fs\";\n *\n * const handle = setupFs(context, {\n * getDirectory: async (path) => {\n * // Only allow access to /sandbox\n * if (!path.startsWith(\"/sandbox\")) {\n * throw new Error(\"Access denied\");\n * }\n * const realPath = path.replace(\"/sandbox\", \"/var/app/sandbox\");\n * return createBunDirectoryHandle(realPath);\n * }\n * });\n */\nexport function createBunDirectoryHandle(\n rootPath: string\n): HostDirectoryHandle {\n const name = rootPath.split(\"/\").pop() || \"\";\n return new BunDirectoryHandle(name, rootPath);\n}\n\n// Re-export for convenience\nexport { BunWritableFileStream, BunFileHandle, BunDirectoryHandle };\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "345EB45BBDB0764164756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/fs/src/node-adapter.ts
|
|
3
|
+
import { BunWritableFileStream } from "./bun-adapter.mjs";
|
|
4
|
+
import { BunWritableFileStream as BunWritableFileStream2 } from "./bun-adapter.mjs";
|
|
5
|
+
|
|
3
6
|
class NodeFileHandle {
|
|
4
7
|
name;
|
|
5
8
|
fullPath;
|
|
@@ -18,11 +21,11 @@ class NodeFileHandle {
|
|
|
18
21
|
});
|
|
19
22
|
}
|
|
20
23
|
async createWritable(options) {
|
|
21
|
-
return new
|
|
24
|
+
return new BunWritableFileStream(this.fullPath, options?.keepExistingData ?? false);
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
class
|
|
28
|
+
class BufferedWritableFileStream {
|
|
26
29
|
path;
|
|
27
30
|
buffer;
|
|
28
31
|
position = 0;
|
|
@@ -183,7 +186,9 @@ function createNodeDirectoryHandle(rootPath) {
|
|
|
183
186
|
return new NodeDirectoryHandle(name, rootPath);
|
|
184
187
|
}
|
|
185
188
|
export {
|
|
186
|
-
createNodeDirectoryHandle
|
|
189
|
+
createNodeDirectoryHandle,
|
|
190
|
+
BunWritableFileStream2 as BunWritableFileStream,
|
|
191
|
+
BufferedWritableFileStream
|
|
187
192
|
};
|
|
188
193
|
|
|
189
|
-
//# debugId=
|
|
194
|
+
//# debugId=4AFA3CC27041FE3064756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/node-adapter.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import type {\n HostDirectoryHandle,\n HostFileHandle,\n HostWritableFileStream,\n WriteParams,\n} from \"./types.mjs\";\n\n/**\n * Node/Bun file handle implementation\n */\nclass NodeFileHandle implements HostFileHandle {\n readonly kind = \"file\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFile(): Promise<File> {\n const file = Bun.file(this.fullPath);\n const buffer = await file.arrayBuffer();\n const stat = await Bun.file(this.fullPath).stat();\n\n return new File([buffer], this.name, {\n type: file.type,\n lastModified: stat?.mtime?.getTime() ?? Date.now(),\n });\n }\n\n async createWritable(\n options?: { keepExistingData?: boolean }\n ): Promise<HostWritableFileStream> {\n return new
|
|
5
|
+
"import type {\n HostDirectoryHandle,\n HostFileHandle,\n HostWritableFileStream,\n WriteParams,\n} from \"./types.mjs\";\nimport { BunWritableFileStream } from \"./bun-adapter.mjs\";\n\n/**\n * Node/Bun file handle implementation\n *\n * Uses BunWritableFileStream for streaming writes when creating writable streams.\n */\nclass NodeFileHandle implements HostFileHandle {\n readonly kind = \"file\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFile(): Promise<File> {\n const file = Bun.file(this.fullPath);\n const buffer = await file.arrayBuffer();\n const stat = await Bun.file(this.fullPath).stat();\n\n return new File([buffer], this.name, {\n type: file.type,\n lastModified: stat?.mtime?.getTime() ?? Date.now(),\n });\n }\n\n async createWritable(\n options?: { keepExistingData?: boolean }\n ): Promise<HostWritableFileStream> {\n // Use streaming implementation by default\n return new BunWritableFileStream(\n this.fullPath,\n options?.keepExistingData ?? false\n );\n }\n}\n\n/**\n * Buffered writable file stream implementation (legacy)\n *\n * WARNING: This implementation buffers ALL data in memory and only writes\n * to disk on close(). For large files, use BunWritableFileStream instead\n * which provides true streaming writes.\n *\n * @deprecated Use BunWritableFileStream for new code - it has better memory\n * characteristics for large file writes.\n */\nclass BufferedWritableFileStream implements HostWritableFileStream {\n private buffer: Uint8Array;\n private position = 0;\n\n constructor(\n private readonly path: string,\n keepExistingData: boolean\n ) {\n if (keepExistingData) {\n // Read existing file synchronously for simplicity\n try {\n const file = Bun.file(this.path);\n // Note: This is simplified; in production you'd want async\n this.buffer = new Uint8Array(0);\n file.arrayBuffer().then((ab) => {\n this.buffer = new Uint8Array(ab);\n });\n } catch {\n this.buffer = new Uint8Array(0);\n }\n } else {\n this.buffer = new Uint8Array(0);\n }\n }\n\n async write(\n data: string | ArrayBuffer | Uint8Array | Blob | WriteParams\n ): Promise<void> {\n let bytes: Uint8Array;\n\n if (typeof data === \"string\") {\n bytes = new TextEncoder().encode(data);\n } else if (data instanceof ArrayBuffer) {\n bytes = new Uint8Array(data);\n } else if (data instanceof Uint8Array) {\n bytes = data;\n } else if (data instanceof Blob) {\n bytes = new Uint8Array(await data.arrayBuffer());\n } else {\n // WriteParams\n if (data.type === \"seek\") {\n this.position = data.position ?? 0;\n return;\n }\n if (data.type === \"truncate\") {\n this.buffer = this.buffer.slice(0, data.size ?? 0);\n if (this.position > this.buffer.length) {\n this.position = this.buffer.length;\n }\n return;\n }\n // type === \"write\"\n if (data.data === undefined) return;\n return this.write(data.data);\n }\n\n // Expand buffer if needed\n const neededSize = this.position + bytes.length;\n if (neededSize > this.buffer.length) {\n const newBuffer = new Uint8Array(neededSize);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n\n this.buffer.set(bytes, this.position);\n this.position += bytes.length;\n }\n\n async seek(position: number): Promise<void> {\n this.position = position;\n }\n\n async truncate(size: number): Promise<void> {\n if (size < this.buffer.length) {\n this.buffer = this.buffer.slice(0, size);\n } else if (size > this.buffer.length) {\n const newBuffer = new Uint8Array(size);\n newBuffer.set(this.buffer);\n this.buffer = newBuffer;\n }\n if (this.position > size) {\n this.position = size;\n }\n }\n\n async close(): Promise<void> {\n await Bun.write(this.path, this.buffer);\n }\n\n async abort(_reason?: unknown): Promise<void> {\n // Don't write, just discard\n this.buffer = new Uint8Array(0);\n }\n}\n\n/**\n * Node/Bun directory handle implementation\n */\nclass NodeDirectoryHandle implements HostDirectoryHandle {\n readonly kind = \"directory\" as const;\n\n constructor(\n public readonly name: string,\n private readonly fullPath: string\n ) {}\n\n async getFileHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostFileHandle> {\n const path = `${this.fullPath}/${name}`;\n const file = Bun.file(path);\n const exists = await file.exists();\n\n if (!exists && !options?.create) {\n throw new DOMException(`File not found: ${name}`, \"NotFoundError\");\n }\n\n if (!exists && options?.create) {\n await Bun.write(path, \"\");\n }\n\n return new NodeFileHandle(name, path);\n }\n\n async getDirectoryHandle(\n name: string,\n options?: { create?: boolean }\n ): Promise<HostDirectoryHandle> {\n const path = `${this.fullPath}/${name}`;\n\n try {\n const stat = await Bun.file(path).stat();\n if (!stat) {\n throw new Error(\"Not found\");\n }\n } catch {\n if (!options?.create) {\n throw new DOMException(`Directory not found: ${name}`, \"NotFoundError\");\n }\n // Create directory\n await Bun.$`mkdir -p ${path}`;\n }\n\n return new NodeDirectoryHandle(name, path);\n }\n\n async removeEntry(\n name: string,\n options?: { recursive?: boolean }\n ): Promise<void> {\n const path = `${this.fullPath}/${name}`;\n\n if (options?.recursive) {\n await Bun.$`rm -rf ${path}`;\n } else {\n await Bun.$`rm ${path}`;\n }\n }\n\n async resolve(\n possibleDescendant: HostFileHandle | HostDirectoryHandle\n ): Promise<string[] | null> {\n // Get the paths and compare\n const descendantPath =\n possibleDescendant instanceof NodeFileHandle ||\n possibleDescendant instanceof NodeDirectoryHandle\n ? (possibleDescendant as unknown as { fullPath: string }).fullPath\n : null;\n\n if (!descendantPath || !descendantPath.startsWith(this.fullPath)) {\n return null;\n }\n\n const relativePath = descendantPath.slice(this.fullPath.length);\n return relativePath.split(\"/\").filter(Boolean);\n }\n\n async *entries(): AsyncIterable<\n [string, HostFileHandle | HostDirectoryHandle]\n > {\n const result = await Bun.$`ls -1 ${this.fullPath}`.text();\n const names = result.trim().split(\"\\n\").filter(Boolean);\n\n for (const name of names) {\n const path = `${this.fullPath}/${name}`;\n try {\n const stat = await Bun.file(path).stat();\n if (stat && typeof stat.isDirectory === 'function' && stat.isDirectory()) {\n yield [name, new NodeDirectoryHandle(name, path)];\n } else {\n yield [name, new NodeFileHandle(name, path)];\n }\n } catch {\n // Skip entries we can't stat\n }\n }\n }\n\n async *keys(): AsyncIterable<string> {\n for await (const [name] of this.entries()) {\n yield name;\n }\n }\n\n async *values(): AsyncIterable<HostFileHandle | HostDirectoryHandle> {\n for await (const [, handle] of this.entries()) {\n yield handle;\n }\n }\n}\n\n/**\n * Create a directory handle backed by Node.js/Bun fs\n * Useful for server-side implementations\n *\n * @param rootPath - Absolute path to the directory on disk\n * @returns A HostDirectoryHandle implementation\n *\n * @example\n * import { createNodeDirectoryHandle } from \"@ricsam/quickjs-fs\";\n *\n * const handle = setupFs(context, {\n * getDirectory: async (path) => {\n * // Only allow access to /sandbox\n * if (!path.startsWith(\"/sandbox\")) {\n * throw new Error(\"Access denied\");\n * }\n * const realPath = path.replace(\"/sandbox\", \"/var/app/sandbox\");\n * return createNodeDirectoryHandle(realPath);\n * }\n * });\n */\nexport function createNodeDirectoryHandle(\n rootPath: string\n): HostDirectoryHandle {\n const name = rootPath.split(\"/\").pop() || \"\";\n return new NodeDirectoryHandle(name, rootPath);\n}\n\n// Re-export streaming implementation for convenience\nexport { BunWritableFileStream } from \"./bun-adapter.mjs\";\n\n// Export legacy buffered implementation for fallback scenarios\nexport { BufferedWritableFileStream };\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "4AFA3CC27041FE3064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { HostDirectoryHandle, HostFileHandle, HostWritableFileStream, WriteParams } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Bun file handle implementation with true streaming writes
|
|
4
|
+
*/
|
|
5
|
+
declare class BunFileHandle implements HostFileHandle {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
private readonly fullPath;
|
|
8
|
+
readonly kind: "file";
|
|
9
|
+
constructor(name: string, fullPath: string);
|
|
10
|
+
getFile(): Promise<File>;
|
|
11
|
+
createWritable(options?: {
|
|
12
|
+
keepExistingData?: boolean;
|
|
13
|
+
}): Promise<HostWritableFileStream>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Streaming writable file stream using Bun's FileSink
|
|
17
|
+
*
|
|
18
|
+
* Uses a hybrid approach:
|
|
19
|
+
* - Sequential writes use FileSink for true streaming (no full buffering)
|
|
20
|
+
* - Falls back to buffered mode if seek/truncate is called
|
|
21
|
+
*
|
|
22
|
+
* This enables streaming large files to disk without memory issues
|
|
23
|
+
* while maintaining full API compatibility.
|
|
24
|
+
*/
|
|
25
|
+
declare class BunWritableFileStream implements HostWritableFileStream {
|
|
26
|
+
private sink;
|
|
27
|
+
private buffer;
|
|
28
|
+
private position;
|
|
29
|
+
private fileSize;
|
|
30
|
+
private streamingMode;
|
|
31
|
+
private closed;
|
|
32
|
+
private readonly path;
|
|
33
|
+
private initPromise;
|
|
34
|
+
constructor(path: string, keepExistingData: boolean);
|
|
35
|
+
private initStreamingMode;
|
|
36
|
+
private initBufferedModeWithExisting;
|
|
37
|
+
private switchToBufferedMode;
|
|
38
|
+
write(data: string | ArrayBuffer | Uint8Array | Blob | WriteParams): Promise<void>;
|
|
39
|
+
seek(position: number): Promise<void>;
|
|
40
|
+
truncate(size: number): Promise<void>;
|
|
41
|
+
close(): Promise<void>;
|
|
42
|
+
abort(_reason?: unknown): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Bun directory handle implementation
|
|
46
|
+
*/
|
|
47
|
+
declare class BunDirectoryHandle implements HostDirectoryHandle {
|
|
48
|
+
readonly name: string;
|
|
49
|
+
private readonly fullPath;
|
|
50
|
+
readonly kind: "directory";
|
|
51
|
+
constructor(name: string, fullPath: string);
|
|
52
|
+
getFileHandle(name: string, options?: {
|
|
53
|
+
create?: boolean;
|
|
54
|
+
}): Promise<HostFileHandle>;
|
|
55
|
+
getDirectoryHandle(name: string, options?: {
|
|
56
|
+
create?: boolean;
|
|
57
|
+
}): Promise<HostDirectoryHandle>;
|
|
58
|
+
removeEntry(name: string, options?: {
|
|
59
|
+
recursive?: boolean;
|
|
60
|
+
}): Promise<void>;
|
|
61
|
+
resolve(possibleDescendant: HostFileHandle | HostDirectoryHandle): Promise<string[] | null>;
|
|
62
|
+
entries(): AsyncIterable<[
|
|
63
|
+
string,
|
|
64
|
+
HostFileHandle | HostDirectoryHandle
|
|
65
|
+
]>;
|
|
66
|
+
keys(): AsyncIterable<string>;
|
|
67
|
+
values(): AsyncIterable<HostFileHandle | HostDirectoryHandle>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a directory handle backed by Bun's file APIs with streaming support
|
|
71
|
+
*
|
|
72
|
+
* This implementation uses Bun's FileSink for streaming writes, which enables
|
|
73
|
+
* writing large files without buffering them entirely in memory.
|
|
74
|
+
*
|
|
75
|
+
* @param rootPath - Absolute path to the directory on disk
|
|
76
|
+
* @returns A HostDirectoryHandle implementation with streaming support
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* import { createBunDirectoryHandle } from "@ricsam/quickjs-fs";
|
|
80
|
+
*
|
|
81
|
+
* const handle = setupFs(context, {
|
|
82
|
+
* getDirectory: async (path) => {
|
|
83
|
+
* // Only allow access to /sandbox
|
|
84
|
+
* if (!path.startsWith("/sandbox")) {
|
|
85
|
+
* throw new Error("Access denied");
|
|
86
|
+
* }
|
|
87
|
+
* const realPath = path.replace("/sandbox", "/var/app/sandbox");
|
|
88
|
+
* return createBunDirectoryHandle(realPath);
|
|
89
|
+
* }
|
|
90
|
+
* });
|
|
91
|
+
*/
|
|
92
|
+
export declare function createBunDirectoryHandle(rootPath: string): HostDirectoryHandle;
|
|
93
|
+
export { BunWritableFileStream, BunFileHandle, BunDirectoryHandle };
|
|
@@ -1,4 +1,25 @@
|
|
|
1
|
-
import type { HostDirectoryHandle } from "./types.ts";
|
|
1
|
+
import type { HostDirectoryHandle, HostWritableFileStream, WriteParams } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Buffered writable file stream implementation (legacy)
|
|
4
|
+
*
|
|
5
|
+
* WARNING: This implementation buffers ALL data in memory and only writes
|
|
6
|
+
* to disk on close(). For large files, use BunWritableFileStream instead
|
|
7
|
+
* which provides true streaming writes.
|
|
8
|
+
*
|
|
9
|
+
* @deprecated Use BunWritableFileStream for new code - it has better memory
|
|
10
|
+
* characteristics for large file writes.
|
|
11
|
+
*/
|
|
12
|
+
declare class BufferedWritableFileStream implements HostWritableFileStream {
|
|
13
|
+
private readonly path;
|
|
14
|
+
private buffer;
|
|
15
|
+
private position;
|
|
16
|
+
constructor(path: string, keepExistingData: boolean);
|
|
17
|
+
write(data: string | ArrayBuffer | Uint8Array | Blob | WriteParams): Promise<void>;
|
|
18
|
+
seek(position: number): Promise<void>;
|
|
19
|
+
truncate(size: number): Promise<void>;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
abort(_reason?: unknown): Promise<void>;
|
|
22
|
+
}
|
|
2
23
|
/**
|
|
3
24
|
* Create a directory handle backed by Node.js/Bun fs
|
|
4
25
|
* Useful for server-side implementations
|
|
@@ -21,3 +42,5 @@ import type { HostDirectoryHandle } from "./types.ts";
|
|
|
21
42
|
* });
|
|
22
43
|
*/
|
|
23
44
|
export declare function createNodeDirectoryHandle(rootPath: string): HostDirectoryHandle;
|
|
45
|
+
export { BunWritableFileStream } from "./bun-adapter.ts";
|
|
46
|
+
export { BufferedWritableFileStream };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/quickjs-fs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"main": "./dist/cjs/index.cjs",
|
|
5
5
|
"types": "./dist/types/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"typecheck": "tsc --noEmit"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@ricsam/quickjs-core": "^0.2.
|
|
19
|
+
"@ricsam/quickjs-core": "^0.2.9",
|
|
20
20
|
"quickjs-emscripten": "^0.31.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|