@ai-ecoverse/slicc-kernel 1.15.1 → 1.16.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/dist/node.js CHANGED
@@ -1067,9 +1067,9 @@ var OpfsFs = class {
1067
1067
  var NAME = /^[A-Za-z0-9._+-]+$/;
1068
1068
  var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
1069
1069
  var PACKAGE = /\$\{package\}/g;
1070
- function inside(pkg, rel) {
1071
- if (typeof rel !== "string" || rel === "") return void 0;
1072
- const clean = rel.replace(/^\.\//, "");
1070
+ function inside(pkg, rel2) {
1071
+ if (typeof rel2 !== "string" || rel2 === "") return void 0;
1072
+ const clean = rel2.replace(/^\.\//, "");
1073
1073
  if (clean.startsWith("/") || clean.split("/").includes("..")) return void 0;
1074
1074
  return `${pkg}/${clean}`;
1075
1075
  }
@@ -5911,6 +5911,57 @@ function serveFilesystem(port, handlers, capabilities = {}) {
5911
5911
  return { invalidate: (paths) => reply({ invalidate: paths }) };
5912
5912
  }
5913
5913
 
5914
+ // src/mount/medium.ts
5915
+ var EMPTY_ROOT = { kind: "directory", size: 0, mtime: 0, mode: 16877 };
5916
+ var absent = () => fsError2("ENOMEDIUM", "No medium found");
5917
+ function mediumSlot(lost, onLost) {
5918
+ let current;
5919
+ function wrap(pick) {
5920
+ return async (...args) => {
5921
+ const medium = current;
5922
+ if (!medium) throw absent();
5923
+ try {
5924
+ return await pick(medium.handlers)(...args);
5925
+ } catch (err) {
5926
+ if (!await lost(err, medium.key)) throw err;
5927
+ if (current === medium) {
5928
+ current = void 0;
5929
+ onLost(medium.key);
5930
+ }
5931
+ throw absent();
5932
+ }
5933
+ };
5934
+ }
5935
+ const getattr = wrap((h) => h.getattr);
5936
+ const readdir = wrap((h) => h.readdir);
5937
+ const statfs = wrap((h) => async () => await h.statfs?.() ?? null);
5938
+ return {
5939
+ handlers: {
5940
+ getattr: (path) => current || path !== "/" ? getattr(path) : Promise.resolve(EMPTY_ROOT),
5941
+ readdir: (path) => current || path !== "/" ? readdir(path) : Promise.resolve([]),
5942
+ open: wrap((h) => h.open),
5943
+ read: wrap((h) => h.read),
5944
+ write: wrap((h) => h.write),
5945
+ release: wrap((h) => h.release),
5946
+ mkdir: wrap((h) => h.mkdir),
5947
+ rmdir: wrap((h) => h.rmdir),
5948
+ unlink: wrap((h) => h.unlink),
5949
+ rename: wrap((h) => h.rename),
5950
+ symlink: wrap((h) => h.symlink),
5951
+ readlink: wrap((h) => h.readlink),
5952
+ setattr: wrap((h) => h.setattr),
5953
+ statfs: () => current ? statfs() : Promise.resolve(null)
5954
+ },
5955
+ present: () => current !== void 0,
5956
+ insert(key, handlers) {
5957
+ current = { key, handlers };
5958
+ },
5959
+ eject() {
5960
+ current = void 0;
5961
+ }
5962
+ };
5963
+ }
5964
+
5914
5965
  // src/mount/fsa.ts
5915
5966
  var FSA_CAPABILITIES = { listingStats: true };
5916
5967
  var codeOf = (err) => err?.code;
@@ -5998,7 +6049,6 @@ function kernelFsHandlers(fs) {
5998
6049
  }
5999
6050
  };
6000
6051
  }
6001
- var EMPTY_ROOT = { kind: "directory", size: 0, mtime: 0, mode: 16877 };
6002
6052
  function granted(handle) {
6003
6053
  if (!handle.queryPermission) return Promise.resolve(true);
6004
6054
  return handle.queryPermission({ mode: "readwrite" }).then(
@@ -6007,48 +6057,345 @@ function granted(handle) {
6007
6057
  );
6008
6058
  }
6009
6059
  function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
6010
- let current;
6011
- const absent = () => fsError2("ENOMEDIUM", "No medium found");
6012
- function wrap(pick) {
6013
- return async (...args) => {
6014
- const medium = current;
6015
- if (!medium) throw absent();
6016
- try {
6017
- return await pick(medium.handlers)(...args);
6018
- } catch (err) {
6019
- if (!denied(err) || await granted(medium.handle)) throw err;
6020
- if (current === medium) {
6021
- current = void 0;
6022
- onLost(medium.handle);
6060
+ const slot = mediumSlot(
6061
+ async (err, handle) => denied(err) && !await granted(handle),
6062
+ onLost
6063
+ );
6064
+ return {
6065
+ handlers: slot.handlers,
6066
+ present: slot.present,
6067
+ insert: (handle) => slot.insert(handle, kernelFsHandlers(open(handle))),
6068
+ eject: slot.eject
6069
+ };
6070
+ }
6071
+
6072
+ // src/mount/hostfs.ts
6073
+ var HOSTFS_IO = 16 * 1024 * 1024;
6074
+ var codeOf2 = (err) => err?.code;
6075
+ var unreachable = (err) => codeOf2(err) === "ENOTCONN";
6076
+ function rel(path) {
6077
+ return path.replace(/^\/+/, "");
6078
+ }
6079
+ var HostfsClient = class {
6080
+ grant;
6081
+ regrant;
6082
+ fetch;
6083
+ constructor(grant, regrant, fetch2) {
6084
+ this.grant = grant;
6085
+ this.regrant = regrant;
6086
+ this.fetch = fetch2;
6087
+ }
6088
+ get capabilities() {
6089
+ return this.grant.capabilities ?? {};
6090
+ }
6091
+ get token() {
6092
+ return this.grant.token;
6093
+ }
6094
+ get maxIo() {
6095
+ return this.capabilities.maxIo ?? HOSTFS_IO;
6096
+ }
6097
+ async send(path, init, handle = false, retried = false) {
6098
+ let response;
6099
+ try {
6100
+ response = await this.fetch(`${this.grant.url}${path}`, {
6101
+ ...init,
6102
+ credentials: "omit",
6103
+ headers: {
6104
+ ...init.headers,
6105
+ "X-Hostfs-Token": this.grant.token
6023
6106
  }
6024
- throw absent();
6107
+ });
6108
+ } catch (err) {
6109
+ throw fsError2("ENOTCONN", `the host proxy is unreachable: ${err.message}`);
6110
+ }
6111
+ if (response.ok) return response;
6112
+ if (response.headers.get("X-Proxy-Error")) {
6113
+ await response.body?.cancel();
6114
+ if (retried || response.status !== 403) {
6115
+ throw fsError2("EACCES", `the host proxy refused this folder (${response.status})`);
6025
6116
  }
6026
- };
6117
+ this.grant = await this.regrant();
6118
+ if (handle)
6119
+ throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
6120
+ return this.send(path, init, false, true);
6121
+ }
6122
+ const errno = response.headers.get("X-Hostfs-Errno") ?? "EIO";
6123
+ const body = await response.json().catch(() => ({}));
6124
+ throw fsError2(errno, typeof body.message === "string" ? body.message : errno);
6125
+ }
6126
+ async call(op, body = {}, handle = false) {
6127
+ const response = await this.send(
6128
+ "/api/hostfs",
6129
+ {
6130
+ method: "POST",
6131
+ headers: { "Content-Type": "application/json" },
6132
+ body: JSON.stringify({ op, ...body })
6133
+ },
6134
+ handle
6135
+ );
6136
+ return await response.json();
6137
+ }
6138
+ async read(fh, offset, size, ifMatch) {
6139
+ const response = await this.send(
6140
+ "/api/hostfs",
6141
+ {
6142
+ method: "POST",
6143
+ headers: { "Content-Type": "application/json" },
6144
+ body: JSON.stringify({ op: "read", fh, offset, size, ...ifMatch ? { ifMatch } : {} })
6145
+ },
6146
+ true
6147
+ );
6148
+ return new Uint8Array(await response.arrayBuffer());
6149
+ }
6150
+ async write(fh, offset, bytes) {
6151
+ const response = await this.send(
6152
+ "/api/hostfs/write",
6153
+ {
6154
+ method: "PUT",
6155
+ headers: {
6156
+ "Content-Type": "application/octet-stream",
6157
+ "X-Hostfs-Request": JSON.stringify({ fh, offset })
6158
+ },
6159
+ body: bytes
6160
+ },
6161
+ true
6162
+ );
6163
+ await response.body?.cancel();
6164
+ }
6165
+ async watch(signal, onEvent, onOpen = () => void 0) {
6166
+ const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
6167
+ onOpen();
6168
+ const reader = response.body.getReader();
6169
+ const decoder2 = new TextDecoder();
6170
+ let buffered = "";
6171
+ try {
6172
+ for (; ; ) {
6173
+ const { done: done4, value } = await reader.read();
6174
+ if (done4) return;
6175
+ buffered += decoder2.decode(value, { stream: true });
6176
+ let end = buffered.indexOf("\n");
6177
+ while (end >= 0) {
6178
+ const line = buffered.slice(0, end).trim();
6179
+ buffered = buffered.slice(end + 1);
6180
+ if (line) onEvent(JSON.parse(line));
6181
+ end = buffered.indexOf("\n");
6182
+ }
6183
+ }
6184
+ } catch (err) {
6185
+ if (signal.aborted) return;
6186
+ throw fsError2("ENOTCONN", `the host proxy's watch broke: ${err.message}`);
6187
+ } finally {
6188
+ reader.releaseLock();
6189
+ }
6190
+ }
6191
+ };
6192
+ function hostfsHandlers(current) {
6193
+ const handles = /* @__PURE__ */ new Map();
6194
+ let nextFh = 0;
6195
+ const fresh = (remote) => {
6196
+ if (remote.client.token === remote.token) return remote;
6197
+ throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
6198
+ };
6199
+ const live = (fh) => {
6200
+ const remote = handles.get(fh);
6201
+ if (!remote) throw fsError2("EBADF", `handle ${fh}`);
6202
+ return fresh(remote);
6203
+ };
6204
+ const call = (op, body = {}) => current().call(op, body);
6205
+ async function writable(client, path, flags) {
6206
+ const opening = () => client.call("open", { path: rel(path), ...flags });
6207
+ try {
6208
+ return { opened: await opening(), restore: void 0 };
6209
+ } catch (err) {
6210
+ if (!flags.write || codeOf2(err) !== "EACCES") throw err;
6211
+ const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
6212
+ if (mode & 128) throw err;
6213
+ const restore = { path: rel(path), mode };
6214
+ await client.call("setattr", { path: rel(path), mode: mode | 128 });
6215
+ const opened2 = await opening().catch(async (again) => {
6216
+ await client.call("setattr", restore);
6217
+ throw again;
6218
+ });
6219
+ return { opened: opened2, restore };
6220
+ }
6027
6221
  }
6028
- const getattr = wrap((h) => h.getattr);
6029
- const readdir = wrap((h) => h.readdir);
6030
6222
  return {
6031
- handlers: {
6032
- getattr: (path) => current || path !== "/" ? getattr(path) : Promise.resolve(EMPTY_ROOT),
6033
- readdir: (path) => current || path !== "/" ? readdir(path) : Promise.resolve([]),
6034
- open: wrap((h) => h.open),
6035
- read: wrap((h) => h.read),
6036
- write: wrap((h) => h.write),
6037
- release: wrap((h) => h.release),
6038
- mkdir: wrap((h) => h.mkdir),
6039
- rmdir: wrap((h) => h.rmdir),
6040
- unlink: wrap((h) => h.unlink),
6041
- rename: wrap((h) => h.rename),
6042
- symlink: wrap((h) => h.symlink),
6043
- readlink: wrap((h) => h.readlink),
6044
- setattr: wrap((h) => h.setattr)
6223
+ getattr: async (path) => await call("stat", { path: rel(path) }),
6224
+ async readdir(path) {
6225
+ const { entries } = await call(
6226
+ "list",
6227
+ { path: rel(path) }
6228
+ );
6229
+ return entries.map(({ name: name2, attr }) => ({ name: name2, kind: attr.kind, attr }));
6045
6230
  },
6046
- present: () => current !== void 0,
6047
- insert(handle) {
6048
- current = { handle, handlers: kernelFsHandlers(open(handle)) };
6231
+ async open(path, flags) {
6232
+ const client = current();
6233
+ const token = client.token;
6234
+ const { opened: opened2, restore } = await writable(client, path, flags);
6235
+ handles.set(++nextFh, {
6236
+ client,
6237
+ token,
6238
+ fh: opened2.fh,
6239
+ etag: flags.write ? void 0 : opened2.attr?.etag,
6240
+ restore
6241
+ });
6242
+ return nextFh;
6049
6243
  },
6050
- eject() {
6051
- current = void 0;
6244
+ async read(fh, offset, size) {
6245
+ const remote = live(fh);
6246
+ return remote.client.read(
6247
+ remote.fh,
6248
+ offset,
6249
+ Math.min(size, remote.client.maxIo),
6250
+ remote.etag
6251
+ );
6252
+ },
6253
+ async write(fh, offset, bytes) {
6254
+ const remote = live(fh);
6255
+ const most = remote.client.maxIo;
6256
+ for (let at = 0; at < bytes.length; at += most) {
6257
+ await remote.client.write(remote.fh, offset + at, bytes.subarray(at, at + most));
6258
+ }
6259
+ },
6260
+ async release(fh) {
6261
+ const remote = handles.get(fh);
6262
+ if (!remote) throw fsError2("EBADF", `handle ${fh}`);
6263
+ handles.delete(fh);
6264
+ try {
6265
+ await fresh(remote).client.call("release", { fh: remote.fh }, true);
6266
+ } finally {
6267
+ if (remote.restore) await current().call("setattr", remote.restore);
6268
+ }
6269
+ },
6270
+ mkdir: async (path) => void await call("mkdir", { path: rel(path) }),
6271
+ rmdir: async (path) => void await call("rmdir", { path: rel(path) }),
6272
+ unlink: async (path) => void await call("unlink", { path: rel(path) }),
6273
+ rename: async (from, to) => void await call("rename", { from: rel(from), to: rel(to) }),
6274
+ symlink: async (target, path) => void await call("symlink", { target, path: rel(path) }),
6275
+ readlink: async (path) => (await call("readlink", { path: rel(path) })).target,
6276
+ setattr: async (path, change) => void await call("setattr", { path: rel(path), ...change }),
6277
+ statfs: () => call("statfs")
6278
+ };
6279
+ }
6280
+ function sleep(ms, signal) {
6281
+ return new Promise((resolve) => {
6282
+ const done4 = () => {
6283
+ clearTimeout(timer);
6284
+ signal.removeEventListener("abort", done4);
6285
+ resolve();
6286
+ };
6287
+ const timer = setTimeout(done4, ms);
6288
+ signal.addEventListener("abort", done4);
6289
+ });
6290
+ }
6291
+ function keepWatching(loop, signal) {
6292
+ const silenceMs = loop.silenceMs ?? 45e3;
6293
+ const [first, most] = loop.backoffMs ?? [1e3, 3e4];
6294
+ const ready2 = Promise.withResolvers();
6295
+ let wait = first;
6296
+ let up = false;
6297
+ let current;
6298
+ async function session() {
6299
+ const session2 = new AbortController();
6300
+ current = session2;
6301
+ const stop = () => session2.abort();
6302
+ signal.addEventListener("abort", stop);
6303
+ let timer;
6304
+ const quiet = () => {
6305
+ clearTimeout(timer);
6306
+ timer = setTimeout(stop, silenceMs);
6307
+ };
6308
+ let opened2 = false;
6309
+ try {
6310
+ const client = await loop.connect();
6311
+ await client.watch(
6312
+ session2.signal,
6313
+ (event) => {
6314
+ quiet();
6315
+ if (!event.ping) loop.event(event);
6316
+ },
6317
+ () => {
6318
+ quiet();
6319
+ opened2 = true;
6320
+ up = true;
6321
+ loop.up(client);
6322
+ ready2.resolve();
6323
+ }
6324
+ );
6325
+ return opened2;
6326
+ } catch {
6327
+ return opened2;
6328
+ } finally {
6329
+ clearTimeout(timer);
6330
+ signal.removeEventListener("abort", stop);
6331
+ }
6332
+ }
6333
+ void (async () => {
6334
+ while (!signal.aborted) {
6335
+ const began = Date.now();
6336
+ const opened2 = await session();
6337
+ if (signal.aborted) return;
6338
+ if (opened2 && Date.now() - began >= first) {
6339
+ wait = first;
6340
+ continue;
6341
+ }
6342
+ if (!opened2 && up) {
6343
+ up = false;
6344
+ loop.down();
6345
+ }
6346
+ ready2.resolve();
6347
+ await sleep(wait, signal);
6348
+ wait = Math.min(wait * 2, most);
6349
+ }
6350
+ })();
6351
+ return { ready: ready2.promise, kick: () => current?.abort() };
6352
+ }
6353
+ function invalidation(event) {
6354
+ return event.paths ?? true;
6355
+ }
6356
+ async function openHostfs(spec, hook, fetch2, timing = {}) {
6357
+ const readonly = spec.options?.ro !== void 0;
6358
+ const { port1, port2 } = new MessageChannel();
6359
+ const slot = mediumSlot(
6360
+ async (err) => unreachable(err),
6361
+ () => watching.kick()
6362
+ );
6363
+ const stop = new AbortController();
6364
+ const grant = () => hook(spec.source, { readonly });
6365
+ let active;
6366
+ const handlers = hostfsHandlers(() => active);
6367
+ const served = serveFilesystem(port2, slot.handlers, {
6368
+ symlinks: true,
6369
+ chmod: true,
6370
+ listingStats: true,
6371
+ maxIo: HOSTFS_IO,
6372
+ ...readonly ? { readonly: true } : {}
6373
+ });
6374
+ const watching = keepWatching(
6375
+ {
6376
+ connect: async () => new HostfsClient(await grant(), grant, fetch2),
6377
+ up(client) {
6378
+ active = client;
6379
+ slot.insert(client, handlers);
6380
+ served.invalidate(true);
6381
+ },
6382
+ event: (event) => served.invalidate(invalidation(event)),
6383
+ down() {
6384
+ slot.eject();
6385
+ served.invalidate(true);
6386
+ },
6387
+ ...timing
6388
+ },
6389
+ stop.signal
6390
+ );
6391
+ await watching.ready;
6392
+ return {
6393
+ port: port1,
6394
+ present: slot.present,
6395
+ dispose() {
6396
+ stop.abort();
6397
+ port1.close();
6398
+ port2.close();
6052
6399
  }
6053
6400
  };
6054
6401
  }
@@ -6157,6 +6504,7 @@ var DriverConnection = class _DriverConnection {
6157
6504
 
6158
6505
  // src/mount/mount-fs.ts
6159
6506
  var MAX_LINKS4 = 40;
6507
+ var STALE_RETRIES = 3;
6160
6508
  var DEFAULT_IO = 8 * 1024 * 1024;
6161
6509
  var UNITS = { "": 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3, t: 1024 ** 4 };
6162
6510
  function parseSize(text) {
@@ -6223,8 +6571,8 @@ var MountTable = class {
6223
6571
  best = mount;
6224
6572
  }
6225
6573
  if (!best) return void 0;
6226
- const rel = best.target === "/" ? path : path.slice(best.target.length);
6227
- return { mount: best, rel: rel || "/" };
6574
+ const rel2 = best.target === "/" ? path : path.slice(best.target.length);
6575
+ return { mount: best, rel: rel2 || "/" };
6228
6576
  }
6229
6577
  async mount(spec, fs) {
6230
6578
  const target = normalizePath(spec.target);
@@ -6329,20 +6677,20 @@ var MountTable = class {
6329
6677
  mount.lists.delete(parentOf4(path));
6330
6678
  for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
6331
6679
  }
6332
- async getattr(mount, path, rel) {
6680
+ async getattr(mount, path, rel2) {
6333
6681
  if (mount.conn.failure) throw mount.conn.failure;
6334
6682
  const cached = mount.attrs.get(path);
6335
6683
  if (cached && cached.expires > Date.now()) return cached.value;
6336
- const attr = await mount.conn.call({ op: "getattr", path: rel });
6684
+ const attr = await mount.conn.call({ op: "getattr", path: rel2 });
6337
6685
  const ttl = mount.caps.attrTtl ?? 1e3;
6338
6686
  if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
6339
6687
  return attr;
6340
6688
  }
6341
- async readdir(mount, path, rel) {
6689
+ async readdir(mount, path, rel2) {
6342
6690
  if (mount.conn.failure) throw mount.conn.failure;
6343
6691
  const cached = mount.lists.get(path);
6344
6692
  if (cached && cached.expires > Date.now()) return cached.value;
6345
- const entries = await mount.conn.call({ op: "readdir", path: rel });
6693
+ const entries = await mount.conn.call({ op: "readdir", path: rel2 });
6346
6694
  const ttl = mount.caps.entryTtl ?? mount.caps.attrTtl ?? 1e3;
6347
6695
  if (ttl > 0) mount.lists.set(path, { value: entries, expires: Date.now() + ttl });
6348
6696
  return entries;
@@ -6387,10 +6735,10 @@ var MountFs = class {
6387
6735
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
6388
6736
  return this.follow(this.base.resolvePath(parentOf4(real), target), hops + 1, create);
6389
6737
  }
6390
- async open(mount, rel, write) {
6738
+ async open(mount, rel2, write) {
6391
6739
  return await mount.conn.call({
6392
6740
  op: "open",
6393
- path: rel,
6741
+ path: rel2,
6394
6742
  write,
6395
6743
  create: write,
6396
6744
  truncate: write,
@@ -6407,13 +6755,22 @@ var MountFs = class {
6407
6755
  const real = await this.follow(path);
6408
6756
  const found = this.at(real);
6409
6757
  if (!found) return this.base.readFileBuffer(real);
6410
- const { mount, rel } = found;
6411
- const attr = await this.table.getattr(mount, real, rel);
6758
+ for (let attempt = 1; ; attempt++) {
6759
+ try {
6760
+ return await this.readWhole(found.mount, real, found.rel);
6761
+ } catch (err) {
6762
+ if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
6763
+ this.table.forget(found.mount, real);
6764
+ }
6765
+ }
6766
+ }
6767
+ async readWhole(mount, real, rel2) {
6768
+ const attr = await this.table.getattr(mount, real, rel2);
6412
6769
  if (attr.kind === "directory") throw errnoError("EISDIR", real);
6413
6770
  if (mount.maxFile > 0 && attr.size > mount.maxFile) {
6414
6771
  throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
6415
6772
  }
6416
- const fh = await this.open(mount, rel, false);
6773
+ const fh = await this.open(mount, rel2, false);
6417
6774
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
6418
6775
  const chunks2 = [];
6419
6776
  let total = 0;
@@ -6439,7 +6796,7 @@ var MountFs = class {
6439
6796
  const real = await this.follow(path, 0, true);
6440
6797
  const found = this.at(real);
6441
6798
  if (!found) return this.base.writeFile(real, content);
6442
- const { mount, rel } = found;
6799
+ const { mount, rel: rel2 } = found;
6443
6800
  this.writable(mount, real);
6444
6801
  const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
6445
6802
  if (mount.maxFile > 0 && bytes.length > mount.maxFile) {
@@ -6448,8 +6805,17 @@ var MountFs = class {
6448
6805
  `${real} would be larger than this mount's maxfile (${mount.maxFile})`
6449
6806
  );
6450
6807
  }
6808
+ for (let attempt = 1; ; attempt++) {
6809
+ try {
6810
+ return await this.writeWhole(mount, real, rel2, bytes);
6811
+ } catch (err) {
6812
+ if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
6813
+ }
6814
+ }
6815
+ }
6816
+ async writeWhole(mount, real, rel2, bytes) {
6451
6817
  this.table.forget(mount, real);
6452
- const fh = await this.open(mount, rel, true);
6818
+ const fh = await this.open(mount, rel2, true);
6453
6819
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
6454
6820
  try {
6455
6821
  for (let offset = 0; offset < bytes.length; offset += chunk2) {
@@ -6516,16 +6882,16 @@ var MountFs = class {
6516
6882
  async mkdir(path, options = {}) {
6517
6883
  const found = this.at(path);
6518
6884
  if (!found) return this.base.mkdir(path, options);
6519
- const { mount, rel } = found;
6885
+ const { mount, rel: rel2 } = found;
6520
6886
  const real = normalizePath(path);
6521
6887
  this.writable(mount, real);
6522
6888
  if (!options.recursive) {
6523
- if (rel === "/") throw errnoError("EEXIST", real);
6889
+ if (rel2 === "/") throw errnoError("EEXIST", real);
6524
6890
  this.table.forget(mount, real);
6525
- await mount.conn.call({ op: "mkdir", path: rel });
6891
+ await mount.conn.call({ op: "mkdir", path: rel2 });
6526
6892
  return;
6527
6893
  }
6528
- const parts = rel.split("/").filter(Boolean);
6894
+ const parts = rel2.split("/").filter(Boolean);
6529
6895
  for (let n = 1; n <= parts.length; n++) {
6530
6896
  const sub = `/${parts.slice(0, n).join("/")}`;
6531
6897
  const full = normalizePath(`${mount.target}${sub}`);
@@ -6539,25 +6905,25 @@ var MountFs = class {
6539
6905
  async rm(path, options = {}) {
6540
6906
  const found = this.at(path);
6541
6907
  if (!found) return this.base.rm(path, options);
6542
- const { mount, rel } = found;
6908
+ const { mount, rel: rel2 } = found;
6543
6909
  const real = normalizePath(path);
6544
- if (rel === "/") throw errnoError("EBUSY", `${real} is a mount point`);
6910
+ if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
6545
6911
  this.writable(mount, real);
6546
6912
  let attr;
6547
6913
  try {
6548
- attr = await this.table.getattr(mount, real, rel);
6914
+ attr = await this.table.getattr(mount, real, rel2);
6549
6915
  } catch (err) {
6550
6916
  if (options.force && err.code === "ENOENT") return;
6551
6917
  throw err;
6552
6918
  }
6553
6919
  this.table.forget(mount, real);
6554
6920
  if (attr.kind === "directory" && options.recursive) {
6555
- for (const entry of await this.table.readdir(mount, real, rel)) {
6921
+ for (const entry of await this.table.readdir(mount, real, rel2)) {
6556
6922
  await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
6557
6923
  }
6558
6924
  this.table.forget(mount, real);
6559
6925
  }
6560
- await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel });
6926
+ await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
6561
6927
  this.table.forget(mount, real);
6562
6928
  }
6563
6929
  async rename(from, to) {
@@ -7019,6 +7385,7 @@ var Launcher = class {
7019
7385
  media;
7020
7386
  onMountPending;
7021
7387
  removable = /* @__PURE__ */ new Map();
7388
+ hostfs;
7022
7389
  inserted = /* @__PURE__ */ new Map();
7023
7390
  openFiles = /* @__PURE__ */ new Set();
7024
7391
  held = /* @__PURE__ */ new Set();
@@ -7036,6 +7403,7 @@ var Launcher = class {
7036
7403
  this.createDriverWorker = options.createDriverWorker;
7037
7404
  this.media = options.media ?? memoryMedia();
7038
7405
  this.onMountPending = options.onMountPending;
7406
+ this.hostfs = options;
7039
7407
  this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
7040
7408
  this.modulesDir = options.modules ?? "/node_modules";
7041
7409
  this.env = { ...networkEnv(), ...options.env };
@@ -7301,6 +7669,17 @@ var Launcher = class {
7301
7669
  }
7302
7670
  async driver(type, spec) {
7303
7671
  if (type === "fsa") return this.fsaDriver(spec);
7672
+ if (type === "hostfs") {
7673
+ const { hostfs, hostfsFetch, hostfsTiming } = this.hostfs;
7674
+ if (!hostfs)
7675
+ throw fsError("ENODEV", "this kernel has no hostfs hook (createKernel({ hostfs }))");
7676
+ return openHostfs(
7677
+ spec,
7678
+ hostfs,
7679
+ hostfsFetch ?? ((url, init) => fetch(url, init)),
7680
+ hostfsTiming
7681
+ );
7682
+ }
7304
7683
  if (type === "tmpfs") {
7305
7684
  const { port1, port2 } = new MessageChannel();
7306
7685
  serveFilesystem(port2, tmpfs(), { symlinks: true, chmod: true, attrTtl: 0 });