@ai-ecoverse/slicc-kernel 1.23.0 → 1.24.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.
- package/README.md +2 -2
- package/dist/kernel-worker.js +1733 -1634
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node.js +185 -28
- package/dist/node.js.map +3 -3
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -177,123 +177,6 @@ function openMeta(name2, factory) {
|
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
// src/mount/media.ts
|
|
181
|
-
var MEDIA_STORE = "handles";
|
|
182
|
-
function memoryMedia() {
|
|
183
|
-
const handles = /* @__PURE__ */ new Map();
|
|
184
|
-
return {
|
|
185
|
-
get: async (id) => handles.get(id),
|
|
186
|
-
put: async (id, handle) => void handles.set(id, handle)
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
function settle2(request2, value) {
|
|
190
|
-
return new Promise((resolve, reject) => {
|
|
191
|
-
request2.onsuccess = () => resolve(value());
|
|
192
|
-
request2.onerror = () => reject(request2.error);
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
function openMedia(name2, factory) {
|
|
196
|
-
const request2 = factory.open(name2, 1);
|
|
197
|
-
request2.onupgradeneeded = () => request2.result.createObjectStore(MEDIA_STORE);
|
|
198
|
-
return settle2(request2, () => {
|
|
199
|
-
const db = request2.result;
|
|
200
|
-
const store = (mode) => db.transaction(MEDIA_STORE, mode).objectStore(MEDIA_STORE);
|
|
201
|
-
return {
|
|
202
|
-
get: (id) => {
|
|
203
|
-
const got = store("readonly").get(id);
|
|
204
|
-
return settle2(got, () => got.result);
|
|
205
|
-
},
|
|
206
|
-
put: (id, handle) => settle2(store("readwrite").put(handle, id), () => void 0)
|
|
207
|
-
};
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// src/pin.ts
|
|
212
|
-
function pinScript(url) {
|
|
213
|
-
try {
|
|
214
|
-
const request2 = new XMLHttpRequest();
|
|
215
|
-
request2.open("GET", url.href, false);
|
|
216
|
-
request2.send();
|
|
217
|
-
if (request2.status !== 200) return url.href;
|
|
218
|
-
const source = `${request2.responseText}
|
|
219
|
-
//# sourceURL=${url.href}
|
|
220
|
-
`;
|
|
221
|
-
return URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
|
|
222
|
-
} catch {
|
|
223
|
-
return url.href;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// src/cdp/port.ts
|
|
228
|
-
function portCdpHook(ask, gone) {
|
|
229
|
-
return (request2) => new Promise((resolve, reject) => {
|
|
230
|
-
const { port1, port2 } = new MessageChannel();
|
|
231
|
-
let closed = false;
|
|
232
|
-
const conn = {
|
|
233
|
-
send: (m) => {
|
|
234
|
-
if (!closed) port1.postMessage({ m });
|
|
235
|
-
},
|
|
236
|
-
onmessage: null,
|
|
237
|
-
onclose: null,
|
|
238
|
-
close: () => {
|
|
239
|
-
if (closed) return;
|
|
240
|
-
closed = true;
|
|
241
|
-
port1.postMessage({ close: true });
|
|
242
|
-
port1.close();
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
port1.addEventListener("message", ({ data }) => {
|
|
246
|
-
if ("m" in data) conn.onmessage?.(data.m);
|
|
247
|
-
else if ("open" in data) resolve(conn);
|
|
248
|
-
else if ("error" in data) {
|
|
249
|
-
closed = true;
|
|
250
|
-
port1.close();
|
|
251
|
-
reject(new Error(data.error));
|
|
252
|
-
} else if (!closed) {
|
|
253
|
-
closed = true;
|
|
254
|
-
port1.close();
|
|
255
|
-
conn.onclose?.(data.close ?? void 0);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
port1.start();
|
|
259
|
-
void gone?.then(() => {
|
|
260
|
-
if (closed) return;
|
|
261
|
-
closed = true;
|
|
262
|
-
port1.close();
|
|
263
|
-
reject(new Error("the CDP host is gone"));
|
|
264
|
-
conn.onclose?.("the CDP host is gone");
|
|
265
|
-
});
|
|
266
|
-
ask(request2, port2);
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// src/client/protocol.ts
|
|
271
|
-
var PROTOCOL = [1, 5];
|
|
272
|
-
var FS_METHODS = [
|
|
273
|
-
"readFile",
|
|
274
|
-
"writeFile",
|
|
275
|
-
"stat",
|
|
276
|
-
"lstat",
|
|
277
|
-
"readdir",
|
|
278
|
-
"mkdir",
|
|
279
|
-
"rm",
|
|
280
|
-
"rename",
|
|
281
|
-
"realpath",
|
|
282
|
-
"symlink",
|
|
283
|
-
"readlink",
|
|
284
|
-
"exists"
|
|
285
|
-
];
|
|
286
|
-
function locksOf(scope = globalThis) {
|
|
287
|
-
const locks = scope.navigator?.locks;
|
|
288
|
-
return typeof locks?.request === "function" ? locks : void 0;
|
|
289
|
-
}
|
|
290
|
-
function versionError(theirs) {
|
|
291
|
-
const major = Array.isArray(theirs) ? theirs[0] : void 0;
|
|
292
|
-
if (major === PROTOCOL[0]) return void 0;
|
|
293
|
-
const named = typeof major === "number" ? `${major}.x` : "without a version";
|
|
294
|
-
return `slicc-kernel client protocol ${named} is not supported: this side speaks ${PROTOCOL.join(".")}`;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
180
|
// src/fs/types.ts
|
|
298
181
|
function rangedOps(fs) {
|
|
299
182
|
const { pread, pwrite, truncate } = fs;
|
|
@@ -328,1672 +211,1878 @@ function inodeOf(path) {
|
|
|
328
211
|
return 4294967296 * (2097151 & h2) + (h1 >>> 0) || 1;
|
|
329
212
|
}
|
|
330
213
|
|
|
331
|
-
// src/
|
|
332
|
-
var
|
|
333
|
-
var
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
214
|
+
// src/fs/opfs.ts
|
|
215
|
+
var S_IFDIR = 16384;
|
|
216
|
+
var S_IFREG = 32768;
|
|
217
|
+
var S_IFLNK = 40960;
|
|
218
|
+
var PERM_MASK = 4095;
|
|
219
|
+
var READ_ATTEMPTS = 3;
|
|
220
|
+
var MAX_LINKS = 40;
|
|
221
|
+
var CHECK_MS = 1e3;
|
|
222
|
+
var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
|
|
223
|
+
var ERRNO_BY_DOM_ERROR = {
|
|
224
|
+
NotFoundError: "ENOENT",
|
|
225
|
+
TypeMismatchError: "ENOTDIR",
|
|
226
|
+
InvalidModificationError: "ENOTEMPTY",
|
|
227
|
+
NoModificationAllowedError: "EBUSY",
|
|
228
|
+
TypeError: "EINVAL",
|
|
229
|
+
NotAllowedError: "EACCES"
|
|
339
230
|
};
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
231
|
+
function translate(err, path) {
|
|
232
|
+
if (typeof err?.code === "string") return err;
|
|
233
|
+
const name2 = err?.name;
|
|
234
|
+
return fsError(typeof name2 === "string" && ERRNO_BY_DOM_ERROR[name2] || "EIO", path);
|
|
235
|
+
}
|
|
236
|
+
function missing(err) {
|
|
237
|
+
const code = err?.code;
|
|
238
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
239
|
+
}
|
|
240
|
+
function split(path) {
|
|
241
|
+
return path.split("/").filter(Boolean);
|
|
242
|
+
}
|
|
243
|
+
function parentOf2(path) {
|
|
244
|
+
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
245
|
+
}
|
|
246
|
+
function baseOf(path) {
|
|
247
|
+
return path.slice(path.lastIndexOf("/") + 1);
|
|
248
|
+
}
|
|
249
|
+
function prefixes(path) {
|
|
250
|
+
const parts = split(path);
|
|
251
|
+
return parts.map((_, i) => `/${parts.slice(0, i + 1).join("/")}`);
|
|
252
|
+
}
|
|
253
|
+
function within(path, root) {
|
|
254
|
+
return path === root || path.startsWith(`${root}/`);
|
|
255
|
+
}
|
|
256
|
+
var LOCKED = /* @__PURE__ */ new Set(["NotReadableError", "NoModificationAllowedError", "InvalidStateError"]);
|
|
257
|
+
var SYNC_BACKOFF_MS = [5, 10, 20, 40, 80];
|
|
258
|
+
async function openSync(handle) {
|
|
259
|
+
const sync = handle.createSyncAccessHandle;
|
|
260
|
+
if (!sync) return void 0;
|
|
261
|
+
for (let attempt = 0; ; attempt++) {
|
|
262
|
+
try {
|
|
263
|
+
return await sync.call(handle, { mode: "readwrite-unsafe" });
|
|
264
|
+
} catch (err) {
|
|
265
|
+
const delay = SYNC_BACKOFF_MS[attempt];
|
|
266
|
+
if (!LOCKED.has(err?.name)) throw err;
|
|
267
|
+
if (delay === void 0) return void 0;
|
|
268
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
269
|
+
}
|
|
361
270
|
}
|
|
362
|
-
|
|
363
|
-
|
|
271
|
+
}
|
|
272
|
+
function writeAll(access, at, bytes) {
|
|
273
|
+
for (let done4 = 0; done4 < bytes.length; ) {
|
|
274
|
+
const n = access.write(bytes.subarray(done4), { at: at + done4 });
|
|
275
|
+
if (!(n > 0))
|
|
276
|
+
throw Object.assign(new Error("the file system took no more bytes"), { code: "ENOSPC" });
|
|
277
|
+
done4 += n;
|
|
364
278
|
}
|
|
365
|
-
|
|
366
|
-
|
|
279
|
+
}
|
|
280
|
+
async function positioned(handle, op) {
|
|
281
|
+
const access = await openSync(handle);
|
|
282
|
+
if (access) {
|
|
283
|
+
try {
|
|
284
|
+
op({
|
|
285
|
+
write: (at, bytes) => writeAll(access, at, bytes),
|
|
286
|
+
truncate: (n) => access.truncate(n)
|
|
287
|
+
});
|
|
288
|
+
access.flush();
|
|
289
|
+
} finally {
|
|
290
|
+
access.close();
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
367
293
|
}
|
|
368
|
-
|
|
369
|
-
|
|
294
|
+
const writable = await handle.createWritable({ keepExistingData: true });
|
|
295
|
+
try {
|
|
296
|
+
await op({
|
|
297
|
+
write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes }),
|
|
298
|
+
truncate: (n) => writable.truncate(n)
|
|
299
|
+
});
|
|
300
|
+
} catch (err) {
|
|
301
|
+
await writable.abort();
|
|
302
|
+
throw err;
|
|
370
303
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
304
|
+
await writable.close();
|
|
305
|
+
}
|
|
306
|
+
function syncAccessHandles(scope = globalThis) {
|
|
307
|
+
const handle = scope.FileSystemFileHandle;
|
|
308
|
+
return handle !== void 0 && "createSyncAccessHandle" in handle.prototype;
|
|
309
|
+
}
|
|
310
|
+
function isHandle(child) {
|
|
311
|
+
return "kind" in child;
|
|
312
|
+
}
|
|
313
|
+
var OpfsFs = class {
|
|
314
|
+
root;
|
|
315
|
+
meta;
|
|
316
|
+
dirs = /* @__PURE__ */ new Map();
|
|
317
|
+
checked = /* @__PURE__ */ new WeakMap();
|
|
318
|
+
channel;
|
|
319
|
+
ranged;
|
|
320
|
+
constructor(root, meta = new MemoryMeta(), channel, options = {}) {
|
|
321
|
+
this.root = root;
|
|
322
|
+
this.meta = meta;
|
|
323
|
+
this.ranged = options.ranged === true;
|
|
324
|
+
this.channel = channel === void 0 ? void 0 : new BroadcastChannel(channel);
|
|
325
|
+
if (this.channel) {
|
|
326
|
+
this.channel.onmessage = ({ data }) => {
|
|
327
|
+
for (const path of data) this.forget(path);
|
|
328
|
+
};
|
|
329
|
+
this.channel.unref?.();
|
|
376
330
|
}
|
|
377
|
-
this.wake();
|
|
378
331
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
this.
|
|
332
|
+
changed(...paths) {
|
|
333
|
+
for (const path of paths) this.forget(path);
|
|
334
|
+
this.channel?.postMessage(paths);
|
|
382
335
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
336
|
+
resolvePath(base, path) {
|
|
337
|
+
return resolvePath(base, path);
|
|
338
|
+
}
|
|
339
|
+
async walk(parts, from, start) {
|
|
340
|
+
let dir = start;
|
|
341
|
+
for (let at = from; at < parts.length; at++) {
|
|
342
|
+
dir = await dir.getDirectoryHandle(parts[at]);
|
|
343
|
+
this.dirs.set(`/${parts.slice(0, at + 1).join("/")}`, dir);
|
|
344
|
+
this.checked.set(dir, performance.now());
|
|
387
345
|
}
|
|
388
|
-
|
|
389
|
-
this.wake();
|
|
390
|
-
return out;
|
|
346
|
+
return dir;
|
|
391
347
|
}
|
|
392
|
-
async
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
348
|
+
async dir(path) {
|
|
349
|
+
const parts = split(path);
|
|
350
|
+
let from = parts.length;
|
|
351
|
+
let cached;
|
|
352
|
+
for (; from > 0 && !cached; from--)
|
|
353
|
+
cached = this.dirs.get(`/${parts.slice(0, from).join("/")}`);
|
|
354
|
+
if (cached) from++;
|
|
355
|
+
const at = `/${parts.slice(0, from).join("/")}`;
|
|
356
|
+
if (cached && !await this.current(at, cached)) {
|
|
357
|
+
this.forget(at);
|
|
358
|
+
cached = void 0;
|
|
359
|
+
from = 0;
|
|
396
360
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const count = Math.min(chunk2.length, out.length - filled);
|
|
402
|
-
out.set(chunk2.subarray(0, count), filled);
|
|
403
|
-
filled += count;
|
|
361
|
+
try {
|
|
362
|
+
return await this.walk(parts, from, cached ?? this.root);
|
|
363
|
+
} catch (err) {
|
|
364
|
+
throw translate(err, path);
|
|
404
365
|
}
|
|
405
|
-
return out;
|
|
406
366
|
}
|
|
407
|
-
async
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
367
|
+
async current(path, dir) {
|
|
368
|
+
if (performance.now() - (this.checked.get(dir) ?? -CHECK_MS) < CHECK_MS) return true;
|
|
369
|
+
const where = await this.root.resolve(dir).catch(() => null);
|
|
370
|
+
if (where === null || `/${where.join("/")}` !== path) return false;
|
|
371
|
+
this.checked.set(dir, performance.now());
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
forget(path) {
|
|
375
|
+
for (const key of [...this.dirs.keys()]) if (within(key, path)) this.dirs.delete(key);
|
|
376
|
+
}
|
|
377
|
+
async inDir(path, op) {
|
|
378
|
+
const dir = await this.dir(path);
|
|
379
|
+
try {
|
|
380
|
+
return await op(dir);
|
|
381
|
+
} catch (err) {
|
|
382
|
+
if (err?.name !== "NotFoundError" || await this.current(path, dir))
|
|
383
|
+
throw err;
|
|
384
|
+
this.forget(path);
|
|
385
|
+
return op(await this.dir(path));
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async child(parent, name2, kind) {
|
|
389
|
+
return kind === "file" ? parent.getFileHandle(name2) : parent.getDirectoryHandle(name2);
|
|
390
|
+
}
|
|
391
|
+
async parent(path) {
|
|
392
|
+
const parts = split(path);
|
|
393
|
+
const name2 = parts.pop();
|
|
394
|
+
if (name2 === void 0) throw fsError("EBUSY", path);
|
|
395
|
+
return [`/${parts.join("/")}`, name2];
|
|
396
|
+
}
|
|
397
|
+
async handle(path) {
|
|
398
|
+
if (split(path).length === 0) return this.root;
|
|
399
|
+
const [dir, name2] = await this.parent(path);
|
|
400
|
+
const known = this.dirs.has(path);
|
|
401
|
+
return this.inDir(dir, async (parent) => {
|
|
402
|
+
const [first, second] = known ? ["directory", "file"] : ["file", "directory"];
|
|
403
|
+
try {
|
|
404
|
+
return await this.child(parent, name2, first);
|
|
405
|
+
} catch (err) {
|
|
406
|
+
if (err?.name !== "TypeMismatchError") throw err;
|
|
420
407
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
408
|
+
return this.child(parent, name2, second);
|
|
409
|
+
}).catch((err) => {
|
|
410
|
+
throw translate(err, path);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
real(path) {
|
|
414
|
+
return this.handle(path).then(
|
|
415
|
+
() => true,
|
|
416
|
+
() => false
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
async linkAlong(path) {
|
|
420
|
+
for (const entry of await this.meta.get(prefixes(path))) {
|
|
421
|
+
if (entry?.link === void 0) continue;
|
|
422
|
+
if (!await this.real(entry.path) && await this.real(parentOf2(entry.path))) return entry;
|
|
426
423
|
}
|
|
427
|
-
return
|
|
424
|
+
return void 0;
|
|
428
425
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
let
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
426
|
+
async locate(path, follow) {
|
|
427
|
+
let current = path;
|
|
428
|
+
for (let hops = 0; hops <= MAX_LINKS; hops++) {
|
|
429
|
+
try {
|
|
430
|
+
return { path: current, handle: await this.handle(current) };
|
|
431
|
+
} catch (err) {
|
|
432
|
+
if (!missing(err)) throw err;
|
|
433
|
+
const link2 = await this.linkAlong(current);
|
|
434
|
+
if (!link2) throw err;
|
|
435
|
+
if (link2.path === current && !follow) return { path: current, link: link2 };
|
|
436
|
+
const rest = current.slice(link2.path.length);
|
|
437
|
+
current = resolvePath(parentOf2(link2.path), `${link2.link}${rest}`);
|
|
438
|
+
}
|
|
439
439
|
}
|
|
440
|
-
|
|
441
|
-
return out;
|
|
440
|
+
throw fsError("ELOOP", path);
|
|
442
441
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return
|
|
446
|
-
const onAbort = () => {
|
|
447
|
-
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
448
|
-
reject(new PipeError("EINTR"));
|
|
449
|
-
};
|
|
450
|
-
const waiter = () => {
|
|
451
|
-
signal?.removeEventListener("abort", onAbort);
|
|
452
|
-
resolve();
|
|
453
|
-
};
|
|
454
|
-
this.waiters.push(waiter);
|
|
455
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
456
|
-
});
|
|
442
|
+
mode(path, directory, meta) {
|
|
443
|
+
const fallback = directory || EXECUTABLE.test(path) ? 493 : 420;
|
|
444
|
+
return (directory ? S_IFDIR : S_IFREG) | (meta?.mode ?? fallback);
|
|
457
445
|
}
|
|
458
|
-
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
446
|
+
async statOf(path, handle, meta) {
|
|
447
|
+
const directory = handle.kind === "directory";
|
|
448
|
+
const file = directory ? void 0 : await handle.getFile();
|
|
449
|
+
const modified = file?.lastModified ?? 0;
|
|
450
|
+
const mtime = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
|
|
451
|
+
return {
|
|
452
|
+
isFile: !directory,
|
|
453
|
+
isDirectory: directory,
|
|
454
|
+
isSymbolicLink: false,
|
|
455
|
+
size: file?.size ?? 0,
|
|
456
|
+
mode: this.mode(path, directory, meta),
|
|
457
|
+
mtime: new Date(mtime),
|
|
458
|
+
atime: new Date(meta?.atimeMs ?? mtime),
|
|
459
|
+
ctime: new Date(Math.max(meta?.ctimeMs ?? mtime, modified)),
|
|
460
|
+
ino: meta?.ino ?? inodeOf(path),
|
|
461
|
+
...this.ranged && !directory ? { ranged: true } : {}
|
|
462
|
+
};
|
|
462
463
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
464
|
+
linkStat(entry) {
|
|
465
|
+
const time2 = new Date(entry.mtimeMs ?? 0);
|
|
466
|
+
return {
|
|
467
|
+
isFile: false,
|
|
468
|
+
isDirectory: false,
|
|
469
|
+
isSymbolicLink: true,
|
|
470
|
+
size: new TextEncoder().encode(entry.link).length,
|
|
471
|
+
mode: S_IFLNK | 511,
|
|
472
|
+
mtime: time2,
|
|
473
|
+
atime: time2,
|
|
474
|
+
ctime: new Date(entry.ctimeMs ?? 0),
|
|
475
|
+
ino: entry.ino ?? inodeOf(entry.path)
|
|
476
|
+
};
|
|
471
477
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
var OpenFile = class {
|
|
477
|
-
refs = 1;
|
|
478
|
-
file;
|
|
479
|
-
constructor(file) {
|
|
480
|
-
this.file = file;
|
|
478
|
+
async describe(found) {
|
|
479
|
+
if (found.link) return this.linkStat(found.link);
|
|
480
|
+
const [meta] = await this.meta.get([found.path]);
|
|
481
|
+
return this.statOf(found.path, found.handle, meta);
|
|
481
482
|
}
|
|
482
|
-
|
|
483
|
-
this.
|
|
484
|
-
return this;
|
|
483
|
+
async stat(path) {
|
|
484
|
+
return this.describe(await this.locate(path, true));
|
|
485
485
|
}
|
|
486
|
-
|
|
487
|
-
this.
|
|
488
|
-
if (this.refs === 0) return this.file.close();
|
|
486
|
+
async lstat(path) {
|
|
487
|
+
return this.describe(await this.locate(path, false));
|
|
489
488
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
return {
|
|
496
|
-
read: new OpenFile({
|
|
497
|
-
read: async (max, signal) => {
|
|
498
|
-
try {
|
|
499
|
-
return await pipe.read(max, signal);
|
|
500
|
-
} catch (e) {
|
|
501
|
-
throw new KernelError(e.code);
|
|
502
|
-
}
|
|
503
|
-
},
|
|
504
|
-
close: () => pipe.closeRead(),
|
|
505
|
-
poll: () => ({ readable: pipe.readReady, writable: false, hangup: pipe.writersGone }),
|
|
506
|
-
changed: (signal) => pipe.changed(signal)
|
|
507
|
-
}),
|
|
508
|
-
write: new OpenFile({
|
|
509
|
-
write: async (bytes, signal) => {
|
|
510
|
-
try {
|
|
511
|
-
return await pipe.write(bytes, signal);
|
|
512
|
-
} catch (e) {
|
|
513
|
-
throw new KernelError(e.code);
|
|
514
|
-
}
|
|
515
|
-
},
|
|
516
|
-
close: () => pipe.closeWrite(),
|
|
517
|
-
poll: () => ({ readable: false, writable: pipe.writeReady, hangup: pipe.readersGone }),
|
|
518
|
-
changed: (signal) => pipe.changed(signal)
|
|
519
|
-
})
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
function bytesSource(data) {
|
|
523
|
-
let offset = 0;
|
|
524
|
-
return new OpenFile({
|
|
525
|
-
read: async (max) => {
|
|
526
|
-
const out = data.subarray(offset, offset + max);
|
|
527
|
-
offset += out.length;
|
|
528
|
-
return out;
|
|
529
|
-
},
|
|
530
|
-
close: () => {
|
|
531
|
-
}
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
function sinkFile(onData) {
|
|
535
|
-
return new OpenFile({
|
|
536
|
-
write: async (bytes) => {
|
|
537
|
-
onData(bytes.slice());
|
|
538
|
-
return bytes.length;
|
|
539
|
-
},
|
|
540
|
-
close: () => {
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
function nullFile() {
|
|
545
|
-
return new OpenFile({
|
|
546
|
-
read: async () => new Uint8Array(0),
|
|
547
|
-
write: async (bytes) => bytes.length,
|
|
548
|
-
close: () => {
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
function deviceFile(device, access) {
|
|
553
|
-
const read = async (max) => {
|
|
554
|
-
if (device === "null") return new Uint8Array(0);
|
|
555
|
-
const out = new Uint8Array(max);
|
|
556
|
-
if (device === "urandom") {
|
|
557
|
-
for (let at = 0; at < max; at += 65536) {
|
|
558
|
-
crypto.getRandomValues(out.subarray(at, Math.min(max, at + 65536)));
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
return out;
|
|
562
|
-
};
|
|
563
|
-
return new OpenFile({
|
|
564
|
-
...access !== "write" ? { read } : {},
|
|
565
|
-
...access !== "read" ? { write: async (bytes) => bytes.length } : {},
|
|
566
|
-
seek: async () => 0,
|
|
567
|
-
heldMeta: { device, ...access ? { access } : {} },
|
|
568
|
-
close: () => {
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
function heldFile(meta) {
|
|
573
|
-
return new OpenFile({ held: true, ...meta ? { heldMeta: meta } : {}, close: () => {
|
|
574
|
-
} });
|
|
575
|
-
}
|
|
576
|
-
function kernelFdKind(file) {
|
|
577
|
-
if (file.held) return "held";
|
|
578
|
-
if (file.heldMeta && "device" in file.heldMeta) return "device";
|
|
579
|
-
if (file.tty) return "tty";
|
|
580
|
-
return file.seek ? "file" : "stream";
|
|
581
|
-
}
|
|
582
|
-
var FdTable = class _FdTable {
|
|
583
|
-
static MAX_FDS = 1024;
|
|
584
|
-
fds = /* @__PURE__ */ new Map();
|
|
585
|
-
cloexec = /* @__PURE__ */ new Set();
|
|
586
|
-
status = /* @__PURE__ */ new Map();
|
|
587
|
-
get(fd) {
|
|
588
|
-
const file = this.fds.get(fd);
|
|
589
|
-
if (!file) throw new KernelError("EBADF");
|
|
590
|
-
return file;
|
|
489
|
+
async exists(path) {
|
|
490
|
+
return this.locate(path, true).then(
|
|
491
|
+
() => true,
|
|
492
|
+
() => false
|
|
493
|
+
);
|
|
591
494
|
}
|
|
592
|
-
|
|
593
|
-
for (
|
|
594
|
-
|
|
595
|
-
|
|
495
|
+
async retrying(path, op, locks) {
|
|
496
|
+
for (let attempt = 1; ; attempt++) {
|
|
497
|
+
try {
|
|
498
|
+
return await op();
|
|
499
|
+
} catch (err) {
|
|
500
|
+
const name2 = err?.name;
|
|
501
|
+
const again = name2 === "NotReadableError" || locks && LOCKED.has(name2);
|
|
502
|
+
if (!again || attempt >= READ_ATTEMPTS) throw translate(err, path);
|
|
503
|
+
await new Promise((resolve) => setTimeout(resolve, attempt * 10));
|
|
504
|
+
}
|
|
596
505
|
}
|
|
597
|
-
return void 0;
|
|
598
|
-
}
|
|
599
|
-
numbers() {
|
|
600
|
-
return [...this.fds.keys()].sort((a, b) => a - b);
|
|
601
506
|
}
|
|
602
|
-
|
|
603
|
-
|
|
507
|
+
async existing(path) {
|
|
508
|
+
const handle = (await this.locate(path, true)).handle;
|
|
509
|
+
if (handle.kind === "directory") throw fsError("EISDIR", path);
|
|
510
|
+
return handle;
|
|
604
511
|
}
|
|
605
|
-
|
|
606
|
-
this.
|
|
607
|
-
this.
|
|
512
|
+
async readFileBuffer(path) {
|
|
513
|
+
const handle = await this.existing(path);
|
|
514
|
+
return this.retrying(
|
|
515
|
+
path,
|
|
516
|
+
async () => new Uint8Array(await (await handle.getFile()).arrayBuffer()),
|
|
517
|
+
false
|
|
518
|
+
);
|
|
608
519
|
}
|
|
609
|
-
|
|
610
|
-
this.
|
|
611
|
-
this.
|
|
520
|
+
async pread(path, offset, length2) {
|
|
521
|
+
const handle = await this.existing(path);
|
|
522
|
+
return this.retrying(
|
|
523
|
+
path,
|
|
524
|
+
async () => new Uint8Array(await (await handle.getFile()).slice(offset, offset + length2).arrayBuffer()),
|
|
525
|
+
false
|
|
526
|
+
);
|
|
612
527
|
}
|
|
613
|
-
|
|
614
|
-
|
|
528
|
+
async pwrite(path, offset, bytes) {
|
|
529
|
+
const handle = (await this.fileFor(path))[0];
|
|
530
|
+
await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
|
|
615
531
|
}
|
|
616
|
-
|
|
617
|
-
this.
|
|
618
|
-
this.
|
|
532
|
+
async truncate(path, size) {
|
|
533
|
+
const handle = await this.existing(path);
|
|
534
|
+
await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
|
|
619
535
|
}
|
|
620
|
-
|
|
621
|
-
return this.
|
|
536
|
+
async readFile(path) {
|
|
537
|
+
return new TextDecoder().decode(await this.readFileBuffer(path));
|
|
622
538
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
539
|
+
async writeTarget(path) {
|
|
540
|
+
try {
|
|
541
|
+
return [(await this.locate(path, true)).path, true];
|
|
542
|
+
} catch (err) {
|
|
543
|
+
if (!missing(err)) throw err;
|
|
629
544
|
}
|
|
630
|
-
|
|
631
|
-
|
|
545
|
+
const [entry] = await this.meta.get([path]);
|
|
546
|
+
const wanted = entry?.link === void 0 ? path : resolvePath(parentOf2(path), entry.link);
|
|
547
|
+
return [resolvePath((await this.locate(parentOf2(wanted), true)).path, baseOf(wanted)), false];
|
|
632
548
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
549
|
+
async fileFor(path) {
|
|
550
|
+
const [target, existed] = await this.writeTarget(path);
|
|
551
|
+
const [dir, name2] = await this.parent(target);
|
|
552
|
+
try {
|
|
553
|
+
return [
|
|
554
|
+
await this.inDir(dir, (parent) => parent.getFileHandle(name2, { create: true })),
|
|
555
|
+
existed
|
|
556
|
+
];
|
|
557
|
+
} catch (err) {
|
|
558
|
+
const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
|
|
559
|
+
throw code ? fsError(code, path) : translate(err, path);
|
|
637
560
|
}
|
|
638
|
-
const previous = this.fds.get(fd);
|
|
639
|
-
this.fds.set(fd, file);
|
|
640
|
-
this.cloexec.delete(fd);
|
|
641
|
-
this.status.delete(fd);
|
|
642
|
-
void Promise.resolve(previous?.release()).catch(() => void 0);
|
|
643
561
|
}
|
|
644
|
-
|
|
645
|
-
|
|
562
|
+
async writeFile(path, content) {
|
|
563
|
+
const [file, existed] = await this.fileFor(path);
|
|
564
|
+
if (!existed && content.length === 0) return;
|
|
565
|
+
const writable = await file.createWritable();
|
|
566
|
+
await writable.write(content);
|
|
567
|
+
await writable.close();
|
|
646
568
|
}
|
|
647
|
-
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
569
|
+
async listing(path) {
|
|
570
|
+
const found = await this.locate(path, true);
|
|
571
|
+
const handle = found.handle;
|
|
572
|
+
if (handle.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
573
|
+
const children2 = /* @__PURE__ */ new Map();
|
|
574
|
+
for await (const child of handle.values()) children2.set(child.name, child);
|
|
575
|
+
for (const link2 of await this.meta.links(found.path)) {
|
|
576
|
+
const name2 = baseOf(link2.path);
|
|
577
|
+
if (!children2.has(name2)) children2.set(name2, link2);
|
|
578
|
+
}
|
|
579
|
+
return [found.path, [...children2].sort(([a], [b]) => a < b ? -1 : 1)];
|
|
651
580
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
this.fds.delete(fd);
|
|
655
|
-
this.cloexec.delete(fd);
|
|
656
|
-
this.status.delete(fd);
|
|
657
|
-
return file.release();
|
|
581
|
+
async readdir(path) {
|
|
582
|
+
return (await this.listing(path))[1].map(([name2]) => name2);
|
|
658
583
|
}
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
584
|
+
async readdirStat(path) {
|
|
585
|
+
const [dir, children2] = await this.listing(path);
|
|
586
|
+
const base = dir === "/" ? "" : dir;
|
|
587
|
+
const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
|
|
588
|
+
return Promise.all(
|
|
589
|
+
children2.map(async ([name2, child], i) => {
|
|
590
|
+
if (!isHandle(child)) return [name2, this.linkStat(child)];
|
|
591
|
+
const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
|
|
592
|
+
return [name2, stat];
|
|
593
|
+
})
|
|
594
|
+
);
|
|
665
595
|
}
|
|
666
|
-
async
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
596
|
+
async mkdir(path, options = {}) {
|
|
597
|
+
let found;
|
|
598
|
+
try {
|
|
599
|
+
found = await this.locate(path, true);
|
|
600
|
+
} catch (err) {
|
|
601
|
+
if (!missing(err)) throw err;
|
|
602
|
+
}
|
|
603
|
+
if (found) {
|
|
604
|
+
const directory = found.handle?.kind === "directory";
|
|
605
|
+
if (options.recursive && directory) return;
|
|
606
|
+
throw fsError(options.recursive ? "ENOTDIR" : "EEXIST", path);
|
|
607
|
+
}
|
|
608
|
+
const [entry] = await this.meta.get([path]);
|
|
609
|
+
if (entry?.link !== void 0) throw fsError("EEXIST", path);
|
|
610
|
+
if (options.recursive) await this.mkdir(parentOf2(path), options);
|
|
611
|
+
const parent = await this.locate(parentOf2(path), true);
|
|
612
|
+
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
613
|
+
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
672
614
|
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
async function pipeCall(op) {
|
|
696
|
-
try {
|
|
697
|
-
return await op();
|
|
698
|
-
} catch (e) {
|
|
699
|
-
if (e instanceof PipeError) throw new KernelError(e.code);
|
|
700
|
-
throw e;
|
|
615
|
+
async rm(path, options = {}) {
|
|
616
|
+
let found;
|
|
617
|
+
try {
|
|
618
|
+
found = await this.locate(path, false);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
const error = translate(err, path);
|
|
621
|
+
if (options.force && error.code === "ENOENT") return;
|
|
622
|
+
throw error;
|
|
623
|
+
}
|
|
624
|
+
if (found.handle) {
|
|
625
|
+
const [dir, name2] = await this.parent(found.path);
|
|
626
|
+
try {
|
|
627
|
+
await this.inDir(
|
|
628
|
+
dir,
|
|
629
|
+
(parent) => parent.removeEntry(name2, { recursive: options.recursive === true })
|
|
630
|
+
);
|
|
631
|
+
} catch (err) {
|
|
632
|
+
throw translate(err, path);
|
|
633
|
+
}
|
|
634
|
+
this.changed(found.path);
|
|
635
|
+
}
|
|
636
|
+
await this.meta.remove([found.path]);
|
|
701
637
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
signal?.removeEventListener("abort", onAbort);
|
|
714
|
-
resolve();
|
|
715
|
-
};
|
|
716
|
-
this.waiters.push(waiter);
|
|
717
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
638
|
+
async rename(from, to) {
|
|
639
|
+
if (from === to) return;
|
|
640
|
+
const source = await this.locate(from, false);
|
|
641
|
+
this.forget(source.path);
|
|
642
|
+
const parent = await this.locate(parentOf2(to), true);
|
|
643
|
+
const destination = resolvePath(parent.path, baseOf(to));
|
|
644
|
+
const sourceStat = await this.describe(source);
|
|
645
|
+
if (sourceStat.isDirectory && within(destination, source.path)) throw fsError("EINVAL", to);
|
|
646
|
+
const target = await this.locate(destination, false).catch((err) => {
|
|
647
|
+
if (missing(err)) return null;
|
|
648
|
+
throw err;
|
|
718
649
|
});
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
for (const resolve of waiters) resolve();
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
function link(a, b) {
|
|
727
|
-
const ab = new KernelPipe();
|
|
728
|
-
const ba = new KernelPipe();
|
|
729
|
-
for (const pipe of [ab, ba]) {
|
|
730
|
-
pipe.openRead();
|
|
731
|
-
pipe.openWrite();
|
|
732
|
-
}
|
|
733
|
-
a.attach({ rx: ba, tx: ab });
|
|
734
|
-
b.attach({ rx: ab, tx: ba });
|
|
735
|
-
}
|
|
736
|
-
var KernelSocket = class _KernelSocket {
|
|
737
|
-
net;
|
|
738
|
-
domain;
|
|
739
|
-
state = "open";
|
|
740
|
-
local;
|
|
741
|
-
peer;
|
|
742
|
-
link;
|
|
743
|
-
readShut = false;
|
|
744
|
-
writeShut = false;
|
|
745
|
-
queue = [];
|
|
746
|
-
backlog = 0;
|
|
747
|
-
bound = false;
|
|
748
|
-
changes = new Changes();
|
|
749
|
-
options = /* @__PURE__ */ new Map();
|
|
750
|
-
constructor(net, domain) {
|
|
751
|
-
this.net = net;
|
|
752
|
-
this.domain = domain;
|
|
753
|
-
}
|
|
754
|
-
attach(link2) {
|
|
755
|
-
this.link = link2;
|
|
756
|
-
this.state = "connected";
|
|
757
|
-
this.changes.wake();
|
|
758
|
-
}
|
|
759
|
-
get listening() {
|
|
760
|
-
return this.state === "listening";
|
|
761
|
-
}
|
|
762
|
-
connection() {
|
|
763
|
-
if (!this.link || this.state !== "connected") throw new KernelError("ENOTCONN");
|
|
764
|
-
return this.link;
|
|
765
|
-
}
|
|
766
|
-
async read(max, signal) {
|
|
767
|
-
const { rx } = this.connection();
|
|
768
|
-
if (this.readShut || max <= 0) return new Uint8Array(0);
|
|
769
|
-
return pipeCall(() => rx.read(max, signal));
|
|
770
|
-
}
|
|
771
|
-
async peek(max, signal) {
|
|
772
|
-
const { rx } = this.connection();
|
|
773
|
-
if (this.readShut || max <= 0) return new Uint8Array(0);
|
|
774
|
-
return pipeCall(() => rx.peek(max, signal));
|
|
775
|
-
}
|
|
776
|
-
async write(bytes, signal) {
|
|
777
|
-
const { tx } = this.connection();
|
|
778
|
-
if (this.writeShut) throw new KernelError("EPIPE");
|
|
779
|
-
return pipeCall(() => tx.write(bytes, signal));
|
|
780
|
-
}
|
|
781
|
-
poll() {
|
|
782
|
-
if (this.state === "listening") {
|
|
783
|
-
return { readable: this.queue.length > 0, writable: false, hangup: false };
|
|
650
|
+
const targetStat = target && await this.describe(target);
|
|
651
|
+
if (targetStat?.isDirectory && !sourceStat.isDirectory) throw fsError("EISDIR", to);
|
|
652
|
+
if (targetStat && !targetStat.isDirectory && sourceStat.isDirectory) {
|
|
653
|
+
throw fsError("ENOTDIR", to);
|
|
784
654
|
}
|
|
785
|
-
if (
|
|
786
|
-
|
|
655
|
+
if (targetStat?.isDirectory && (await this.readdir(destination)).length > 0) {
|
|
656
|
+
throw fsError("ENOTEMPTY", to);
|
|
787
657
|
}
|
|
788
|
-
const
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
for (const wait2 of waits) wait2.catch(() => void 0);
|
|
800
|
-
return pipeCall(() => Promise.race(waits));
|
|
801
|
-
}
|
|
802
|
-
bind(addr) {
|
|
803
|
-
if (this.state !== "open" || this.local) throw new KernelError("EINVAL");
|
|
804
|
-
if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
|
|
805
|
-
this.local = this.net.bind(this, addr);
|
|
806
|
-
this.bound = true;
|
|
807
|
-
}
|
|
808
|
-
listen(backlog) {
|
|
809
|
-
if (this.state === "connected") throw new KernelError("EISCONN");
|
|
810
|
-
if (this.state !== "open" && this.state !== "listening") throw new KernelError("EINVAL");
|
|
811
|
-
if (!this.local) {
|
|
812
|
-
if (this.domain === "unix") throw new KernelError("EINVAL");
|
|
813
|
-
this.bind({ family: "inet", host: "0.0.0.0", port: 0 });
|
|
658
|
+
const now = Date.now();
|
|
659
|
+
const carry = (entry) => ({
|
|
660
|
+
ino: inodeOf(source.path),
|
|
661
|
+
...entry,
|
|
662
|
+
path: destination,
|
|
663
|
+
ctimeMs: now
|
|
664
|
+
});
|
|
665
|
+
if (source.link) {
|
|
666
|
+
if (target?.handle) await this.rm(destination);
|
|
667
|
+
await this.meta.move(source.path, destination, carry);
|
|
668
|
+
return;
|
|
814
669
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
670
|
+
const dir = parent.handle;
|
|
671
|
+
const name2 = baseOf(destination);
|
|
672
|
+
const swap = targetStat?.isDirectory === true;
|
|
673
|
+
const staging = swap ? `.${name2}.${crypto.randomUUID()}` : name2;
|
|
674
|
+
this.forget(destination);
|
|
675
|
+
const timed = await this.explicitTimes(source.path);
|
|
676
|
+
const moved2 = await this.place(source.handle, dir, staging);
|
|
677
|
+
if (swap) await this.rm(destination, { recursive: true });
|
|
678
|
+
await this.meta.move(source.path, destination, carry);
|
|
679
|
+
if (!moved2) await this.rm(source.path, { recursive: true });
|
|
680
|
+
let copied = !moved2;
|
|
681
|
+
if (swap) {
|
|
682
|
+
const staged = await dir.getDirectoryHandle(staging);
|
|
683
|
+
const placed = await this.place(staged, dir, name2);
|
|
684
|
+
if (!placed) await dir.removeEntry(staging, { recursive: true });
|
|
685
|
+
copied ||= !placed;
|
|
825
686
|
}
|
|
687
|
+
if (copied) await this.restamp(destination, timed);
|
|
688
|
+
this.changed(source.path, destination);
|
|
826
689
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
this.changes.wake();
|
|
831
|
-
return true;
|
|
690
|
+
async modified(path) {
|
|
691
|
+
const handle = await this.handle(path);
|
|
692
|
+
return handle.kind === "file" ? (await handle.getFile()).lastModified : 0;
|
|
832
693
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
if (addr.family === "inet") {
|
|
840
|
-
this.local = this.net.bind(this, { family: "inet", host: "127.0.0.1", port: 0 });
|
|
841
|
-
this.bound = true;
|
|
842
|
-
} else this.local = { family: "unix", path: "" };
|
|
843
|
-
}
|
|
844
|
-
const server = new _KernelSocket(this.net, this.domain);
|
|
845
|
-
server.local = addr.family === "inet" ? { ...addr, host: canonicalHost(addr.host) } : addr;
|
|
846
|
-
server.peer = this.local;
|
|
847
|
-
this.peer = server.local;
|
|
848
|
-
link(this, server);
|
|
849
|
-
if (!target.offer(server)) {
|
|
850
|
-
this.unlink();
|
|
851
|
-
server.close();
|
|
852
|
-
throw new KernelError("ECONNREFUSED");
|
|
694
|
+
async explicitTimes(path) {
|
|
695
|
+
const timed = [];
|
|
696
|
+
for (const entry of await this.meta.under(path)) {
|
|
697
|
+
if (entry.mtimeFor === void 0 || entry.link !== void 0) continue;
|
|
698
|
+
const current = await this.modified(entry.path).catch(() => void 0);
|
|
699
|
+
if (current === entry.mtimeFor) timed.push(entry.path.slice(path.length));
|
|
853
700
|
}
|
|
701
|
+
return timed;
|
|
854
702
|
}
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
link2.tx.closeWrite();
|
|
703
|
+
async restamp(path, timed) {
|
|
704
|
+
for (const rest of timed) {
|
|
705
|
+
const mtimeFor = await this.modified(path + rest);
|
|
706
|
+
await this.meta.update(path + rest, (entry) => entry && { ...entry, mtimeFor });
|
|
860
707
|
}
|
|
861
|
-
this.link = void 0;
|
|
862
|
-
this.peer = void 0;
|
|
863
|
-
this.state = "open";
|
|
864
708
|
}
|
|
865
|
-
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
709
|
+
async place(handle, dir, name2) {
|
|
710
|
+
const moved2 = await handle.move?.(dir, name2).then(
|
|
711
|
+
() => true,
|
|
712
|
+
() => false
|
|
713
|
+
);
|
|
714
|
+
if (moved2) return true;
|
|
715
|
+
try {
|
|
716
|
+
await this.copy(handle, dir, name2);
|
|
717
|
+
} catch (err) {
|
|
718
|
+
if (handle.kind === "directory") await dir.removeEntry(name2, { recursive: true });
|
|
719
|
+
throw translate(err, name2);
|
|
875
720
|
}
|
|
876
|
-
|
|
721
|
+
return false;
|
|
877
722
|
}
|
|
878
|
-
|
|
879
|
-
if (
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
return 0;
|
|
885
|
-
case SO_ACCEPTCONN:
|
|
886
|
-
return this.state === "listening" ? 1 : 0;
|
|
887
|
-
case SO_DOMAIN:
|
|
888
|
-
return this.domain === "inet" ? AF_INET : AF_UNIX;
|
|
889
|
-
case SO_SNDBUF:
|
|
890
|
-
case SO_RCVBUF:
|
|
891
|
-
return this.options.get(`${level}:${name2}`) ?? PIPE_CAPACITY;
|
|
892
|
-
}
|
|
723
|
+
async copy(handle, dir, name2) {
|
|
724
|
+
if (handle.kind === "file") {
|
|
725
|
+
const writable = await (await dir.getFileHandle(name2, { create: true })).createWritable();
|
|
726
|
+
await writable.write(await handle.getFile());
|
|
727
|
+
await writable.close();
|
|
728
|
+
return;
|
|
893
729
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
setOption(level, name2, value) {
|
|
897
|
-
this.options.set(`${level}:${name2}`, value);
|
|
730
|
+
const target = await dir.getDirectoryHandle(name2, { create: true });
|
|
731
|
+
for await (const child of handle.values()) await this.copy(child, target, child.name);
|
|
898
732
|
}
|
|
899
|
-
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
this.readShut = true;
|
|
909
|
-
this.writeShut = true;
|
|
733
|
+
async symlink(target, path) {
|
|
734
|
+
const parent = await this.locate(parentOf2(path), true);
|
|
735
|
+
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
736
|
+
const at = resolvePath(parent.path, baseOf(path));
|
|
737
|
+
if (await this.locate(at, false).then(
|
|
738
|
+
() => true,
|
|
739
|
+
(err) => !missing(err)
|
|
740
|
+
)) {
|
|
741
|
+
throw fsError("EEXIST", path);
|
|
910
742
|
}
|
|
911
|
-
|
|
912
|
-
this.
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
link(a, b);
|
|
921
|
-
return [a, b];
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
function canonicalHost(host) {
|
|
925
|
-
return host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
926
|
-
}
|
|
927
|
-
function keyOf(addr) {
|
|
928
|
-
return addr.family === "inet" ? `inet:${addr.port}` : `unix:${addr.path}`;
|
|
929
|
-
}
|
|
930
|
-
var LoopbackNet = class {
|
|
931
|
-
bound = /* @__PURE__ */ new Map();
|
|
932
|
-
activators = /* @__PURE__ */ new Map();
|
|
933
|
-
internal = /* @__PURE__ */ new Set();
|
|
934
|
-
nextEphemeral = EPHEMERAL_FIRST;
|
|
935
|
-
kernelOnly(port, reserve = false) {
|
|
936
|
-
if (reserve) this.internal.add(port);
|
|
937
|
-
return this.internal.has(port);
|
|
938
|
-
}
|
|
939
|
-
activate(addr, start) {
|
|
940
|
-
this.activators.set(keyOf(addr), start);
|
|
941
|
-
}
|
|
942
|
-
socket(domain) {
|
|
943
|
-
return new KernelSocket(this, domain);
|
|
944
|
-
}
|
|
945
|
-
listen(addr, backlog = 128) {
|
|
946
|
-
const socket = this.socket(addr.family);
|
|
947
|
-
socket.bind(addr);
|
|
948
|
-
socket.listen(backlog);
|
|
949
|
-
return socket;
|
|
950
|
-
}
|
|
951
|
-
connect(addr) {
|
|
952
|
-
const socket = this.socket(addr.family);
|
|
953
|
-
socket.connect(addr);
|
|
954
|
-
return socket;
|
|
743
|
+
const now = Date.now();
|
|
744
|
+
await this.meta.update(at, () => ({
|
|
745
|
+
path: at,
|
|
746
|
+
link: target,
|
|
747
|
+
dir: parent.path,
|
|
748
|
+
ino: inodeOf(at),
|
|
749
|
+
mtimeMs: now,
|
|
750
|
+
ctimeMs: now
|
|
751
|
+
}));
|
|
955
752
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
throw new KernelError("EADDRNOTAVAIL");
|
|
961
|
-
}
|
|
962
|
-
const named = addr.family === "inet" && addr.port === 0 ? { ...addr, port: this.ephemeral() } : addr;
|
|
963
|
-
const key = keyOf(named);
|
|
964
|
-
if (this.bound.has(key)) throw new KernelError("EADDRINUSE");
|
|
965
|
-
this.bound.set(key, socket);
|
|
966
|
-
return named;
|
|
753
|
+
async readlink(path) {
|
|
754
|
+
const found = await this.locate(path, false);
|
|
755
|
+
if (found.link) return found.link.link;
|
|
756
|
+
throw fsError("EINVAL", path);
|
|
967
757
|
}
|
|
968
|
-
|
|
969
|
-
const
|
|
970
|
-
|
|
758
|
+
async chmod(path, mode) {
|
|
759
|
+
const found = await this.locate(path, true);
|
|
760
|
+
const ctimeMs = Date.now();
|
|
761
|
+
await this.meta.update(found.path, (entry) => ({
|
|
762
|
+
...entry,
|
|
763
|
+
path: found.path,
|
|
764
|
+
mode: mode & PERM_MASK,
|
|
765
|
+
ctimeMs
|
|
766
|
+
}));
|
|
971
767
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
768
|
+
async utimes(path, atime, mtime) {
|
|
769
|
+
const found = await this.locate(path, true);
|
|
770
|
+
const handle = found.handle;
|
|
771
|
+
const file = handle.kind === "file" ? await handle.getFile() : void 0;
|
|
772
|
+
const ctimeMs = Date.now();
|
|
773
|
+
await this.meta.update(found.path, (entry) => ({
|
|
774
|
+
...entry,
|
|
775
|
+
path: found.path,
|
|
776
|
+
atimeMs: atime.getTime(),
|
|
777
|
+
mtimeMs: mtime.getTime(),
|
|
778
|
+
mtimeFor: file?.lastModified ?? 0,
|
|
779
|
+
ctimeMs
|
|
780
|
+
}));
|
|
981
781
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
for (
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
if (!
|
|
782
|
+
async reconcile() {
|
|
783
|
+
let removed = 0;
|
|
784
|
+
for (const entry of await this.meta.all()) {
|
|
785
|
+
const real = await this.real(entry.path);
|
|
786
|
+
const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf2(entry.path));
|
|
787
|
+
if (!stale) continue;
|
|
788
|
+
await this.meta.update(entry.path, () => void 0);
|
|
789
|
+
removed++;
|
|
988
790
|
}
|
|
989
|
-
|
|
791
|
+
return removed;
|
|
990
792
|
}
|
|
991
793
|
};
|
|
992
794
|
|
|
993
|
-
// src/
|
|
994
|
-
var
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
throw Object.assign(
|
|
1001
|
-
new Error(`ECONNREFUSED: port ${port} only takes connections from inside the kernel`),
|
|
1002
|
-
{
|
|
1003
|
-
code: "ECONNREFUSED"
|
|
1004
|
-
}
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
return net.connect({ family: "inet", host: target, port });
|
|
1008
|
-
}
|
|
1009
|
-
var codeOf = (err) => {
|
|
1010
|
-
const code = err?.code;
|
|
1011
|
-
return typeof code === "string" ? code : "EIO";
|
|
1012
|
-
};
|
|
1013
|
-
function serveSocket(socket, port, onClose) {
|
|
1014
|
-
let closed = false;
|
|
1015
|
-
let writes = Promise.resolve();
|
|
1016
|
-
let credit = 0;
|
|
1017
|
-
let granted2;
|
|
1018
|
-
const send = (message2, transfer) => {
|
|
1019
|
-
if (!closed) port.postMessage(message2, transfer);
|
|
1020
|
-
};
|
|
1021
|
-
const close = (reset = false) => {
|
|
1022
|
-
if (closed) return;
|
|
1023
|
-
if (reset) send({ error: "ECONNRESET" });
|
|
1024
|
-
closed = true;
|
|
1025
|
-
granted2?.();
|
|
1026
|
-
socket.close();
|
|
1027
|
-
port.close();
|
|
1028
|
-
onClose?.();
|
|
1029
|
-
};
|
|
1030
|
-
const write = (data) => {
|
|
1031
|
-
writes = writes.then(async () => {
|
|
1032
|
-
if (closed) return;
|
|
1033
|
-
try {
|
|
1034
|
-
await socket.write(data);
|
|
1035
|
-
send({ ack: true });
|
|
1036
|
-
} catch (err) {
|
|
1037
|
-
send({ error: codeOf(err) });
|
|
1038
|
-
}
|
|
1039
|
-
});
|
|
795
|
+
// src/mount/media.ts
|
|
796
|
+
var MEDIA_STORE = "handles";
|
|
797
|
+
function memoryMedia() {
|
|
798
|
+
const handles = /* @__PURE__ */ new Map();
|
|
799
|
+
return {
|
|
800
|
+
get: async (id) => handles.get(id),
|
|
801
|
+
put: async (id, handle) => void handles.set(id, handle)
|
|
1040
802
|
};
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
803
|
+
}
|
|
804
|
+
function settle2(request2, value) {
|
|
805
|
+
return new Promise((resolve, reject) => {
|
|
806
|
+
request2.onsuccess = () => resolve(value());
|
|
807
|
+
request2.onerror = () => reject(request2.error);
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
function openMedia(name2, factory) {
|
|
811
|
+
const request2 = factory.open(name2, 1);
|
|
812
|
+
request2.onupgradeneeded = () => request2.result.createObjectStore(MEDIA_STORE);
|
|
813
|
+
return settle2(request2, () => {
|
|
814
|
+
const db = request2.result;
|
|
815
|
+
const store = (mode) => db.transaction(MEDIA_STORE, mode).objectStore(MEDIA_STORE);
|
|
816
|
+
return {
|
|
817
|
+
get: (id) => {
|
|
818
|
+
const got = store("readonly").get(id);
|
|
819
|
+
return settle2(got, () => got.result);
|
|
820
|
+
},
|
|
821
|
+
put: (id, handle) => settle2(store("readwrite").put(handle, id), () => void 0)
|
|
822
|
+
};
|
|
1051
823
|
});
|
|
1052
|
-
port.start?.();
|
|
1053
|
-
const turn = () => credit > 0 || closed ? Promise.resolve() : new Promise((r) => granted2 = r);
|
|
1054
|
-
void (async () => {
|
|
1055
|
-
try {
|
|
1056
|
-
for (; ; ) {
|
|
1057
|
-
await turn();
|
|
1058
|
-
if (closed) return;
|
|
1059
|
-
const chunk2 = await socket.read(CHUNK);
|
|
1060
|
-
if (chunk2.length === 0) break;
|
|
1061
|
-
credit--;
|
|
1062
|
-
send(chunk2, [chunk2.buffer]);
|
|
1063
|
-
}
|
|
1064
|
-
send({ end: true });
|
|
1065
|
-
} catch (err) {
|
|
1066
|
-
send({ error: codeOf(err) });
|
|
1067
|
-
}
|
|
1068
|
-
})();
|
|
1069
|
-
return () => close(true);
|
|
1070
824
|
}
|
|
1071
825
|
|
|
1072
|
-
// src/
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
PIPE: 13,
|
|
1087
|
-
ALRM: 14,
|
|
1088
|
-
TERM: 15,
|
|
1089
|
-
STKFLT: 16,
|
|
1090
|
-
CHLD: 17,
|
|
1091
|
-
CONT: 18,
|
|
1092
|
-
STOP: 19,
|
|
1093
|
-
TSTP: 20,
|
|
1094
|
-
TTIN: 21,
|
|
1095
|
-
TTOU: 22,
|
|
1096
|
-
URG: 23,
|
|
1097
|
-
XCPU: 24,
|
|
1098
|
-
XFSZ: 25,
|
|
1099
|
-
VTALRM: 26,
|
|
1100
|
-
PROF: 27,
|
|
1101
|
-
WINCH: 28,
|
|
1102
|
-
POLL: 29,
|
|
1103
|
-
PWR: 30,
|
|
1104
|
-
SYS: 31
|
|
1105
|
-
};
|
|
1106
|
-
var IGNORED_BY_DEFAULT = /* @__PURE__ */ new Set([SIG.CHLD, SIG.URG, SIG.WINCH]);
|
|
1107
|
-
var STOPPING = /* @__PURE__ */ new Set([SIG.STOP, SIG.TSTP, SIG.TTIN, SIG.TTOU]);
|
|
1108
|
-
function defaultAction(sig) {
|
|
1109
|
-
if (sig === SIG.CONT) return "continue";
|
|
1110
|
-
if (STOPPING.has(sig)) return "stop";
|
|
1111
|
-
return IGNORED_BY_DEFAULT.has(sig) ? "ignore" : "terminate";
|
|
1112
|
-
}
|
|
1113
|
-
function isSignal(sig) {
|
|
1114
|
-
return Number.isInteger(sig) && sig >= 1 && sig <= 31;
|
|
1115
|
-
}
|
|
1116
|
-
function sigbit(sig) {
|
|
1117
|
-
return 1 << sig;
|
|
826
|
+
// src/pin.ts
|
|
827
|
+
function pinScript(url) {
|
|
828
|
+
try {
|
|
829
|
+
const request2 = new XMLHttpRequest();
|
|
830
|
+
request2.open("GET", url.href, false);
|
|
831
|
+
request2.send();
|
|
832
|
+
if (request2.status !== 200) return url.href;
|
|
833
|
+
const source = `${request2.responseText}
|
|
834
|
+
//# sourceURL=${url.href}
|
|
835
|
+
`;
|
|
836
|
+
return URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
|
|
837
|
+
} catch {
|
|
838
|
+
return url.href;
|
|
839
|
+
}
|
|
1118
840
|
}
|
|
1119
|
-
var SIGNAL_BY_NAME = {
|
|
1120
|
-
SIGINT: SIG.INT,
|
|
1121
|
-
SIGTERM: SIG.TERM,
|
|
1122
|
-
SIGKILL: SIG.KILL,
|
|
1123
|
-
SIGSTOP: SIG.STOP,
|
|
1124
|
-
SIGCONT: SIG.CONT
|
|
1125
|
-
};
|
|
1126
841
|
|
|
1127
|
-
// src/
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
});
|
|
1144
|
-
};
|
|
1145
|
-
const start = async (call) => {
|
|
1146
|
-
const abort = new AbortController();
|
|
1147
|
-
const entry = { abort };
|
|
1148
|
-
open.set(call.nid, entry);
|
|
1149
|
-
try {
|
|
1150
|
-
const response = await transport.fetch({
|
|
1151
|
-
url: call.url,
|
|
1152
|
-
method: call.method,
|
|
1153
|
-
headers: call.headers,
|
|
1154
|
-
...call.body ? { body: call.body } : {},
|
|
1155
|
-
signal: abort.signal
|
|
1156
|
-
});
|
|
1157
|
-
if (!open.has(call.nid)) {
|
|
1158
|
-
await response.cancel();
|
|
1159
|
-
return;
|
|
842
|
+
// src/cdp/port.ts
|
|
843
|
+
function portCdpHook(ask, gone) {
|
|
844
|
+
return (request2) => new Promise((resolve, reject) => {
|
|
845
|
+
const { port1, port2 } = new MessageChannel();
|
|
846
|
+
let closed = false;
|
|
847
|
+
const conn = {
|
|
848
|
+
send: (m) => {
|
|
849
|
+
if (!closed) port1.postMessage({ m });
|
|
850
|
+
},
|
|
851
|
+
onmessage: null,
|
|
852
|
+
onclose: null,
|
|
853
|
+
close: () => {
|
|
854
|
+
if (closed) return;
|
|
855
|
+
closed = true;
|
|
856
|
+
port1.postMessage({ close: true });
|
|
857
|
+
port1.close();
|
|
1160
858
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
const read = async (nid) => {
|
|
1174
|
-
const body = open.get(nid)?.body;
|
|
1175
|
-
if (!body) return;
|
|
1176
|
-
try {
|
|
1177
|
-
const next = await body.next();
|
|
1178
|
-
if (next.done) {
|
|
1179
|
-
open.delete(nid);
|
|
1180
|
-
peer.postMessage({ net: "end", nid });
|
|
1181
|
-
} else {
|
|
1182
|
-
const bytes = next.value.slice();
|
|
1183
|
-
peer.postMessage({ net: "chunk", nid, bytes }, [bytes.buffer]);
|
|
859
|
+
};
|
|
860
|
+
port1.addEventListener("message", ({ data }) => {
|
|
861
|
+
if ("m" in data) conn.onmessage?.(data.m);
|
|
862
|
+
else if ("open" in data) resolve(conn);
|
|
863
|
+
else if ("error" in data) {
|
|
864
|
+
closed = true;
|
|
865
|
+
port1.close();
|
|
866
|
+
reject(new Error(data.error));
|
|
867
|
+
} else if (!closed) {
|
|
868
|
+
closed = true;
|
|
869
|
+
port1.close();
|
|
870
|
+
conn.onclose?.(data.close ?? void 0);
|
|
1184
871
|
}
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
answer(call) {
|
|
1197
|
-
if (call.net === "fetch") void start(call);
|
|
1198
|
-
else if (call.net === "read") void read(call.nid);
|
|
1199
|
-
else drop(call.nid);
|
|
1200
|
-
},
|
|
1201
|
-
close() {
|
|
1202
|
-
for (const nid of [...open.keys()]) drop(nid);
|
|
1203
|
-
}
|
|
1204
|
-
};
|
|
872
|
+
});
|
|
873
|
+
port1.start();
|
|
874
|
+
void gone?.then(() => {
|
|
875
|
+
if (closed) return;
|
|
876
|
+
closed = true;
|
|
877
|
+
port1.close();
|
|
878
|
+
reject(new Error("the CDP host is gone"));
|
|
879
|
+
conn.onclose?.("the CDP host is gone");
|
|
880
|
+
});
|
|
881
|
+
ask(request2, port2);
|
|
882
|
+
});
|
|
1205
883
|
}
|
|
1206
884
|
|
|
1207
|
-
// src/client/
|
|
1208
|
-
var
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
885
|
+
// src/client/protocol.ts
|
|
886
|
+
var PROTOCOL = [1, 5];
|
|
887
|
+
var FS_METHODS = [
|
|
888
|
+
"readFile",
|
|
889
|
+
"writeFile",
|
|
890
|
+
"stat",
|
|
891
|
+
"lstat",
|
|
892
|
+
"readdir",
|
|
893
|
+
"mkdir",
|
|
894
|
+
"rm",
|
|
895
|
+
"rename",
|
|
896
|
+
"realpath",
|
|
897
|
+
"symlink",
|
|
898
|
+
"readlink",
|
|
899
|
+
"exists"
|
|
900
|
+
];
|
|
901
|
+
function locksOf(scope = globalThis) {
|
|
902
|
+
const locks = scope.navigator?.locks;
|
|
903
|
+
return typeof locks?.request === "function" ? locks : void 0;
|
|
1214
904
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
const next = `${resolved}/${name2}`;
|
|
1221
|
-
if (!(await fs.lstat(next)).isSymbolicLink) {
|
|
1222
|
-
resolved = next;
|
|
1223
|
-
parts = rest;
|
|
1224
|
-
continue;
|
|
1225
|
-
}
|
|
1226
|
-
if (++hops > MAX_LINKS) throw fsError("ELOOP", path);
|
|
1227
|
-
const target = await fs.readlink(next);
|
|
1228
|
-
const base = target.startsWith("/") ? "/" : resolved || "/";
|
|
1229
|
-
parts = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest];
|
|
1230
|
-
resolved = "";
|
|
1231
|
-
}
|
|
1232
|
-
return resolved || "/";
|
|
905
|
+
function versionError(theirs) {
|
|
906
|
+
const major = Array.isArray(theirs) ? theirs[0] : void 0;
|
|
907
|
+
if (major === PROTOCOL[0]) return void 0;
|
|
908
|
+
const named = typeof major === "number" ? `${major}.x` : "without a version";
|
|
909
|
+
return `slicc-kernel client protocol ${named} is not supported: this side speaks ${PROTOCOL.join(".")}`;
|
|
1233
910
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
await fs.writeFile(path(0), args[1]);
|
|
1243
|
-
return { result: true };
|
|
1244
|
-
case "stat":
|
|
1245
|
-
return { result: await fs.stat(path(0)) };
|
|
1246
|
-
case "lstat":
|
|
1247
|
-
return { result: await fs.lstat(path(0)) };
|
|
1248
|
-
case "readdir":
|
|
1249
|
-
return { result: await fs.readdir(path(0)) };
|
|
1250
|
-
case "mkdir":
|
|
1251
|
-
await fs.mkdir(path(0), { recursive: true });
|
|
1252
|
-
return { result: true };
|
|
1253
|
-
case "rm":
|
|
1254
|
-
await fs.rm(path(0), { recursive: true, force: args[1] === true });
|
|
1255
|
-
return { result: true };
|
|
1256
|
-
case "rename":
|
|
1257
|
-
await fs.rename(path(0), path(1));
|
|
1258
|
-
return { result: true };
|
|
1259
|
-
case "realpath":
|
|
1260
|
-
return { result: await realpath(fs, String(args[0])) };
|
|
1261
|
-
case "symlink":
|
|
1262
|
-
await fs.symlink(String(args[0]), path(1));
|
|
1263
|
-
return { result: true };
|
|
1264
|
-
case "readlink":
|
|
1265
|
-
return { result: await fs.readlink(path(0)) };
|
|
1266
|
-
default:
|
|
1267
|
-
return { result: await fs.exists(path(0)) };
|
|
911
|
+
|
|
912
|
+
// src/kernel/pipe.ts
|
|
913
|
+
var PIPE_CAPACITY = 64 * 1024;
|
|
914
|
+
var PipeError = class extends Error {
|
|
915
|
+
code;
|
|
916
|
+
constructor(code) {
|
|
917
|
+
super(code);
|
|
918
|
+
this.code = code;
|
|
1268
919
|
}
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
]),
|
|
1307
|
-
gone
|
|
1308
|
-
);
|
|
1309
|
-
registered.set(req.id, l.cdp.register(hook, req.runtime));
|
|
1310
|
-
return req.id;
|
|
1311
|
-
},
|
|
1312
|
-
drop,
|
|
1313
|
-
close: () => {
|
|
1314
|
-
for (const id of [...registered.keys()]) drop(id);
|
|
920
|
+
};
|
|
921
|
+
var KernelPipe = class {
|
|
922
|
+
chunks = [];
|
|
923
|
+
size = 0;
|
|
924
|
+
readers = 0;
|
|
925
|
+
writers = 0;
|
|
926
|
+
waiters = [];
|
|
927
|
+
capacity;
|
|
928
|
+
constructor(capacity = PIPE_CAPACITY) {
|
|
929
|
+
this.capacity = capacity;
|
|
930
|
+
}
|
|
931
|
+
get buffered() {
|
|
932
|
+
return this.size;
|
|
933
|
+
}
|
|
934
|
+
get readReady() {
|
|
935
|
+
return this.size > 0 || this.writers === 0;
|
|
936
|
+
}
|
|
937
|
+
get writeReady() {
|
|
938
|
+
return this.size < this.capacity || this.readers === 0;
|
|
939
|
+
}
|
|
940
|
+
get writersGone() {
|
|
941
|
+
return this.writers === 0;
|
|
942
|
+
}
|
|
943
|
+
get readersGone() {
|
|
944
|
+
return this.readers === 0;
|
|
945
|
+
}
|
|
946
|
+
openRead() {
|
|
947
|
+
this.readers += 1;
|
|
948
|
+
}
|
|
949
|
+
openWrite() {
|
|
950
|
+
this.writers += 1;
|
|
951
|
+
}
|
|
952
|
+
closeRead() {
|
|
953
|
+
if (this.readers > 0) this.readers -= 1;
|
|
954
|
+
if (this.readers === 0) {
|
|
955
|
+
this.chunks = [];
|
|
956
|
+
this.size = 0;
|
|
1315
957
|
}
|
|
1316
|
-
|
|
1317
|
-
}
|
|
1318
|
-
function watchOp(l, req, watches, send) {
|
|
1319
|
-
if (req.op === "unwatch") {
|
|
1320
|
-
watches.get(req.watch)?.();
|
|
1321
|
-
watches.delete(req.watch);
|
|
1322
|
-
return { result: true };
|
|
958
|
+
this.wake();
|
|
1323
959
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
(change) => send({ watch: req.id, change })
|
|
1328
|
-
);
|
|
1329
|
-
watches.set(req.id, unwatch);
|
|
1330
|
-
return { result: req.id };
|
|
1331
|
-
}
|
|
1332
|
-
function serveClient(port, host) {
|
|
1333
|
-
let state = "new";
|
|
1334
|
-
const terminals = /* @__PURE__ */ new Map();
|
|
1335
|
-
const groups = /* @__PURE__ */ new Set();
|
|
1336
|
-
const sessions = /* @__PURE__ */ new Set();
|
|
1337
|
-
const watches = /* @__PURE__ */ new Map();
|
|
1338
|
-
let net;
|
|
1339
|
-
const ended = Promise.withResolvers();
|
|
1340
|
-
const send = (message2, transfer = []) => {
|
|
1341
|
-
if (state !== "closed") port.postMessage(message2, transfer);
|
|
1342
|
-
};
|
|
1343
|
-
const reply = (id, body, transfer) => send({ id, ...body }, transfer);
|
|
1344
|
-
const cdps = cdpRegistry(send, ended.promise);
|
|
1345
|
-
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
1346
|
-
if (state === "closed") return;
|
|
1347
|
-
send({ bye });
|
|
1348
|
-
state = "closed";
|
|
1349
|
-
net?.close();
|
|
1350
|
-
for (const unwatch of watches.values()) unwatch();
|
|
1351
|
-
watches.clear();
|
|
1352
|
-
cdps.close();
|
|
1353
|
-
for (const session of terminals.values()) session.close();
|
|
1354
|
-
terminals.clear();
|
|
1355
|
-
if (kill) killGroups(host, [...groups]);
|
|
1356
|
-
groups.clear();
|
|
1357
|
-
port.close?.();
|
|
1358
|
-
ended.resolve();
|
|
960
|
+
closeWrite() {
|
|
961
|
+
if (this.writers > 0) this.writers -= 1;
|
|
962
|
+
this.wake();
|
|
1359
963
|
}
|
|
1360
|
-
async
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
detach();
|
|
1365
|
-
return;
|
|
964
|
+
async read(max, signal) {
|
|
965
|
+
while (this.size === 0) {
|
|
966
|
+
if (this.writers === 0) return new Uint8Array(0);
|
|
967
|
+
await this.changed(signal);
|
|
1366
968
|
}
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
state = "open";
|
|
1371
|
-
if (message2.lock && host.locks) void host.locks.request(message2.lock, () => detach());
|
|
1372
|
-
send({
|
|
1373
|
-
hello: {
|
|
1374
|
-
protocol: PROTOCOL,
|
|
1375
|
-
traits: transport.traits,
|
|
1376
|
-
...host.lock ? { lock: host.lock } : {}
|
|
1377
|
-
}
|
|
1378
|
-
});
|
|
969
|
+
const out = this.take(Math.min(max, this.size));
|
|
970
|
+
this.wake();
|
|
971
|
+
return out;
|
|
1379
972
|
}
|
|
1380
|
-
async
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
});
|
|
1385
|
-
if (state === "closed") {
|
|
1386
|
-
session.close();
|
|
1387
|
-
} else {
|
|
1388
|
-
terminals.set(req.id, session);
|
|
1389
|
-
sessions.add(session.pid);
|
|
1390
|
-
reply(req.id, { started: session.pid });
|
|
973
|
+
async peek(max, signal) {
|
|
974
|
+
while (this.size === 0) {
|
|
975
|
+
if (this.writers === 0) return new Uint8Array(0);
|
|
976
|
+
await this.changed(signal);
|
|
1391
977
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
978
|
+
const out = new Uint8Array(Math.min(max, this.size));
|
|
979
|
+
let filled = 0;
|
|
980
|
+
for (const chunk2 of this.chunks) {
|
|
981
|
+
if (filled === out.length) break;
|
|
982
|
+
const count = Math.min(chunk2.length, out.length - filled);
|
|
983
|
+
out.set(chunk2.subarray(0, count), filled);
|
|
984
|
+
filled += count;
|
|
1396
985
|
}
|
|
986
|
+
return out;
|
|
1397
987
|
}
|
|
1398
|
-
async
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
if (req.options.pgid === void 0) sessions.add(pid);
|
|
1412
|
-
reply(req.id, { started: pid });
|
|
988
|
+
async write(bytes, signal) {
|
|
989
|
+
let offset = 0;
|
|
990
|
+
while (offset < bytes.length) {
|
|
991
|
+
if (this.readers === 0) throw new PipeError("EPIPE");
|
|
992
|
+
const room = this.capacity - this.size;
|
|
993
|
+
if (room === 0) {
|
|
994
|
+
try {
|
|
995
|
+
await this.changed(signal);
|
|
996
|
+
} catch (e) {
|
|
997
|
+
if (offset > 0) return offset;
|
|
998
|
+
throw e;
|
|
999
|
+
}
|
|
1000
|
+
continue;
|
|
1413
1001
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1002
|
+
const n = Math.min(room, bytes.length - offset);
|
|
1003
|
+
this.chunks.push(bytes.slice(offset, offset + n));
|
|
1004
|
+
this.size += n;
|
|
1005
|
+
offset += n;
|
|
1006
|
+
this.wake();
|
|
1007
|
+
}
|
|
1008
|
+
return bytes.length;
|
|
1418
1009
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
case "terminal": {
|
|
1430
|
-
const session = terminals.get(req.terminal);
|
|
1431
|
-
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
1432
|
-
act(session, req, signalOf(host));
|
|
1433
|
-
return { result: true };
|
|
1434
|
-
}
|
|
1435
|
-
case "kill":
|
|
1436
|
-
if (!l.kill(req.pid, signalOf(host)(req.signal))) throw fsError("ESRCH", `pid ${req.pid}`);
|
|
1437
|
-
return { result: true };
|
|
1438
|
-
case "ps":
|
|
1439
|
-
return {
|
|
1440
|
-
result: l.list().map(({ tid: _, ...entry }) => entry)
|
|
1441
|
-
};
|
|
1442
|
-
case "fs":
|
|
1443
|
-
if (!FS_METHODS.includes(req.method)) {
|
|
1444
|
-
throw new Error(`unknown file system call ${req.method}`);
|
|
1445
|
-
}
|
|
1446
|
-
return fsCall(l.fs, req.method, req.args);
|
|
1447
|
-
case "watch":
|
|
1448
|
-
case "unwatch":
|
|
1449
|
-
return watchOp(l, req, watches, send);
|
|
1450
|
-
case "mount":
|
|
1451
|
-
return { result: await l.mount(req.spec) };
|
|
1452
|
-
case "umount":
|
|
1453
|
-
l.umount(req.target);
|
|
1454
|
-
return { result: true };
|
|
1455
|
-
case "mounts":
|
|
1456
|
-
return { result: l.mounts.list() };
|
|
1457
|
-
case "dial":
|
|
1458
|
-
return dialFor(l, req, watches);
|
|
1459
|
-
case "serve-cdp":
|
|
1460
|
-
return { result: cdps.serve(l, req) };
|
|
1461
|
-
case "unserve-cdp":
|
|
1462
|
-
return { result: cdps.drop(req.registration) };
|
|
1463
|
-
case "detach":
|
|
1464
|
-
return { result: true };
|
|
1465
|
-
default:
|
|
1466
|
-
throw new Error(`unknown op ${req.op}`);
|
|
1010
|
+
take(n) {
|
|
1011
|
+
const out = new Uint8Array(n);
|
|
1012
|
+
let filled = 0;
|
|
1013
|
+
while (filled < n) {
|
|
1014
|
+
const head = this.chunks[0];
|
|
1015
|
+
const count = Math.min(head.length, n - filled);
|
|
1016
|
+
out.set(head.subarray(0, count), filled);
|
|
1017
|
+
filled += count;
|
|
1018
|
+
if (count === head.length) this.chunks.shift();
|
|
1019
|
+
else this.chunks[0] = head.subarray(count);
|
|
1467
1020
|
}
|
|
1021
|
+
this.size -= n;
|
|
1022
|
+
return out;
|
|
1468
1023
|
}
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
if (req.op === "detach") detach(req.kill === true);
|
|
1491
|
-
},
|
|
1492
|
-
(err) => {
|
|
1493
|
-
const code = err?.code;
|
|
1494
|
-
reply(req.id, {
|
|
1495
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1496
|
-
...typeof code === "string" ? { code } : {}
|
|
1497
|
-
});
|
|
1498
|
-
}
|
|
1499
|
-
);
|
|
1500
|
-
});
|
|
1501
|
-
port.addEventListener("close", () => detach());
|
|
1502
|
-
port.start?.();
|
|
1503
|
-
return { detach, closed: ended.promise };
|
|
1504
|
-
}
|
|
1024
|
+
changed(signal) {
|
|
1025
|
+
if (signal?.aborted) return Promise.reject(new PipeError("EINTR"));
|
|
1026
|
+
return new Promise((resolve, reject) => {
|
|
1027
|
+
const onAbort = () => {
|
|
1028
|
+
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
1029
|
+
reject(new PipeError("EINTR"));
|
|
1030
|
+
};
|
|
1031
|
+
const waiter = () => {
|
|
1032
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1033
|
+
resolve();
|
|
1034
|
+
};
|
|
1035
|
+
this.waiters.push(waiter);
|
|
1036
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
wake() {
|
|
1040
|
+
const waiters = this.waiters;
|
|
1041
|
+
this.waiters = [];
|
|
1042
|
+
for (const resolve of waiters) resolve();
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1505
1045
|
|
|
1506
|
-
// src/
|
|
1507
|
-
var
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
var CHECK_MS = 1e3;
|
|
1514
|
-
var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
|
|
1515
|
-
var ERRNO_BY_DOM_ERROR = {
|
|
1516
|
-
NotFoundError: "ENOENT",
|
|
1517
|
-
TypeMismatchError: "ENOTDIR",
|
|
1518
|
-
InvalidModificationError: "ENOTEMPTY",
|
|
1519
|
-
NoModificationAllowedError: "EBUSY",
|
|
1520
|
-
TypeError: "EINVAL",
|
|
1521
|
-
NotAllowedError: "EACCES"
|
|
1046
|
+
// src/kernel/fd-table.ts
|
|
1047
|
+
var KernelError = class extends Error {
|
|
1048
|
+
code;
|
|
1049
|
+
constructor(code) {
|
|
1050
|
+
super(code);
|
|
1051
|
+
this.code = code;
|
|
1052
|
+
}
|
|
1522
1053
|
};
|
|
1523
|
-
function
|
|
1524
|
-
|
|
1525
|
-
const name2 = err?.name;
|
|
1526
|
-
return fsError(typeof name2 === "string" && ERRNO_BY_DOM_ERROR[name2] || "EIO", path);
|
|
1054
|
+
function pollFile(file) {
|
|
1055
|
+
return file.poll?.() ?? { readable: !!file.read, writable: !!file.write, hangup: false };
|
|
1527
1056
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1057
|
+
var OpenFile = class {
|
|
1058
|
+
refs = 1;
|
|
1059
|
+
file;
|
|
1060
|
+
constructor(file) {
|
|
1061
|
+
this.file = file;
|
|
1062
|
+
}
|
|
1063
|
+
retain() {
|
|
1064
|
+
this.refs += 1;
|
|
1065
|
+
return this;
|
|
1066
|
+
}
|
|
1067
|
+
release() {
|
|
1068
|
+
this.refs -= 1;
|
|
1069
|
+
if (this.refs === 0) return this.file.close();
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
function openPipe(capacity) {
|
|
1073
|
+
const pipe = new KernelPipe(capacity);
|
|
1074
|
+
pipe.openRead();
|
|
1075
|
+
pipe.openWrite();
|
|
1076
|
+
return {
|
|
1077
|
+
read: new OpenFile({
|
|
1078
|
+
read: async (max, signal) => {
|
|
1079
|
+
try {
|
|
1080
|
+
return await pipe.read(max, signal);
|
|
1081
|
+
} catch (e) {
|
|
1082
|
+
throw new KernelError(e.code);
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
close: () => pipe.closeRead(),
|
|
1086
|
+
poll: () => ({ readable: pipe.readReady, writable: false, hangup: pipe.writersGone }),
|
|
1087
|
+
changed: (signal) => pipe.changed(signal)
|
|
1088
|
+
}),
|
|
1089
|
+
write: new OpenFile({
|
|
1090
|
+
write: async (bytes, signal) => {
|
|
1091
|
+
try {
|
|
1092
|
+
return await pipe.write(bytes, signal);
|
|
1093
|
+
} catch (e) {
|
|
1094
|
+
throw new KernelError(e.code);
|
|
1095
|
+
}
|
|
1096
|
+
},
|
|
1097
|
+
close: () => pipe.closeWrite(),
|
|
1098
|
+
poll: () => ({ readable: false, writable: pipe.writeReady, hangup: pipe.readersGone }),
|
|
1099
|
+
changed: (signal) => pipe.changed(signal)
|
|
1100
|
+
})
|
|
1101
|
+
};
|
|
1531
1102
|
}
|
|
1532
|
-
function
|
|
1533
|
-
|
|
1103
|
+
function bytesSource(data) {
|
|
1104
|
+
let offset = 0;
|
|
1105
|
+
return new OpenFile({
|
|
1106
|
+
read: async (max) => {
|
|
1107
|
+
const out = data.subarray(offset, offset + max);
|
|
1108
|
+
offset += out.length;
|
|
1109
|
+
return out;
|
|
1110
|
+
},
|
|
1111
|
+
close: () => {
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1534
1114
|
}
|
|
1535
|
-
function
|
|
1536
|
-
return
|
|
1115
|
+
function sinkFile(onData) {
|
|
1116
|
+
return new OpenFile({
|
|
1117
|
+
write: async (bytes) => {
|
|
1118
|
+
onData(bytes.slice());
|
|
1119
|
+
return bytes.length;
|
|
1120
|
+
},
|
|
1121
|
+
close: () => {
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1537
1124
|
}
|
|
1538
|
-
function
|
|
1539
|
-
return
|
|
1125
|
+
function nullFile() {
|
|
1126
|
+
return new OpenFile({
|
|
1127
|
+
read: async () => new Uint8Array(0),
|
|
1128
|
+
write: async (bytes) => bytes.length,
|
|
1129
|
+
close: () => {
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1540
1132
|
}
|
|
1541
|
-
function
|
|
1542
|
-
const
|
|
1543
|
-
|
|
1133
|
+
function deviceFile(device, access) {
|
|
1134
|
+
const read = async (max) => {
|
|
1135
|
+
if (device === "null") return new Uint8Array(0);
|
|
1136
|
+
const out = new Uint8Array(max);
|
|
1137
|
+
if (device === "urandom") {
|
|
1138
|
+
for (let at = 0; at < max; at += 65536) {
|
|
1139
|
+
crypto.getRandomValues(out.subarray(at, Math.min(max, at + 65536)));
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
return out;
|
|
1143
|
+
};
|
|
1144
|
+
return new OpenFile({
|
|
1145
|
+
...access !== "write" ? { read } : {},
|
|
1146
|
+
...access !== "read" ? { write: async (bytes) => bytes.length } : {},
|
|
1147
|
+
seek: async () => 0,
|
|
1148
|
+
heldMeta: { device, ...access ? { access } : {} },
|
|
1149
|
+
close: () => {
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1544
1152
|
}
|
|
1545
|
-
function
|
|
1546
|
-
return
|
|
1153
|
+
function heldFile(meta) {
|
|
1154
|
+
return new OpenFile({ held: true, ...meta ? { heldMeta: meta } : {}, close: () => {
|
|
1155
|
+
} });
|
|
1547
1156
|
}
|
|
1548
|
-
function
|
|
1549
|
-
return "
|
|
1157
|
+
function kernelFdKind(file) {
|
|
1158
|
+
if (file.held) return "held";
|
|
1159
|
+
if (file.heldMeta && "device" in file.heldMeta) return "device";
|
|
1160
|
+
if (file.tty) return "tty";
|
|
1161
|
+
return file.seek ? "file" : "stream";
|
|
1550
1162
|
}
|
|
1551
|
-
var
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
this.channel.unref?.();
|
|
1163
|
+
var FdTable = class _FdTable {
|
|
1164
|
+
static MAX_FDS = 1024;
|
|
1165
|
+
fds = /* @__PURE__ */ new Map();
|
|
1166
|
+
cloexec = /* @__PURE__ */ new Set();
|
|
1167
|
+
status = /* @__PURE__ */ new Map();
|
|
1168
|
+
get(fd) {
|
|
1169
|
+
const file = this.fds.get(fd);
|
|
1170
|
+
if (!file) throw new KernelError("EBADF");
|
|
1171
|
+
return file;
|
|
1172
|
+
}
|
|
1173
|
+
stdioTerminal() {
|
|
1174
|
+
for (const fd of [0, 1, 2]) {
|
|
1175
|
+
const tty = this.fds.get(fd)?.file.tty;
|
|
1176
|
+
if (tty) return tty;
|
|
1566
1177
|
}
|
|
1178
|
+
return void 0;
|
|
1567
1179
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
this.channel?.postMessage(paths);
|
|
1180
|
+
numbers() {
|
|
1181
|
+
return [...this.fds.keys()].sort((a, b) => a - b);
|
|
1571
1182
|
}
|
|
1572
|
-
|
|
1573
|
-
return
|
|
1574
|
-
}
|
|
1575
|
-
async walk(parts, from, start) {
|
|
1576
|
-
let dir = start;
|
|
1577
|
-
for (let at = from; at < parts.length; at++) {
|
|
1578
|
-
dir = await dir.getDirectoryHandle(parts[at]);
|
|
1579
|
-
this.dirs.set(`/${parts.slice(0, at + 1).join("/")}`, dir);
|
|
1580
|
-
this.checked.set(dir, performance.now());
|
|
1581
|
-
}
|
|
1582
|
-
return dir;
|
|
1583
|
-
}
|
|
1584
|
-
async dir(path) {
|
|
1585
|
-
const parts = split(path);
|
|
1586
|
-
let from = parts.length;
|
|
1587
|
-
let cached;
|
|
1588
|
-
for (; from > 0 && !cached; from--)
|
|
1589
|
-
cached = this.dirs.get(`/${parts.slice(0, from).join("/")}`);
|
|
1590
|
-
if (cached) from++;
|
|
1591
|
-
const at = `/${parts.slice(0, from).join("/")}`;
|
|
1592
|
-
if (cached && !await this.current(at, cached)) {
|
|
1593
|
-
this.forget(at);
|
|
1594
|
-
cached = void 0;
|
|
1595
|
-
from = 0;
|
|
1596
|
-
}
|
|
1597
|
-
try {
|
|
1598
|
-
return await this.walk(parts, from, cached ?? this.root);
|
|
1599
|
-
} catch (err) {
|
|
1600
|
-
throw translate(err, path);
|
|
1601
|
-
}
|
|
1183
|
+
has(fd) {
|
|
1184
|
+
return this.fds.has(fd);
|
|
1602
1185
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
if (where === null || `/${where.join("/")}` !== path) return false;
|
|
1607
|
-
this.checked.set(dir, performance.now());
|
|
1608
|
-
return true;
|
|
1186
|
+
setCloseOnExec(fd) {
|
|
1187
|
+
this.get(fd);
|
|
1188
|
+
this.cloexec.add(fd);
|
|
1609
1189
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
1190
|
+
clearCloseOnExec(fd) {
|
|
1191
|
+
this.get(fd);
|
|
1192
|
+
this.cloexec.delete(fd);
|
|
1612
1193
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
try {
|
|
1616
|
-
return await op(dir);
|
|
1617
|
-
} catch (err) {
|
|
1618
|
-
if (err?.name !== "NotFoundError" || await this.current(path, dir))
|
|
1619
|
-
throw err;
|
|
1620
|
-
this.forget(path);
|
|
1621
|
-
return op(await this.dir(path));
|
|
1622
|
-
}
|
|
1194
|
+
closesOnExec(fd) {
|
|
1195
|
+
return this.cloexec.has(fd);
|
|
1623
1196
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1197
|
+
setStatusFlags(fd, flags) {
|
|
1198
|
+
this.get(fd);
|
|
1199
|
+
this.status.set(fd, flags);
|
|
1626
1200
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
const name2 = parts.pop();
|
|
1630
|
-
if (name2 === void 0) throw fsError("EBUSY", path);
|
|
1631
|
-
return [`/${parts.join("/")}`, name2];
|
|
1201
|
+
statusFlags(fd) {
|
|
1202
|
+
return this.status.get(fd);
|
|
1632
1203
|
}
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
const [first, second] = known ? ["directory", "file"] : ["file", "directory"];
|
|
1639
|
-
try {
|
|
1640
|
-
return await this.child(parent, name2, first);
|
|
1641
|
-
} catch (err) {
|
|
1642
|
-
if (err?.name !== "TypeMismatchError") throw err;
|
|
1204
|
+
install(file, min = 0) {
|
|
1205
|
+
for (let fd = min; fd < _FdTable.MAX_FDS; fd++) {
|
|
1206
|
+
if (!this.fds.has(fd)) {
|
|
1207
|
+
this.fds.set(fd, file);
|
|
1208
|
+
return fd;
|
|
1643
1209
|
}
|
|
1644
|
-
return this.child(parent, name2, second);
|
|
1645
|
-
}).catch((err) => {
|
|
1646
|
-
throw translate(err, path);
|
|
1647
|
-
});
|
|
1648
|
-
}
|
|
1649
|
-
real(path) {
|
|
1650
|
-
return this.handle(path).then(
|
|
1651
|
-
() => true,
|
|
1652
|
-
() => false
|
|
1653
|
-
);
|
|
1654
|
-
}
|
|
1655
|
-
async linkAlong(path) {
|
|
1656
|
-
for (const entry of await this.meta.get(prefixes(path))) {
|
|
1657
|
-
if (entry?.link === void 0) continue;
|
|
1658
|
-
if (!await this.real(entry.path) && await this.real(parentOf2(entry.path))) return entry;
|
|
1659
1210
|
}
|
|
1660
|
-
|
|
1211
|
+
void Promise.resolve(file.release()).catch(() => void 0);
|
|
1212
|
+
throw new KernelError("EMFILE");
|
|
1661
1213
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
return { path: current, handle: await this.handle(current) };
|
|
1667
|
-
} catch (err) {
|
|
1668
|
-
if (!missing(err)) throw err;
|
|
1669
|
-
const link2 = await this.linkAlong(current);
|
|
1670
|
-
if (!link2) throw err;
|
|
1671
|
-
if (link2.path === current && !follow) return { path: current, link: link2 };
|
|
1672
|
-
const rest = current.slice(link2.path.length);
|
|
1673
|
-
current = resolvePath(parentOf2(link2.path), `${link2.link}${rest}`);
|
|
1674
|
-
}
|
|
1214
|
+
installAt(fd, file) {
|
|
1215
|
+
if (fd < 0 || fd >= _FdTable.MAX_FDS) {
|
|
1216
|
+
void Promise.resolve(file.release()).catch(() => void 0);
|
|
1217
|
+
throw new KernelError("EBADF");
|
|
1675
1218
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1219
|
+
const previous = this.fds.get(fd);
|
|
1220
|
+
this.fds.set(fd, file);
|
|
1221
|
+
this.cloexec.delete(fd);
|
|
1222
|
+
this.status.delete(fd);
|
|
1223
|
+
void Promise.resolve(previous?.release()).catch(() => void 0);
|
|
1681
1224
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
const file = directory ? void 0 : await handle.getFile();
|
|
1685
|
-
const modified = file?.lastModified ?? 0;
|
|
1686
|
-
const mtime = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
|
|
1687
|
-
return {
|
|
1688
|
-
isFile: !directory,
|
|
1689
|
-
isDirectory: directory,
|
|
1690
|
-
isSymbolicLink: false,
|
|
1691
|
-
size: file?.size ?? 0,
|
|
1692
|
-
mode: this.mode(path, directory, meta),
|
|
1693
|
-
mtime: new Date(mtime),
|
|
1694
|
-
atime: new Date(meta?.atimeMs ?? mtime),
|
|
1695
|
-
ctime: new Date(Math.max(meta?.ctimeMs ?? mtime, modified)),
|
|
1696
|
-
ino: meta?.ino ?? inodeOf(path)
|
|
1697
|
-
};
|
|
1225
|
+
dup(fd, min = 0) {
|
|
1226
|
+
return this.install(this.get(fd).retain(), min);
|
|
1698
1227
|
}
|
|
1699
|
-
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
isDirectory: false,
|
|
1704
|
-
isSymbolicLink: true,
|
|
1705
|
-
size: new TextEncoder().encode(entry.link).length,
|
|
1706
|
-
mode: S_IFLNK | 511,
|
|
1707
|
-
mtime: time2,
|
|
1708
|
-
atime: time2,
|
|
1709
|
-
ctime: new Date(entry.ctimeMs ?? 0),
|
|
1710
|
-
ino: entry.ino ?? inodeOf(entry.path)
|
|
1711
|
-
};
|
|
1228
|
+
dup2(oldFd, newFd) {
|
|
1229
|
+
const file = this.get(oldFd);
|
|
1230
|
+
if (oldFd !== newFd) this.installAt(newFd, file.retain());
|
|
1231
|
+
return newFd;
|
|
1712
1232
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1233
|
+
close(fd) {
|
|
1234
|
+
const file = this.get(fd);
|
|
1235
|
+
this.fds.delete(fd);
|
|
1236
|
+
this.cloexec.delete(fd);
|
|
1237
|
+
this.status.delete(fd);
|
|
1238
|
+
return file.release();
|
|
1717
1239
|
}
|
|
1718
|
-
|
|
1719
|
-
|
|
1240
|
+
fork() {
|
|
1241
|
+
const child = new _FdTable();
|
|
1242
|
+
for (const [fd, file] of this.fds) child.fds.set(fd, file.retain());
|
|
1243
|
+
for (const fd of this.cloexec) child.cloexec.add(fd);
|
|
1244
|
+
for (const [fd, flags] of this.status) child.status.set(fd, flags);
|
|
1245
|
+
return child;
|
|
1720
1246
|
}
|
|
1721
|
-
async
|
|
1722
|
-
|
|
1247
|
+
async closeAll() {
|
|
1248
|
+
const files = [...this.fds.values()];
|
|
1249
|
+
this.fds.clear();
|
|
1250
|
+
this.cloexec.clear();
|
|
1251
|
+
this.status.clear();
|
|
1252
|
+
await Promise.allSettled(files.map((file) => Promise.resolve().then(() => file.release())));
|
|
1723
1253
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
// src/kernel/socket.ts
|
|
1257
|
+
var SOMAXCONN = 4096;
|
|
1258
|
+
var EPHEMERAL_FIRST = 32768;
|
|
1259
|
+
var EPHEMERAL_LAST = 60999;
|
|
1260
|
+
var SOL_SOCKET = 1;
|
|
1261
|
+
var SO_TYPE = 3;
|
|
1262
|
+
var SO_ERROR = 4;
|
|
1263
|
+
var SO_SNDBUF = 7;
|
|
1264
|
+
var SO_RCVBUF = 8;
|
|
1265
|
+
var SO_ACCEPTCONN = 30;
|
|
1266
|
+
var SO_DOMAIN = 39;
|
|
1267
|
+
var SOCK_STREAM = 1;
|
|
1268
|
+
var AF_UNIX = 1;
|
|
1269
|
+
var AF_INET = 2;
|
|
1270
|
+
var SHUT_RD = 0;
|
|
1271
|
+
var SHUT_WR = 1;
|
|
1272
|
+
var SHUT_RDWR = 2;
|
|
1273
|
+
function isLoopback(host) {
|
|
1274
|
+
return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
|
|
1275
|
+
}
|
|
1276
|
+
async function pipeCall(op) {
|
|
1277
|
+
try {
|
|
1278
|
+
return await op();
|
|
1279
|
+
} catch (e) {
|
|
1280
|
+
if (e instanceof PipeError) throw new KernelError(e.code);
|
|
1281
|
+
throw e;
|
|
1729
1282
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1283
|
+
}
|
|
1284
|
+
var Changes = class {
|
|
1285
|
+
waiters = [];
|
|
1286
|
+
wait(signal) {
|
|
1287
|
+
if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
|
|
1288
|
+
return new Promise((resolve, reject) => {
|
|
1289
|
+
const onAbort = () => {
|
|
1290
|
+
this.waiters = this.waiters.filter((w) => w !== waiter);
|
|
1291
|
+
reject(new KernelError("EINTR"));
|
|
1292
|
+
};
|
|
1293
|
+
const waiter = () => {
|
|
1294
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1295
|
+
resolve();
|
|
1296
|
+
};
|
|
1297
|
+
this.waiters.push(waiter);
|
|
1298
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1299
|
+
});
|
|
1743
1300
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1301
|
+
wake() {
|
|
1302
|
+
const waiters = this.waiters;
|
|
1303
|
+
this.waiters = [];
|
|
1304
|
+
for (const resolve of waiters) resolve();
|
|
1746
1305
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
const wanted = entry?.link === void 0 ? path : resolvePath(parentOf2(path), entry.link);
|
|
1755
|
-
return [resolvePath((await this.locate(parentOf2(wanted), true)).path, baseOf(wanted)), false];
|
|
1306
|
+
};
|
|
1307
|
+
function link(a, b) {
|
|
1308
|
+
const ab = new KernelPipe();
|
|
1309
|
+
const ba = new KernelPipe();
|
|
1310
|
+
for (const pipe of [ab, ba]) {
|
|
1311
|
+
pipe.openRead();
|
|
1312
|
+
pipe.openWrite();
|
|
1756
1313
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1314
|
+
a.attach({ rx: ba, tx: ab });
|
|
1315
|
+
b.attach({ rx: ab, tx: ba });
|
|
1316
|
+
}
|
|
1317
|
+
var KernelSocket = class _KernelSocket {
|
|
1318
|
+
net;
|
|
1319
|
+
domain;
|
|
1320
|
+
state = "open";
|
|
1321
|
+
local;
|
|
1322
|
+
peer;
|
|
1323
|
+
link;
|
|
1324
|
+
readShut = false;
|
|
1325
|
+
writeShut = false;
|
|
1326
|
+
queue = [];
|
|
1327
|
+
backlog = 0;
|
|
1328
|
+
bound = false;
|
|
1329
|
+
changes = new Changes();
|
|
1330
|
+
options = /* @__PURE__ */ new Map();
|
|
1331
|
+
constructor(net, domain) {
|
|
1332
|
+
this.net = net;
|
|
1333
|
+
this.domain = domain;
|
|
1334
|
+
}
|
|
1335
|
+
attach(link2) {
|
|
1336
|
+
this.link = link2;
|
|
1337
|
+
this.state = "connected";
|
|
1338
|
+
this.changes.wake();
|
|
1339
|
+
}
|
|
1340
|
+
get listening() {
|
|
1341
|
+
return this.state === "listening";
|
|
1342
|
+
}
|
|
1343
|
+
connection() {
|
|
1344
|
+
if (!this.link || this.state !== "connected") throw new KernelError("ENOTCONN");
|
|
1345
|
+
return this.link;
|
|
1346
|
+
}
|
|
1347
|
+
async read(max, signal) {
|
|
1348
|
+
const { rx } = this.connection();
|
|
1349
|
+
if (this.readShut || max <= 0) return new Uint8Array(0);
|
|
1350
|
+
return pipeCall(() => rx.read(max, signal));
|
|
1351
|
+
}
|
|
1352
|
+
async peek(max, signal) {
|
|
1353
|
+
const { rx } = this.connection();
|
|
1354
|
+
if (this.readShut || max <= 0) return new Uint8Array(0);
|
|
1355
|
+
return pipeCall(() => rx.peek(max, signal));
|
|
1356
|
+
}
|
|
1357
|
+
async write(bytes, signal) {
|
|
1358
|
+
const { tx } = this.connection();
|
|
1359
|
+
if (this.writeShut) throw new KernelError("EPIPE");
|
|
1360
|
+
return pipeCall(() => tx.write(bytes, signal));
|
|
1361
|
+
}
|
|
1362
|
+
poll() {
|
|
1363
|
+
if (this.state === "listening") {
|
|
1364
|
+
return { readable: this.queue.length > 0, writable: false, hangup: false };
|
|
1766
1365
|
}
|
|
1767
|
-
if (
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1366
|
+
if (this.state !== "connected" || !this.link) {
|
|
1367
|
+
return { readable: false, writable: false, hangup: true };
|
|
1368
|
+
}
|
|
1369
|
+
const { rx, tx } = this.link;
|
|
1370
|
+
return {
|
|
1371
|
+
readable: this.readShut || rx.readReady,
|
|
1372
|
+
writable: this.writeShut || tx.writeReady,
|
|
1373
|
+
hangup: rx.writersGone && tx.readersGone
|
|
1374
|
+
};
|
|
1771
1375
|
}
|
|
1772
|
-
|
|
1773
|
-
const
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
const
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1376
|
+
changed(signal) {
|
|
1377
|
+
const own = this.changes.wait(signal);
|
|
1378
|
+
if (this.state !== "connected" || !this.link) return own;
|
|
1379
|
+
const waits = [own, this.link.rx.changed(signal), this.link.tx.changed(signal)];
|
|
1380
|
+
for (const wait2 of waits) wait2.catch(() => void 0);
|
|
1381
|
+
return pipeCall(() => Promise.race(waits));
|
|
1382
|
+
}
|
|
1383
|
+
bind(addr) {
|
|
1384
|
+
if (this.state !== "open" || this.local) throw new KernelError("EINVAL");
|
|
1385
|
+
if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
|
|
1386
|
+
this.local = this.net.bind(this, addr);
|
|
1387
|
+
this.bound = true;
|
|
1388
|
+
}
|
|
1389
|
+
listen(backlog) {
|
|
1390
|
+
if (this.state === "connected") throw new KernelError("EISCONN");
|
|
1391
|
+
if (this.state !== "open" && this.state !== "listening") throw new KernelError("EINVAL");
|
|
1392
|
+
if (!this.local) {
|
|
1393
|
+
if (this.domain === "unix") throw new KernelError("EINVAL");
|
|
1394
|
+
this.bind({ family: "inet", host: "0.0.0.0", port: 0 });
|
|
1781
1395
|
}
|
|
1782
|
-
|
|
1396
|
+
this.backlog = Math.min(Math.max(backlog, 1), SOMAXCONN);
|
|
1397
|
+
this.state = "listening";
|
|
1783
1398
|
}
|
|
1784
|
-
async
|
|
1785
|
-
|
|
1399
|
+
async accept(signal, nonblock = false) {
|
|
1400
|
+
for (; ; ) {
|
|
1401
|
+
if (this.state !== "listening") throw new KernelError("EINVAL");
|
|
1402
|
+
const next = this.queue.shift();
|
|
1403
|
+
if (next) return next;
|
|
1404
|
+
if (nonblock) throw new KernelError("EAGAIN");
|
|
1405
|
+
await this.changes.wait(signal);
|
|
1406
|
+
}
|
|
1786
1407
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
return
|
|
1792
|
-
children2.map(async ([name2, child], i) => {
|
|
1793
|
-
if (!isHandle(child)) return [name2, this.linkStat(child)];
|
|
1794
|
-
const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
|
|
1795
|
-
return [name2, stat];
|
|
1796
|
-
})
|
|
1797
|
-
);
|
|
1408
|
+
offer(server) {
|
|
1409
|
+
if (this.state !== "listening" || this.queue.length >= this.backlog) return false;
|
|
1410
|
+
this.queue.push(server);
|
|
1411
|
+
this.changes.wake();
|
|
1412
|
+
return true;
|
|
1798
1413
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1414
|
+
connect(addr) {
|
|
1415
|
+
if (this.state === "connected") throw new KernelError("EISCONN");
|
|
1416
|
+
if (this.state !== "open") throw new KernelError("EINVAL");
|
|
1417
|
+
if (addr.family !== this.domain) throw new KernelError("EAFNOSUPPORT");
|
|
1418
|
+
const target = this.net.target(addr);
|
|
1419
|
+
if (!this.local) {
|
|
1420
|
+
if (addr.family === "inet") {
|
|
1421
|
+
this.local = this.net.bind(this, { family: "inet", host: "127.0.0.1", port: 0 });
|
|
1422
|
+
this.bound = true;
|
|
1423
|
+
} else this.local = { family: "unix", path: "" };
|
|
1805
1424
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1425
|
+
const server = new _KernelSocket(this.net, this.domain);
|
|
1426
|
+
server.local = addr.family === "inet" ? { ...addr, host: canonicalHost(addr.host) } : addr;
|
|
1427
|
+
server.peer = this.local;
|
|
1428
|
+
this.peer = server.local;
|
|
1429
|
+
link(this, server);
|
|
1430
|
+
if (!target.offer(server)) {
|
|
1431
|
+
this.unlink();
|
|
1432
|
+
server.close();
|
|
1433
|
+
throw new KernelError("ECONNREFUSED");
|
|
1810
1434
|
}
|
|
1811
|
-
const [entry] = await this.meta.get([path]);
|
|
1812
|
-
if (entry?.link !== void 0) throw fsError("EEXIST", path);
|
|
1813
|
-
if (options.recursive) await this.mkdir(parentOf2(path), options);
|
|
1814
|
-
const parent = await this.locate(parentOf2(path), true);
|
|
1815
|
-
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
1816
|
-
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
1817
1435
|
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
const error = translate(err, path);
|
|
1824
|
-
if (options.force && error.code === "ENOENT") return;
|
|
1825
|
-
throw error;
|
|
1436
|
+
unlink() {
|
|
1437
|
+
const link2 = this.link;
|
|
1438
|
+
if (link2) {
|
|
1439
|
+
link2.rx.closeRead();
|
|
1440
|
+
link2.tx.closeWrite();
|
|
1826
1441
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
this.changed(found.path);
|
|
1442
|
+
this.link = void 0;
|
|
1443
|
+
this.peer = void 0;
|
|
1444
|
+
this.state = "open";
|
|
1445
|
+
}
|
|
1446
|
+
shutdown(how) {
|
|
1447
|
+
const { rx, tx } = this.connection();
|
|
1448
|
+
if (how !== SHUT_RD && how !== SHUT_WR && how !== SHUT_RDWR) throw new KernelError("EINVAL");
|
|
1449
|
+
if (how !== SHUT_WR && !this.readShut) {
|
|
1450
|
+
this.readShut = true;
|
|
1451
|
+
rx.closeRead();
|
|
1838
1452
|
}
|
|
1839
|
-
|
|
1453
|
+
if (how !== SHUT_RD && !this.writeShut) {
|
|
1454
|
+
this.writeShut = true;
|
|
1455
|
+
tx.closeWrite();
|
|
1456
|
+
}
|
|
1457
|
+
this.changes.wake();
|
|
1840
1458
|
}
|
|
1841
|
-
|
|
1842
|
-
if (
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
throw fsError("ENOTDIR", to);
|
|
1459
|
+
getOption(level, name2) {
|
|
1460
|
+
if (level === SOL_SOCKET) {
|
|
1461
|
+
switch (name2) {
|
|
1462
|
+
case SO_TYPE:
|
|
1463
|
+
return SOCK_STREAM;
|
|
1464
|
+
case SO_ERROR:
|
|
1465
|
+
return 0;
|
|
1466
|
+
case SO_ACCEPTCONN:
|
|
1467
|
+
return this.state === "listening" ? 1 : 0;
|
|
1468
|
+
case SO_DOMAIN:
|
|
1469
|
+
return this.domain === "inet" ? AF_INET : AF_UNIX;
|
|
1470
|
+
case SO_SNDBUF:
|
|
1471
|
+
case SO_RCVBUF:
|
|
1472
|
+
return this.options.get(`${level}:${name2}`) ?? PIPE_CAPACITY;
|
|
1473
|
+
}
|
|
1857
1474
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1475
|
+
return this.options.get(`${level}:${name2}`) ?? 0;
|
|
1476
|
+
}
|
|
1477
|
+
setOption(level, name2, value) {
|
|
1478
|
+
this.options.set(`${level}:${name2}`, value);
|
|
1479
|
+
}
|
|
1480
|
+
close() {
|
|
1481
|
+
const was = this.state;
|
|
1482
|
+
this.state = "closed";
|
|
1483
|
+
if (was === "listening") {
|
|
1484
|
+
for (const pending of this.queue.splice(0)) pending.close();
|
|
1485
|
+
}
|
|
1486
|
+
if (this.link) {
|
|
1487
|
+
if (!this.readShut) this.link.rx.closeRead();
|
|
1488
|
+
if (!this.writeShut) this.link.tx.closeWrite();
|
|
1489
|
+
this.readShut = true;
|
|
1490
|
+
this.writeShut = true;
|
|
1491
|
+
}
|
|
1492
|
+
if (this.bound && this.local) this.net.release(this.local, this);
|
|
1493
|
+
this.bound = false;
|
|
1494
|
+
this.changes.wake();
|
|
1495
|
+
}
|
|
1496
|
+
static pair(net, domain) {
|
|
1497
|
+
const a = new _KernelSocket(net, domain);
|
|
1498
|
+
const b = new _KernelSocket(net, domain);
|
|
1499
|
+
const name2 = domain === "inet" ? { family: "inet", host: "127.0.0.1", port: 0 } : { family: "unix", path: "" };
|
|
1500
|
+
a.local = b.local = a.peer = b.peer = name2;
|
|
1501
|
+
link(a, b);
|
|
1502
|
+
return [a, b];
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
function canonicalHost(host) {
|
|
1506
|
+
return host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
1507
|
+
}
|
|
1508
|
+
function keyOf(addr) {
|
|
1509
|
+
return addr.family === "inet" ? `inet:${addr.port}` : `unix:${addr.path}`;
|
|
1510
|
+
}
|
|
1511
|
+
var LoopbackNet = class {
|
|
1512
|
+
bound = /* @__PURE__ */ new Map();
|
|
1513
|
+
activators = /* @__PURE__ */ new Map();
|
|
1514
|
+
internal = /* @__PURE__ */ new Set();
|
|
1515
|
+
nextEphemeral = EPHEMERAL_FIRST;
|
|
1516
|
+
kernelOnly(port, reserve = false) {
|
|
1517
|
+
if (reserve) this.internal.add(port);
|
|
1518
|
+
return this.internal.has(port);
|
|
1519
|
+
}
|
|
1520
|
+
activate(addr, start) {
|
|
1521
|
+
this.activators.set(keyOf(addr), start);
|
|
1522
|
+
}
|
|
1523
|
+
socket(domain) {
|
|
1524
|
+
return new KernelSocket(this, domain);
|
|
1525
|
+
}
|
|
1526
|
+
listen(addr, backlog = 128) {
|
|
1527
|
+
const socket = this.socket(addr.family);
|
|
1528
|
+
socket.bind(addr);
|
|
1529
|
+
socket.listen(backlog);
|
|
1530
|
+
return socket;
|
|
1531
|
+
}
|
|
1532
|
+
connect(addr) {
|
|
1533
|
+
const socket = this.socket(addr.family);
|
|
1534
|
+
socket.connect(addr);
|
|
1535
|
+
return socket;
|
|
1536
|
+
}
|
|
1537
|
+
bind(socket, addr) {
|
|
1538
|
+
if (addr.family === "unix") {
|
|
1539
|
+
if (addr.path === "") throw new KernelError("EINVAL");
|
|
1540
|
+
} else if (addr.host !== "0.0.0.0" && !isLoopback(addr.host)) {
|
|
1541
|
+
throw new KernelError("EADDRNOTAVAIL");
|
|
1542
|
+
}
|
|
1543
|
+
const named = addr.family === "inet" && addr.port === 0 ? { ...addr, port: this.ephemeral() } : addr;
|
|
1544
|
+
const key = keyOf(named);
|
|
1545
|
+
if (this.bound.has(key)) throw new KernelError("EADDRINUSE");
|
|
1546
|
+
this.bound.set(key, socket);
|
|
1547
|
+
return named;
|
|
1548
|
+
}
|
|
1549
|
+
release(addr, socket) {
|
|
1550
|
+
const key = keyOf(addr);
|
|
1551
|
+
if (this.bound.get(key) === socket) this.bound.delete(key);
|
|
1552
|
+
}
|
|
1553
|
+
target(addr) {
|
|
1554
|
+
if (addr.family === "inet" && !isLoopback(canonicalHost(addr.host))) {
|
|
1555
|
+
throw new KernelError("ENETUNREACH");
|
|
1556
|
+
}
|
|
1557
|
+
const key = keyOf(addr);
|
|
1558
|
+
if (!this.bound.has(key)) this.activators.get(key)?.();
|
|
1559
|
+
const socket = this.bound.get(key);
|
|
1560
|
+
if (!socket?.listening) throw new KernelError("ECONNREFUSED");
|
|
1561
|
+
return socket;
|
|
1562
|
+
}
|
|
1563
|
+
ephemeral() {
|
|
1564
|
+
const span = EPHEMERAL_LAST - EPHEMERAL_FIRST + 1;
|
|
1565
|
+
for (let i = 0; i < span; i++) {
|
|
1566
|
+
const port = this.nextEphemeral;
|
|
1567
|
+
this.nextEphemeral = port === EPHEMERAL_LAST ? EPHEMERAL_FIRST : port + 1;
|
|
1568
|
+
if (!this.bound.has(`inet:${port}`)) return port;
|
|
1569
|
+
}
|
|
1570
|
+
throw new KernelError("EADDRINUSE");
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
|
|
1574
|
+
// src/kernel/dial.ts
|
|
1575
|
+
var CHUNK = 64 * 1024;
|
|
1576
|
+
var LOOPBACK_NAMES = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
|
|
1577
|
+
function dialSocket(net, port, host = "127.0.0.1") {
|
|
1578
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new KernelError("EINVAL");
|
|
1579
|
+
const target = LOOPBACK_NAMES.has(host.toLowerCase()) ? "127.0.0.1" : host;
|
|
1580
|
+
if (net.kernelOnly(port)) {
|
|
1581
|
+
throw Object.assign(
|
|
1582
|
+
new Error(`ECONNREFUSED: port ${port} only takes connections from inside the kernel`),
|
|
1583
|
+
{
|
|
1584
|
+
code: "ECONNREFUSED"
|
|
1585
|
+
}
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
return net.connect({ family: "inet", host: target, port });
|
|
1589
|
+
}
|
|
1590
|
+
var codeOf = (err) => {
|
|
1591
|
+
const code = err?.code;
|
|
1592
|
+
return typeof code === "string" ? code : "EIO";
|
|
1593
|
+
};
|
|
1594
|
+
function serveSocket(socket, port, onClose) {
|
|
1595
|
+
let closed = false;
|
|
1596
|
+
let writes = Promise.resolve();
|
|
1597
|
+
let credit = 0;
|
|
1598
|
+
let granted2;
|
|
1599
|
+
const send = (message2, transfer) => {
|
|
1600
|
+
if (!closed) port.postMessage(message2, transfer);
|
|
1601
|
+
};
|
|
1602
|
+
const close = (reset = false) => {
|
|
1603
|
+
if (closed) return;
|
|
1604
|
+
if (reset) send({ error: "ECONNRESET" });
|
|
1605
|
+
closed = true;
|
|
1606
|
+
granted2?.();
|
|
1607
|
+
socket.close();
|
|
1608
|
+
port.close();
|
|
1609
|
+
onClose?.();
|
|
1610
|
+
};
|
|
1611
|
+
const write = (data) => {
|
|
1612
|
+
writes = writes.then(async () => {
|
|
1613
|
+
if (closed) return;
|
|
1614
|
+
try {
|
|
1615
|
+
await socket.write(data);
|
|
1616
|
+
send({ ack: true });
|
|
1617
|
+
} catch (err) {
|
|
1618
|
+
send({ error: codeOf(err) });
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
};
|
|
1622
|
+
port.addEventListener("message", ({ data }) => {
|
|
1623
|
+
if (data instanceof Uint8Array) write(data);
|
|
1624
|
+
else if ("more" in data) {
|
|
1625
|
+
credit++;
|
|
1626
|
+
granted2?.();
|
|
1627
|
+
} else if ("end" in data) {
|
|
1628
|
+
writes = writes.then(() => {
|
|
1629
|
+
if (!closed) socket.shutdown(SHUT_WR);
|
|
1630
|
+
});
|
|
1631
|
+
} else if ("close" in data) close();
|
|
1632
|
+
});
|
|
1633
|
+
port.start?.();
|
|
1634
|
+
const turn = () => credit > 0 || closed ? Promise.resolve() : new Promise((r) => granted2 = r);
|
|
1635
|
+
void (async () => {
|
|
1636
|
+
try {
|
|
1637
|
+
for (; ; ) {
|
|
1638
|
+
await turn();
|
|
1639
|
+
if (closed) return;
|
|
1640
|
+
const chunk2 = await socket.read(CHUNK);
|
|
1641
|
+
if (chunk2.length === 0) break;
|
|
1642
|
+
credit--;
|
|
1643
|
+
send(chunk2, [chunk2.buffer]);
|
|
1644
|
+
}
|
|
1645
|
+
send({ end: true });
|
|
1646
|
+
} catch (err) {
|
|
1647
|
+
send({ error: codeOf(err) });
|
|
1648
|
+
}
|
|
1649
|
+
})();
|
|
1650
|
+
return () => close(true);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// src/kernel/signals.ts
|
|
1654
|
+
var SIG = {
|
|
1655
|
+
HUP: 1,
|
|
1656
|
+
INT: 2,
|
|
1657
|
+
QUIT: 3,
|
|
1658
|
+
ILL: 4,
|
|
1659
|
+
TRAP: 5,
|
|
1660
|
+
ABRT: 6,
|
|
1661
|
+
BUS: 7,
|
|
1662
|
+
FPE: 8,
|
|
1663
|
+
KILL: 9,
|
|
1664
|
+
USR1: 10,
|
|
1665
|
+
SEGV: 11,
|
|
1666
|
+
USR2: 12,
|
|
1667
|
+
PIPE: 13,
|
|
1668
|
+
ALRM: 14,
|
|
1669
|
+
TERM: 15,
|
|
1670
|
+
STKFLT: 16,
|
|
1671
|
+
CHLD: 17,
|
|
1672
|
+
CONT: 18,
|
|
1673
|
+
STOP: 19,
|
|
1674
|
+
TSTP: 20,
|
|
1675
|
+
TTIN: 21,
|
|
1676
|
+
TTOU: 22,
|
|
1677
|
+
URG: 23,
|
|
1678
|
+
XCPU: 24,
|
|
1679
|
+
XFSZ: 25,
|
|
1680
|
+
VTALRM: 26,
|
|
1681
|
+
PROF: 27,
|
|
1682
|
+
WINCH: 28,
|
|
1683
|
+
POLL: 29,
|
|
1684
|
+
PWR: 30,
|
|
1685
|
+
SYS: 31
|
|
1686
|
+
};
|
|
1687
|
+
var IGNORED_BY_DEFAULT = /* @__PURE__ */ new Set([SIG.CHLD, SIG.URG, SIG.WINCH]);
|
|
1688
|
+
var STOPPING = /* @__PURE__ */ new Set([SIG.STOP, SIG.TSTP, SIG.TTIN, SIG.TTOU]);
|
|
1689
|
+
function defaultAction(sig) {
|
|
1690
|
+
if (sig === SIG.CONT) return "continue";
|
|
1691
|
+
if (STOPPING.has(sig)) return "stop";
|
|
1692
|
+
return IGNORED_BY_DEFAULT.has(sig) ? "ignore" : "terminate";
|
|
1693
|
+
}
|
|
1694
|
+
function isSignal(sig) {
|
|
1695
|
+
return Number.isInteger(sig) && sig >= 1 && sig <= 31;
|
|
1696
|
+
}
|
|
1697
|
+
function sigbit(sig) {
|
|
1698
|
+
return 1 << sig;
|
|
1699
|
+
}
|
|
1700
|
+
var SIGNAL_BY_NAME = {
|
|
1701
|
+
SIGINT: SIG.INT,
|
|
1702
|
+
SIGTERM: SIG.TERM,
|
|
1703
|
+
SIGKILL: SIG.KILL,
|
|
1704
|
+
SIGSTOP: SIG.STOP,
|
|
1705
|
+
SIGCONT: SIG.CONT
|
|
1706
|
+
};
|
|
1707
|
+
|
|
1708
|
+
// src/body-idle.ts
|
|
1709
|
+
var BODY_IDLE_MS = 5 * 60 * 1e3;
|
|
1710
|
+
|
|
1711
|
+
// src/transport.ts
|
|
1712
|
+
var MAX_REQUEST_BODY = 64 * 1024 * 1024;
|
|
1713
|
+
function serveTransport(peer, transport) {
|
|
1714
|
+
const open = /* @__PURE__ */ new Map();
|
|
1715
|
+
const fail = (nid, e) => {
|
|
1716
|
+
open.delete(nid);
|
|
1717
|
+
const { status, code } = e;
|
|
1718
|
+
peer.postMessage({
|
|
1719
|
+
net: "error",
|
|
1720
|
+
nid,
|
|
1721
|
+
message: e instanceof Error ? e.message : String(e),
|
|
1722
|
+
...typeof status === "number" ? { status } : {},
|
|
1723
|
+
...typeof code === "string" ? { code } : {}
|
|
1724
|
+
});
|
|
1725
|
+
};
|
|
1726
|
+
const start = async (call) => {
|
|
1727
|
+
const abort = new AbortController();
|
|
1728
|
+
const entry = { abort };
|
|
1729
|
+
open.set(call.nid, entry);
|
|
1730
|
+
try {
|
|
1731
|
+
const response = await transport.fetch({
|
|
1732
|
+
url: call.url,
|
|
1733
|
+
method: call.method,
|
|
1734
|
+
headers: call.headers,
|
|
1735
|
+
...call.body ? { body: call.body } : {},
|
|
1736
|
+
signal: abort.signal
|
|
1737
|
+
});
|
|
1738
|
+
if (!open.has(call.nid)) {
|
|
1739
|
+
await response.cancel();
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1742
|
+
open.set(call.nid, { abort, body: response.body[Symbol.asyncIterator](), response });
|
|
1743
|
+
peer.postMessage({
|
|
1744
|
+
net: "head",
|
|
1745
|
+
nid: call.nid,
|
|
1746
|
+
status: response.status,
|
|
1747
|
+
statusText: response.statusText,
|
|
1748
|
+
headers: response.headers
|
|
1749
|
+
});
|
|
1750
|
+
} catch (e) {
|
|
1751
|
+
fail(call.nid, e);
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
const read = async (nid) => {
|
|
1755
|
+
const body = open.get(nid)?.body;
|
|
1756
|
+
if (!body) return;
|
|
1757
|
+
try {
|
|
1758
|
+
const next = await body.next();
|
|
1759
|
+
if (next.done) {
|
|
1760
|
+
open.delete(nid);
|
|
1761
|
+
peer.postMessage({ net: "end", nid });
|
|
1762
|
+
} else {
|
|
1763
|
+
const bytes = next.value.slice();
|
|
1764
|
+
peer.postMessage({ net: "chunk", nid, bytes }, [bytes.buffer]);
|
|
1765
|
+
}
|
|
1766
|
+
} catch (e) {
|
|
1767
|
+
fail(nid, e);
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
const drop = (nid) => {
|
|
1771
|
+
const entry = open.get(nid);
|
|
1772
|
+
open.delete(nid);
|
|
1773
|
+
entry?.abort.abort();
|
|
1774
|
+
void entry?.response?.cancel().catch(() => void 0);
|
|
1775
|
+
};
|
|
1776
|
+
return {
|
|
1777
|
+
answer(call) {
|
|
1778
|
+
if (call.net === "fetch") void start(call);
|
|
1779
|
+
else if (call.net === "read") void read(call.nid);
|
|
1780
|
+
else drop(call.nid);
|
|
1781
|
+
},
|
|
1782
|
+
close() {
|
|
1783
|
+
for (const nid of [...open.keys()]) drop(nid);
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// src/client/serve-client.ts
|
|
1789
|
+
var MAX_LINKS2 = 40;
|
|
1790
|
+
function act(session, req, signal) {
|
|
1791
|
+
if (req.action === "write") session.write(req.bytes);
|
|
1792
|
+
else if (req.action === "resize") session.resize(req.cols, req.rows);
|
|
1793
|
+
else if (req.action === "signal") session.signal(signal(req.signal));
|
|
1794
|
+
else session.close();
|
|
1795
|
+
}
|
|
1796
|
+
async function realpath(fs, path) {
|
|
1797
|
+
let parts = fs.resolvePath("/", path).split("/").filter(Boolean);
|
|
1798
|
+
let resolved = "";
|
|
1799
|
+
for (let hops = 0; parts.length > 0; ) {
|
|
1800
|
+
const [name2, ...rest] = parts;
|
|
1801
|
+
const next = `${resolved}/${name2}`;
|
|
1802
|
+
if (!(await fs.lstat(next)).isSymbolicLink) {
|
|
1803
|
+
resolved = next;
|
|
1804
|
+
parts = rest;
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
if (++hops > MAX_LINKS2) throw fsError("ELOOP", path);
|
|
1808
|
+
const target = await fs.readlink(next);
|
|
1809
|
+
const base = target.startsWith("/") ? "/" : resolved || "/";
|
|
1810
|
+
parts = [...fs.resolvePath(base, target).split("/").filter(Boolean), ...rest];
|
|
1811
|
+
resolved = "";
|
|
1812
|
+
}
|
|
1813
|
+
return resolved || "/";
|
|
1814
|
+
}
|
|
1815
|
+
async function fsCall(fs, method, args) {
|
|
1816
|
+
const path = (i) => fs.resolvePath("/", String(args[i]));
|
|
1817
|
+
switch (method) {
|
|
1818
|
+
case "readFile": {
|
|
1819
|
+
const bytes = (await fs.readFileBuffer(path(0))).slice();
|
|
1820
|
+
return { result: bytes, transfer: [bytes.buffer] };
|
|
1821
|
+
}
|
|
1822
|
+
case "writeFile":
|
|
1823
|
+
await fs.writeFile(path(0), args[1]);
|
|
1824
|
+
return { result: true };
|
|
1825
|
+
case "stat":
|
|
1826
|
+
return { result: await fs.stat(path(0)) };
|
|
1827
|
+
case "lstat":
|
|
1828
|
+
return { result: await fs.lstat(path(0)) };
|
|
1829
|
+
case "readdir":
|
|
1830
|
+
return { result: await fs.readdir(path(0)) };
|
|
1831
|
+
case "mkdir":
|
|
1832
|
+
await fs.mkdir(path(0), { recursive: true });
|
|
1833
|
+
return { result: true };
|
|
1834
|
+
case "rm":
|
|
1835
|
+
await fs.rm(path(0), { recursive: true, force: args[1] === true });
|
|
1836
|
+
return { result: true };
|
|
1837
|
+
case "rename":
|
|
1838
|
+
await fs.rename(path(0), path(1));
|
|
1839
|
+
return { result: true };
|
|
1840
|
+
case "realpath":
|
|
1841
|
+
return { result: await realpath(fs, String(args[0])) };
|
|
1842
|
+
case "symlink":
|
|
1843
|
+
await fs.symlink(String(args[0]), path(1));
|
|
1844
|
+
return { result: true };
|
|
1845
|
+
case "readlink":
|
|
1846
|
+
return { result: await fs.readlink(path(0)) };
|
|
1847
|
+
default:
|
|
1848
|
+
return { result: await fs.exists(path(0)) };
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
function signalOf(host) {
|
|
1852
|
+
return host.signal ?? ((name2) => {
|
|
1853
|
+
throw fsError("EINVAL", `this kernel names no signal ${name2}`);
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
function dialFor(l, req, open) {
|
|
1857
|
+
const socket = dialSocket(l.net, req.port, req.host);
|
|
1858
|
+
const { port1, port2 } = new MessageChannel();
|
|
1859
|
+
open.set(
|
|
1860
|
+
req.id,
|
|
1861
|
+
serveSocket(socket, port1, () => open.delete(req.id))
|
|
1862
|
+
);
|
|
1863
|
+
return { result: port2, transfer: [port2] };
|
|
1864
|
+
}
|
|
1865
|
+
function killGroups(host, pgids) {
|
|
1866
|
+
if (pgids.length === 0) return;
|
|
1867
|
+
void host.launcher().then((launcher) => {
|
|
1868
|
+
for (const pgid of pgids) launcher.kill(-pgid, SIG.KILL);
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
function joinable(l, pgid, ours) {
|
|
1872
|
+
const sid = l.groupSession(pgid);
|
|
1873
|
+
if (sid === void 0) throw fsError("ESRCH", `no process group ${pgid}`);
|
|
1874
|
+
if (!ours(sid)) throw fsError("EPERM", `process group ${pgid} is in another session`);
|
|
1875
|
+
}
|
|
1876
|
+
function cdpRegistry(send, gone) {
|
|
1877
|
+
const registered = /* @__PURE__ */ new Map();
|
|
1878
|
+
const drop = (id) => {
|
|
1879
|
+
registered.get(id)?.();
|
|
1880
|
+
return registered.delete(id);
|
|
1881
|
+
};
|
|
1882
|
+
return {
|
|
1883
|
+
serve(l, req) {
|
|
1884
|
+
const hook = portCdpHook(
|
|
1885
|
+
(request2, cdp) => send({ cdpOpen: { ...request2, registration: req.id }, port: cdp }, [
|
|
1886
|
+
cdp
|
|
1887
|
+
]),
|
|
1888
|
+
gone
|
|
1889
|
+
);
|
|
1890
|
+
registered.set(req.id, l.cdp.register(hook, req.runtime));
|
|
1891
|
+
return req.id;
|
|
1892
|
+
},
|
|
1893
|
+
drop,
|
|
1894
|
+
close: () => {
|
|
1895
|
+
for (const id of [...registered.keys()]) drop(id);
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
function watchOp(l, req, watches, send) {
|
|
1900
|
+
if (req.op === "unwatch") {
|
|
1901
|
+
watches.get(req.watch)?.();
|
|
1902
|
+
watches.delete(req.watch);
|
|
1903
|
+
return { result: true };
|
|
1904
|
+
}
|
|
1905
|
+
const unwatch = l.watchers.watch(
|
|
1906
|
+
req.paths,
|
|
1907
|
+
{ recursive: req.recursive },
|
|
1908
|
+
(change) => send({ watch: req.id, change })
|
|
1909
|
+
);
|
|
1910
|
+
watches.set(req.id, unwatch);
|
|
1911
|
+
return { result: req.id };
|
|
1912
|
+
}
|
|
1913
|
+
function serveClient(port, host) {
|
|
1914
|
+
let state = "new";
|
|
1915
|
+
const terminals = /* @__PURE__ */ new Map();
|
|
1916
|
+
const groups = /* @__PURE__ */ new Set();
|
|
1917
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
1918
|
+
const watches = /* @__PURE__ */ new Map();
|
|
1919
|
+
let net;
|
|
1920
|
+
const ended = Promise.withResolvers();
|
|
1921
|
+
const send = (message2, transfer = []) => {
|
|
1922
|
+
if (state !== "closed") port.postMessage(message2, transfer);
|
|
1923
|
+
};
|
|
1924
|
+
const reply = (id, body, transfer) => send({ id, ...body }, transfer);
|
|
1925
|
+
const cdps = cdpRegistry(send, ended.promise);
|
|
1926
|
+
function detach(kill = false, bye = "the slicc-kernel detached this client") {
|
|
1927
|
+
if (state === "closed") return;
|
|
1928
|
+
send({ bye });
|
|
1929
|
+
state = "closed";
|
|
1930
|
+
net?.close();
|
|
1931
|
+
for (const unwatch of watches.values()) unwatch();
|
|
1932
|
+
watches.clear();
|
|
1933
|
+
cdps.close();
|
|
1934
|
+
for (const session of terminals.values()) session.close();
|
|
1935
|
+
terminals.clear();
|
|
1936
|
+
if (kill) killGroups(host, [...groups]);
|
|
1937
|
+
groups.clear();
|
|
1938
|
+
port.close?.();
|
|
1939
|
+
ended.resolve();
|
|
1940
|
+
}
|
|
1941
|
+
async function hello(message2) {
|
|
1942
|
+
const refused = versionError(message2.protocol);
|
|
1943
|
+
if (refused) {
|
|
1944
|
+
send({ hello: { protocol: PROTOCOL, error: refused } });
|
|
1945
|
+
detach();
|
|
1946
|
+
return;
|
|
1860
1947
|
}
|
|
1861
|
-
const
|
|
1862
|
-
const
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1948
|
+
const launcher = await host.launcher();
|
|
1949
|
+
const transport = host.transport?.() ?? launcher.transport;
|
|
1950
|
+
net = serveTransport({ postMessage: (m, t) => send(m, t) }, transport);
|
|
1951
|
+
state = "open";
|
|
1952
|
+
if (message2.lock && host.locks) void host.locks.request(message2.lock, () => detach());
|
|
1953
|
+
send({
|
|
1954
|
+
hello: {
|
|
1955
|
+
protocol: PROTOCOL,
|
|
1956
|
+
traits: transport.traits,
|
|
1957
|
+
...host.lock ? { lock: host.lock } : {}
|
|
1958
|
+
}
|
|
1867
1959
|
});
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1960
|
+
}
|
|
1961
|
+
async function terminal(req, l) {
|
|
1962
|
+
const session = await l.openTerminal(req.argv, {
|
|
1963
|
+
...req.options,
|
|
1964
|
+
onData: (bytes) => reply(req.id, { fd: 1, bytes: bytes.slice() })
|
|
1965
|
+
});
|
|
1966
|
+
if (state === "closed") {
|
|
1967
|
+
session.close();
|
|
1968
|
+
} else {
|
|
1969
|
+
terminals.set(req.id, session);
|
|
1970
|
+
sessions.add(session.pid);
|
|
1971
|
+
reply(req.id, { started: session.pid });
|
|
1872
1972
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
this.forget(destination);
|
|
1878
|
-
const timed = await this.explicitTimes(source.path);
|
|
1879
|
-
const moved2 = await this.place(source.handle, dir, staging);
|
|
1880
|
-
if (swap) await this.rm(destination, { recursive: true });
|
|
1881
|
-
await this.meta.move(source.path, destination, carry);
|
|
1882
|
-
if (!moved2) await this.rm(source.path, { recursive: true });
|
|
1883
|
-
let copied = !moved2;
|
|
1884
|
-
if (swap) {
|
|
1885
|
-
const staged = await dir.getDirectoryHandle(staging);
|
|
1886
|
-
const placed = await this.place(staged, dir, name2);
|
|
1887
|
-
if (!placed) await dir.removeEntry(staging, { recursive: true });
|
|
1888
|
-
copied ||= !placed;
|
|
1973
|
+
try {
|
|
1974
|
+
return { result: await session.exited };
|
|
1975
|
+
} finally {
|
|
1976
|
+
terminals.delete(req.id);
|
|
1889
1977
|
}
|
|
1890
|
-
if (copied) await this.restamp(destination, timed);
|
|
1891
|
-
this.changed(source.path, destination);
|
|
1892
|
-
}
|
|
1893
|
-
async modified(path) {
|
|
1894
|
-
const handle = await this.handle(path);
|
|
1895
|
-
return handle.kind === "file" ? (await handle.getFile()).lastModified : 0;
|
|
1896
1978
|
}
|
|
1897
|
-
async
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1979
|
+
async function spawn(req, l) {
|
|
1980
|
+
const out = (fd) => (bytes) => reply(req.id, { fd, bytes: bytes.slice() });
|
|
1981
|
+
if (req.options.pgid !== void 0) joinable(l, req.options.pgid, (sid) => sessions.has(sid));
|
|
1982
|
+
let leader;
|
|
1983
|
+
const result = await l.run(req.argv, {
|
|
1984
|
+
...req.options,
|
|
1985
|
+
collect: false,
|
|
1986
|
+
onStdout: out(1),
|
|
1987
|
+
onStderr: out(2),
|
|
1988
|
+
onStarted: (pid) => {
|
|
1989
|
+
leader = pid;
|
|
1990
|
+
if (state === "closed") return;
|
|
1991
|
+
groups.add(req.options.pgid ?? pid);
|
|
1992
|
+
if (req.options.pgid === void 0) sessions.add(pid);
|
|
1993
|
+
reply(req.id, { started: pid });
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
if (leader === void 0) throw fsError("ENOENT", `${req.argv[0] ?? ""}: command not found`);
|
|
1997
|
+
if (!l.list().some((p) => p.pgid === leader)) groups.delete(leader);
|
|
1998
|
+
return { result: result.status };
|
|
1905
1999
|
}
|
|
1906
|
-
async
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
await this.meta.update(path + rest, (entry) => entry && { ...entry, mtimeFor });
|
|
2000
|
+
async function handle(req) {
|
|
2001
|
+
if (host.scope === "transport" && req.op !== "detach") {
|
|
2002
|
+
throw fsError("EPERM", `${req.op}: this client may only use the network transport`);
|
|
1910
2003
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
2004
|
+
const l = await host.launcher();
|
|
2005
|
+
switch (req.op) {
|
|
2006
|
+
case "spawn":
|
|
2007
|
+
return spawn(req, l);
|
|
2008
|
+
case "open-terminal":
|
|
2009
|
+
return terminal(req, l);
|
|
2010
|
+
case "terminal": {
|
|
2011
|
+
const session = terminals.get(req.terminal);
|
|
2012
|
+
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
2013
|
+
act(session, req, signalOf(host));
|
|
2014
|
+
return { result: true };
|
|
2015
|
+
}
|
|
2016
|
+
case "kill":
|
|
2017
|
+
if (!l.kill(req.pid, signalOf(host)(req.signal))) throw fsError("ESRCH", `pid ${req.pid}`);
|
|
2018
|
+
return { result: true };
|
|
2019
|
+
case "ps":
|
|
2020
|
+
return {
|
|
2021
|
+
result: l.list().map(({ tid: _, ...entry }) => entry)
|
|
2022
|
+
};
|
|
2023
|
+
case "fs":
|
|
2024
|
+
if (!FS_METHODS.includes(req.method)) {
|
|
2025
|
+
throw new Error(`unknown file system call ${req.method}`);
|
|
2026
|
+
}
|
|
2027
|
+
return fsCall(l.fs, req.method, req.args);
|
|
2028
|
+
case "watch":
|
|
2029
|
+
case "unwatch":
|
|
2030
|
+
return watchOp(l, req, watches, send);
|
|
2031
|
+
case "mount":
|
|
2032
|
+
return { result: await l.mount(req.spec) };
|
|
2033
|
+
case "umount":
|
|
2034
|
+
l.umount(req.target);
|
|
2035
|
+
return { result: true };
|
|
2036
|
+
case "mounts":
|
|
2037
|
+
return { result: l.mounts.list() };
|
|
2038
|
+
case "dial":
|
|
2039
|
+
return dialFor(l, req, watches);
|
|
2040
|
+
case "serve-cdp":
|
|
2041
|
+
return { result: cdps.serve(l, req) };
|
|
2042
|
+
case "unserve-cdp":
|
|
2043
|
+
return { result: cdps.drop(req.registration) };
|
|
2044
|
+
case "detach":
|
|
2045
|
+
return { result: true };
|
|
2046
|
+
default:
|
|
2047
|
+
throw new Error(`unknown op ${req.op}`);
|
|
1923
2048
|
}
|
|
1924
|
-
return false;
|
|
1925
2049
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
2050
|
+
port.addEventListener("message", (event) => {
|
|
2051
|
+
const data = event.data;
|
|
2052
|
+
if (state === "closed") return;
|
|
2053
|
+
if (data.hello) {
|
|
2054
|
+
if (state === "new") {
|
|
2055
|
+
hello(data.hello).catch((err) => {
|
|
2056
|
+
send({ hello: { protocol: PROTOCOL, error: String(err?.message ?? err) } });
|
|
2057
|
+
detach();
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
1931
2060
|
return;
|
|
1932
2061
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
const parent = await this.locate(parentOf2(path), true);
|
|
1938
|
-
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
1939
|
-
const at = resolvePath(parent.path, baseOf(path));
|
|
1940
|
-
if (await this.locate(at, false).then(
|
|
1941
|
-
() => true,
|
|
1942
|
-
(err) => !missing(err)
|
|
1943
|
-
)) {
|
|
1944
|
-
throw fsError("EEXIST", path);
|
|
1945
|
-
}
|
|
1946
|
-
const now = Date.now();
|
|
1947
|
-
await this.meta.update(at, () => ({
|
|
1948
|
-
path: at,
|
|
1949
|
-
link: target,
|
|
1950
|
-
dir: parent.path,
|
|
1951
|
-
ino: inodeOf(at),
|
|
1952
|
-
mtimeMs: now,
|
|
1953
|
-
ctimeMs: now
|
|
1954
|
-
}));
|
|
1955
|
-
}
|
|
1956
|
-
async readlink(path) {
|
|
1957
|
-
const found = await this.locate(path, false);
|
|
1958
|
-
if (found.link) return found.link.link;
|
|
1959
|
-
throw fsError("EINVAL", path);
|
|
1960
|
-
}
|
|
1961
|
-
async chmod(path, mode) {
|
|
1962
|
-
const found = await this.locate(path, true);
|
|
1963
|
-
const ctimeMs = Date.now();
|
|
1964
|
-
await this.meta.update(found.path, (entry) => ({
|
|
1965
|
-
...entry,
|
|
1966
|
-
path: found.path,
|
|
1967
|
-
mode: mode & PERM_MASK,
|
|
1968
|
-
ctimeMs
|
|
1969
|
-
}));
|
|
1970
|
-
}
|
|
1971
|
-
async utimes(path, atime, mtime) {
|
|
1972
|
-
const found = await this.locate(path, true);
|
|
1973
|
-
const handle = found.handle;
|
|
1974
|
-
const file = handle.kind === "file" ? await handle.getFile() : void 0;
|
|
1975
|
-
const ctimeMs = Date.now();
|
|
1976
|
-
await this.meta.update(found.path, (entry) => ({
|
|
1977
|
-
...entry,
|
|
1978
|
-
path: found.path,
|
|
1979
|
-
atimeMs: atime.getTime(),
|
|
1980
|
-
mtimeMs: mtime.getTime(),
|
|
1981
|
-
mtimeFor: file?.lastModified ?? 0,
|
|
1982
|
-
ctimeMs
|
|
1983
|
-
}));
|
|
1984
|
-
}
|
|
1985
|
-
async reconcile() {
|
|
1986
|
-
let removed = 0;
|
|
1987
|
-
for (const entry of await this.meta.all()) {
|
|
1988
|
-
const real = await this.real(entry.path);
|
|
1989
|
-
const stale = entry.link === void 0 ? !real : real || !await this.real(parentOf2(entry.path));
|
|
1990
|
-
if (!stale) continue;
|
|
1991
|
-
await this.meta.update(entry.path, () => void 0);
|
|
1992
|
-
removed++;
|
|
2062
|
+
if (state !== "open") return;
|
|
2063
|
+
if (data.net !== void 0) {
|
|
2064
|
+
net?.answer(data);
|
|
2065
|
+
return;
|
|
1993
2066
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
}
|
|
2067
|
+
const req = data;
|
|
2068
|
+
handle(req).then(
|
|
2069
|
+
({ result, transfer }) => {
|
|
2070
|
+
reply(req.id, { result }, transfer);
|
|
2071
|
+
if (req.op === "detach") detach(req.kill === true);
|
|
2072
|
+
},
|
|
2073
|
+
(err) => {
|
|
2074
|
+
const code = err?.code;
|
|
2075
|
+
reply(req.id, {
|
|
2076
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2077
|
+
...typeof code === "string" ? { code } : {}
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
);
|
|
2081
|
+
});
|
|
2082
|
+
port.addEventListener("close", () => detach());
|
|
2083
|
+
port.start?.();
|
|
2084
|
+
return { detach, closed: ended.promise };
|
|
2085
|
+
}
|
|
1997
2086
|
|
|
1998
2087
|
// src/kernel/net/loopback-names.ts
|
|
1999
2088
|
var HOST_LOOPBACK = "host.slicc.internal";
|
|
@@ -4985,6 +5074,7 @@ var MountFs = class {
|
|
|
4985
5074
|
async pread(path, offset, length2) {
|
|
4986
5075
|
const real = await this.follow(path);
|
|
4987
5076
|
const found = this.at(real);
|
|
5077
|
+
if (!found && this.base.pread) return this.base.pread(real, offset, length2);
|
|
4988
5078
|
if (!found) return (await this.base.readFileBuffer(real)).slice(offset, offset + length2);
|
|
4989
5079
|
const { mount, rel: rel2 } = found;
|
|
4990
5080
|
if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
|
|
@@ -5022,6 +5112,7 @@ var MountFs = class {
|
|
|
5022
5112
|
async pwrite(path, offset, bytes, transfer = false) {
|
|
5023
5113
|
const real = await this.follow(path, 0, true);
|
|
5024
5114
|
const found = this.at(real);
|
|
5115
|
+
if (!found && this.base.pwrite) return this.base.pwrite(real, offset, bytes);
|
|
5025
5116
|
if (!found)
|
|
5026
5117
|
return this.base.writeFile(
|
|
5027
5118
|
real,
|
|
@@ -5053,6 +5144,7 @@ var MountFs = class {
|
|
|
5053
5144
|
async truncate(path, size) {
|
|
5054
5145
|
const real = await this.follow(path);
|
|
5055
5146
|
const found = this.at(real);
|
|
5147
|
+
if (!found && this.base.truncate) return this.base.truncate(real, size);
|
|
5056
5148
|
if (!found?.mount.caps.ranges) {
|
|
5057
5149
|
return this.writeFile(real, resized(await this.readFileBuffer(real), size));
|
|
5058
5150
|
}
|
|
@@ -9680,6 +9772,11 @@ var Launcher = class {
|
|
|
9680
9772
|
};
|
|
9681
9773
|
|
|
9682
9774
|
// src/serve.ts
|
|
9775
|
+
async function inOpfs(deps, root) {
|
|
9776
|
+
if (deps.syncAccess !== true) return false;
|
|
9777
|
+
const opfs = await deps.storage().catch(() => void 0);
|
|
9778
|
+
return await opfs?.resolve(root).catch(() => null) != null;
|
|
9779
|
+
}
|
|
9683
9780
|
function signalNumber(name2) {
|
|
9684
9781
|
const key = name2.slice(3);
|
|
9685
9782
|
if (!name2.startsWith("SIG") || !Object.hasOwn(SIG, key))
|
|
@@ -9780,7 +9877,8 @@ function serveKernel(port, deps) {
|
|
|
9780
9877
|
const name2 = req.metadata ?? META_DB;
|
|
9781
9878
|
const { meta, media } = await stores(deps, name2, req.media);
|
|
9782
9879
|
remote = req.transport ? new RemoteTransport(port, req.transport) : void 0;
|
|
9783
|
-
const
|
|
9880
|
+
const ranged2 = await inOpfs(deps, root);
|
|
9881
|
+
const fs = new OpfsFs(root, meta, dirsChannel(name2), { ranged: ranged2 });
|
|
9784
9882
|
await fs.reconcile();
|
|
9785
9883
|
const started = new Launcher({
|
|
9786
9884
|
fs,
|
|
@@ -9878,6 +9976,7 @@ var processWorker = pinScript(new URL("./process-worker.js", import.meta.url));
|
|
|
9878
9976
|
var driverWorker = pinScript(new URL("./driver-worker.js", import.meta.url));
|
|
9879
9977
|
serveKernel(globalThis, {
|
|
9880
9978
|
storage: () => navigator.storage.getDirectory(),
|
|
9979
|
+
syncAccess: syncAccessHandles(),
|
|
9881
9980
|
createWorker: () => new Worker(processWorker, { type: "module" }),
|
|
9882
9981
|
createDriverWorker: () => new Worker(driverWorker, { type: "module" }),
|
|
9883
9982
|
metadata: (name2) => openMeta(name2, indexedDB),
|