@ai-ecoverse/slicc-kernel 1.170.3 → 1.171.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
@@ -2362,9 +2362,9 @@ var KernelSocket = class _KernelSocket {
2362
2362
  case SO_TYPE:
2363
2363
  return SOCK_STREAM;
2364
2364
  case SO_ERROR: {
2365
- const failed3 = this.failed;
2365
+ const failed4 = this.failed;
2366
2366
  this.failed = void 0;
2367
- return failed3 ? wasiErrno(failed3) : 0;
2367
+ return failed4 ? wasiErrno(failed4) : 0;
2368
2368
  }
2369
2369
  case SO_ACCEPTCONN:
2370
2370
  return this.state === "listening" ? 1 : 0;
@@ -3695,7 +3695,7 @@ function satisfies(version, range2) {
3695
3695
  }
3696
3696
 
3697
3697
  // src/version.ts
3698
- var KERNEL_VERSION = true ? "1.170.3" : "0.0.0-dev";
3698
+ var KERNEL_VERSION = true ? "1.171.0" : "0.0.0-dev";
3699
3699
  var OS_RELEASE = "6.0.0-slicc";
3700
3700
  function procVersion() {
3701
3701
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -3987,10 +3987,10 @@ async function tierPackages(fs, physical, roots, bins, kernel, global) {
3987
3987
  const own = commandsOf(pkg, manifest);
3988
3988
  const named = new Set(own.map((command) => command.name));
3989
3989
  const extra = bins ? await binCommandsOf(physical, pkg, manifest, named) : [];
3990
- const refused3 = engineRefusal(manifest, kernel);
3990
+ const refused4 = engineRefusal(manifest, kernel);
3991
3991
  const name2 = pkg.slice(root.length + 1);
3992
3992
  const dependency = direct?.has(name2) === false;
3993
- const marks = { ...refused3 ? { refused: refused3 } : {}, ...dependency ? { dependency } : {} };
3993
+ const marks = { ...refused4 ? { refused: refused4 } : {}, ...dependency ? { dependency } : {} };
3994
3994
  out.push({
3995
3995
  name: name2,
3996
3996
  pkg,
@@ -4022,7 +4022,7 @@ function winners(tier) {
4022
4022
  }
4023
4023
  return out;
4024
4024
  }
4025
- async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused3, global = /* @__PURE__ */ new Set()) {
4025
+ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, kernel = KERNEL_VERSION, refused4, global = /* @__PURE__ */ new Set()) {
4026
4026
  const found = /* @__PURE__ */ new Map();
4027
4027
  const physical = physicalResolver(fs);
4028
4028
  const tiers = (typeof modules === "string" ? [modules] : modules).map(
@@ -4036,7 +4036,7 @@ async function scanCommands(fs, modules, binsFrom = Number.POSITIVE_INFINITY, ke
4036
4036
  const dependencies = [];
4037
4037
  for (const [index, roots] of tiers.entries()) {
4038
4038
  const tier = await tierPackages(fs, physical, roots, index >= binsFrom, kernel, global);
4039
- if (refused3) refused3.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
4039
+ if (refused4) refused4.push(...tier.flatMap((entry) => entry.commands.filter((c) => c.refused)));
4040
4040
  await take(tier.filter((entry) => !entry.dependency));
4041
4041
  dependencies.push(tier.filter((entry) => entry.dependency));
4042
4042
  }
@@ -5102,12 +5102,12 @@ var UnlinkedKeeper = class {
5102
5102
  return this.locks;
5103
5103
  }
5104
5104
  async undoing(op, peers, sends, heard, shared2 = []) {
5105
- let failed3;
5105
+ let failed4;
5106
5106
  let out;
5107
5107
  try {
5108
5108
  out = await op();
5109
5109
  } catch (err) {
5110
- failed3 = err ?? new Error("failed");
5110
+ failed4 = err ?? new Error("failed");
5111
5111
  await this.rollBackAsides(sends, shared2);
5112
5112
  }
5113
5113
  const hiddenShared = new Set(shared2.map((one) => one.hidden));
@@ -5128,7 +5128,7 @@ var UnlinkedKeeper = class {
5128
5128
  const done4 = () => one.release().then(() => this.dropIfLast(one.hidden));
5129
5129
  void Promise.all(waits.map(({ told }) => told)).then(done4, done4);
5130
5130
  }
5131
- if (failed3 !== void 0) throw failed3;
5131
+ if (failed4 !== void 0) throw failed4;
5132
5132
  return out;
5133
5133
  }
5134
5134
  async rollBackAsides(sends, shared2) {
@@ -5602,6 +5602,78 @@ var CronService = class {
5602
5602
  }
5603
5603
  };
5604
5604
 
5605
+ // src/kernel/net/placeholder-floor.ts
5606
+ var PLACEHOLDER = /fnox_proxy_[0-9a-f]{24}/;
5607
+ var ESCAPE = /%([0-9a-f]{2})/gi;
5608
+ var BASIC = /\bbasic\b(.*)/is;
5609
+ var PERCENT_LAYERS = 8;
5610
+ var BASE64_GROUP = 4;
5611
+ var PLACEHOLDER_REFUSED = "refused: this request has a sealed placeholder in its URL, a header or a Basic credential, and nothing fills it, so it was not sent";
5612
+ var PlaceholderRefused = class extends Error {
5613
+ status = 403;
5614
+ code = "EACCES";
5615
+ constructor() {
5616
+ super(PLACEHOLDER_REFUSED);
5617
+ }
5618
+ };
5619
+ function carries(text) {
5620
+ let layer = text;
5621
+ for (let i = 0; i < PERCENT_LAYERS; i++) {
5622
+ if (PLACEHOLDER.test(layer)) return true;
5623
+ const next3 = layer.replace(
5624
+ ESCAPE,
5625
+ (_, hex4) => String.fromCharCode(Number.parseInt(hex4, 16))
5626
+ );
5627
+ if (next3 === layer) return false;
5628
+ layer = next3;
5629
+ }
5630
+ return PLACEHOLDER.test(layer);
5631
+ }
5632
+ function basicCarries(value2) {
5633
+ const credential = BASIC.exec(value2)?.[1];
5634
+ if (credential === void 0) return false;
5635
+ const letters = credential.replace(/[^A-Za-z0-9+/_-]/g, "").replaceAll("-", "+").replaceAll("_", "/");
5636
+ for (let shift = 0; shift < BASE64_GROUP; shift++) {
5637
+ const part = letters.slice(shift);
5638
+ const whole = part.length % BASE64_GROUP === 1 ? part.slice(0, -1) : part;
5639
+ if (carries(atob(whole))) return true;
5640
+ }
5641
+ return false;
5642
+ }
5643
+ function headersCarry(headers) {
5644
+ return headers.some(
5645
+ ([name2, value2]) => carries(name2.toLowerCase()) || carries(value2) || basicCarries(value2)
5646
+ );
5647
+ }
5648
+ function urlCarries(url) {
5649
+ return carries(url) || URL.canParse(url) && carries(new URL(url).href);
5650
+ }
5651
+ function refuse(request) {
5652
+ if (urlCarries(request.url) || headersCarry(request.headers)) throw new PlaceholderRefused();
5653
+ }
5654
+ function refuseSocket(request) {
5655
+ refuse(request);
5656
+ if (request.protocols.some(carries)) throw new PlaceholderRefused();
5657
+ }
5658
+ function placeholderFloor(inner) {
5659
+ const open = inner.websocket?.bind(inner);
5660
+ return {
5661
+ get traits() {
5662
+ return inner.traits;
5663
+ },
5664
+ ...open ? {
5665
+ websocket: async (request) => {
5666
+ refuseSocket(request);
5667
+ return open(request);
5668
+ }
5669
+ } : {},
5670
+ async fetch(request, owner) {
5671
+ refuse(request);
5672
+ return inner.fetch(request, owner);
5673
+ }
5674
+ };
5675
+ }
5676
+
5605
5677
  // src/kernel/net/ws-queue.ts
5606
5678
  var INBOUND_LIMIT = 16 * 1024 * 1024;
5607
5679
  var MESSAGE_LIMIT = 64 * 1024 * 1024;
@@ -6118,8 +6190,8 @@ var RealmProxy = class {
6118
6190
  }
6119
6191
  async connect(conn, incoming, req, idle2) {
6120
6192
  const target = tunnelTarget(req);
6121
- const refused3 = target.port === 443 ? await this.tunnelReady(target) : void 0;
6122
- if (refused3) throw refused3;
6193
+ const refused4 = target.port === 443 ? await this.tunnelReady(target) : void 0;
6194
+ if (refused4) throw refused4;
6123
6195
  await conn.write(latin1Bytes("HTTP/1.1 200 Connection Established\r\n\r\n"), this.stop.signal);
6124
6196
  try {
6125
6197
  const first = await incoming.peek(this.stop.signal);
@@ -6176,7 +6248,8 @@ var RealmProxy = class {
6176
6248
  });
6177
6249
  } catch (e) {
6178
6250
  waiting.abort();
6179
- throw new HttpError(502, e instanceof Error ? e.message : String(e));
6251
+ const status = e instanceof PlaceholderRefused ? e.status : 502;
6252
+ throw new HttpError(status, e instanceof Error ? e.message : String(e));
6180
6253
  }
6181
6254
  ctx.idle.ms = Number.POSITIVE_INFINITY;
6182
6255
  try {
@@ -6390,7 +6463,7 @@ async function relay(socket, conn) {
6390
6463
  // src/fs/attest.ts
6391
6464
  var ATTEST_MAX = 1024 * 1024;
6392
6465
  var UNOWNED = 1e3;
6393
- function refuse(code, path, why) {
6466
+ function refuse2(code, path, why) {
6394
6467
  throw Object.assign(new Error(`attest ${path}: ${why}`), { code });
6395
6468
  }
6396
6469
  function chainOf(path) {
@@ -6432,11 +6505,11 @@ async function trustedParents(fs, path, o) {
6432
6505
  const parents = [];
6433
6506
  for (const dir of chainOf(path).slice(0, -1)) {
6434
6507
  const st = await fs.lstat(dir);
6435
- if (st.isSymbolicLink) refuse("EACCES", path, `${dir} is a symlink`);
6436
- if (!st.isDirectory) refuse("ENOTDIR", path, `${dir} is not a directory`);
6508
+ if (st.isSymbolicLink) refuse2("EACCES", path, `${dir} is a symlink`);
6509
+ if (!st.isDirectory) refuse2("ENOTDIR", path, `${dir} is not a directory`);
6437
6510
  const perm = owned(dir, st);
6438
6511
  if (o.users !== "off" && (perm.uid !== 0 || (perm.mode & 18) !== 0)) {
6439
- refuse("EACCES", path, `${dir} is not root-owned or is writable by group or others`);
6512
+ refuse2("EACCES", path, `${dir} is not root-owned or is writable by group or others`);
6440
6513
  }
6441
6514
  parents.push(perm);
6442
6515
  }
@@ -6444,27 +6517,27 @@ async function trustedParents(fs, path, o) {
6444
6517
  }
6445
6518
  async function attest(fs, asked, o) {
6446
6519
  if (typeof asked !== "string" || !asked.startsWith("/")) {
6447
- refuse("EINVAL", String(asked), "not an absolute path");
6520
+ refuse2("EINVAL", String(asked), "not an absolute path");
6448
6521
  }
6449
6522
  const path = normalizePath(asked);
6450
6523
  const mount = o.mounts.find((target) => {
6451
6524
  const at = normalizePath(target);
6452
6525
  return at === "/" || under(path, at);
6453
6526
  });
6454
- if (mount !== void 0) refuse("EACCES", path, `under the mount ${mount}`);
6527
+ if (mount !== void 0) refuse2("EACCES", path, `under the mount ${mount}`);
6455
6528
  const parents = await trustedParents(fs, path, o);
6456
6529
  for (let tries = 0; ; tries++) {
6457
6530
  const before = await fs.lstat(path);
6458
- if (before.isSymbolicLink) refuse("EACCES", path, "is a symlink");
6531
+ if (before.isSymbolicLink) refuse2("EACCES", path, "is a symlink");
6459
6532
  if (!before.isFile)
6460
- refuse(before.isDirectory ? "EISDIR" : "EINVAL", path, "is not a regular file");
6461
- if (before.size > ATTEST_MAX) refuse("EFBIG", path, `is over ${ATTEST_MAX} bytes`);
6533
+ refuse2(before.isDirectory ? "EISDIR" : "EINVAL", path, "is not a regular file");
6534
+ if (before.size > ATTEST_MAX) refuse2("EFBIG", path, `is over ${ATTEST_MAX} bytes`);
6462
6535
  const bytes = (await fs.readFileBuffer(path)).slice();
6463
6536
  const after = await fs.lstat(path);
6464
6537
  if (unchanged(before, after, bytes)) {
6465
6538
  return attested(path, after, bytes, await sha256(bytes), o, parents);
6466
6539
  }
6467
- if (tries >= 1) refuse("EAGAIN", path, "changed while it was read");
6540
+ if (tries >= 1) refuse2("EAGAIN", path, "changed while it was read");
6468
6541
  }
6469
6542
  }
6470
6543
  var WATCH_DEBOUNCE_MS = 50;
@@ -9293,8 +9366,8 @@ var DriverConnection = class _DriverConnection {
9293
9366
  } finally {
9294
9367
  clearTimeout(timer);
9295
9368
  }
9296
- const refused3 = driverVersionError(hello.protocol);
9297
- if (refused3) throw errnoError("EPROTO", refused3);
9369
+ const refused4 = driverVersionError(hello.protocol);
9370
+ if (refused4) throw errnoError("EPROTO", refused4);
9298
9371
  if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
9299
9372
  return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options);
9300
9373
  }
@@ -11318,8 +11391,8 @@ async function guarded(entry, path, req) {
11318
11391
  return { paths };
11319
11392
  } catch (err) {
11320
11393
  const refusal3 = err.code === "EACCES";
11321
- const refused3 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
11322
- return { refused: refused3 };
11394
+ const refused4 = req.op === "exists" && refusal3 ? json2(false) : toErrno(err);
11395
+ return { refused: refused4 };
11323
11396
  }
11324
11397
  }
11325
11398
  async function copyOp(entry, path, req) {
@@ -11515,13 +11588,13 @@ async function created(entry, req) {
11515
11588
  return seen === void 0 ? looked : json2({ ...looked.json, seen });
11516
11589
  }
11517
11590
  var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
11518
- async function rmPermitted(entry, path, req, refused3) {
11591
+ async function rmPermitted(entry, path, req, refused4) {
11519
11592
  const st = await entry.fs.lstat(path).catch(() => void 0);
11520
- if (!st?.isDirectory) return refused3;
11593
+ if (!st?.isDirectory) return refused4;
11521
11594
  const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
11522
11595
  if ("refused" in unlisted) return unlisted.refused;
11523
11596
  const names2 = await entry.fs.readdir(path).catch(() => void 0);
11524
- if (!names2) return refused3;
11597
+ if (!names2) return refused4;
11525
11598
  let first;
11526
11599
  for (const name2 of names2) {
11527
11600
  const child = await dispatchOne(entry, {
@@ -11572,14 +11645,14 @@ async function newDevName(entry, path, req) {
11572
11645
  const named = NAMING.has(req.op);
11573
11646
  if (!named && !CREATIONS.has(req.op)) return void 0;
11574
11647
  const target = named ? entry.fs.resolvePath(entry.cwd, req.arg2 ?? "") : path;
11575
- const refused3 = devCreation(target, entry.cred?.());
11576
- if (!refused3) return void 0;
11648
+ const refused4 = devCreation(target, entry.cred?.());
11649
+ if (!refused4) return void 0;
11577
11650
  const there = await entry.fs.lstat(target).then(
11578
11651
  () => true,
11579
11652
  () => false
11580
11653
  );
11581
11654
  if (there) return void 0;
11582
- return { ok: false, errno: refused3, message: `${target}: no new names in /dev or /proc` };
11655
+ return { ok: false, errno: refused4, message: `${target}: no new names in /dev or /proc` };
11583
11656
  }
11584
11657
  var MISS_OPS = /* @__PURE__ */ new Set(["stat", "lstat"]);
11585
11658
  async function linkFree(fs, path) {
@@ -11702,7 +11775,7 @@ function encodeSabResult(result) {
11702
11775
  message: result.message,
11703
11776
  ...result.direct ? { direct: true } : {},
11704
11777
  ...result.dev !== void 0 ? { dev: result.dev } : {},
11705
- ...result.detail === "transport" ? { detail: result.detail } : {}
11778
+ ...result.detail ? { detail: result.detail } : {}
11706
11779
  })
11707
11780
  )
11708
11781
  };
@@ -13034,11 +13107,11 @@ var EpollFile = class _EpollFile {
13034
13107
  const round = new AbortController();
13035
13108
  const stop = () => round.abort();
13036
13109
  signal.addEventListener("abort", stop, { once: true });
13037
- let failed3 = false;
13110
+ let failed4 = false;
13038
13111
  try {
13039
13112
  await file.changed(round.signal);
13040
13113
  } catch {
13041
- failed3 = true;
13114
+ failed4 = true;
13042
13115
  } finally {
13043
13116
  round.abort();
13044
13117
  signal.removeEventListener("abort", stop);
@@ -13046,7 +13119,7 @@ var EpollFile = class _EpollFile {
13046
13119
  if (signal.aborted) return;
13047
13120
  reg.gen++;
13048
13121
  this.changes.wake();
13049
- if (failed3) await pause(RETRY_MS, signal);
13122
+ if (failed4) await pause(RETRY_MS, signal);
13050
13123
  }
13051
13124
  }
13052
13125
  };
@@ -13682,8 +13755,8 @@ var KernelLoopback = class {
13682
13755
  );
13683
13756
  } catch (err) {
13684
13757
  socket.close();
13685
- const refused3 = err instanceof KernelError && err.code === "ECONNREFUSED";
13686
- throw refused3 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
13758
+ const refused4 = err instanceof KernelError && err.code === "ECONNREFUSED";
13759
+ throw refused4 ? errno2("ECONNREFUSED", `nothing listens on the kernel's port ${port}`) : err;
13687
13760
  }
13688
13761
  signal.addEventListener("abort", () => socket.close(), { once: true });
13689
13762
  return socket;
@@ -15116,23 +15189,29 @@ var HttpHandles = class {
15116
15189
  return response.redirected ? { ...head, json: { ...head.json, redirected: true } } : head;
15117
15190
  } catch (e) {
15118
15191
  entry.upload?.cancel();
15119
- const text = message2(e);
15120
- if (port !== void 0) return { ok: false, errno: codeOf2(e), message: text };
15121
- const status = e.status;
15122
- if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
15123
- const code = codeOf2(e);
15124
- const errno3 = text === NO_TRANSPORT ? "ENETUNREACH" : code !== "EIO" ? code : "ECONNREFUSED";
15125
- const failed3 = e.detail === "transport";
15126
- return { ok: false, errno: errno3, message: text, ...failed3 ? { detail: "transport" } : {} };
15127
- }
15128
- entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
15129
- return this.headOf(handle, req.url, {
15130
- status,
15131
- statusText: "",
15132
- headers: [["content-type", "text/plain; charset=utf-8"]]
15133
- });
15192
+ if (port !== void 0) return { ok: false, errno: codeOf2(e), message: message2(e) };
15193
+ return this.failure(handle, req, e, entry);
15134
15194
  }
15135
15195
  }
15196
+ failure(handle, req, e, entry) {
15197
+ const text = message2(e);
15198
+ if (e instanceof PlaceholderRefused) {
15199
+ return { ok: false, errno: e.code, message: text, detail: "placeholder" };
15200
+ }
15201
+ const status = e.status;
15202
+ if (text === NO_TRANSPORT || typeof status !== "number" || status === 502) {
15203
+ const code = codeOf2(e);
15204
+ const errno3 = text === NO_TRANSPORT ? "ENETUNREACH" : code !== "EIO" ? code : "ECONNREFUSED";
15205
+ const failed4 = e.detail === "transport";
15206
+ return { ok: false, errno: errno3, message: text, ...failed4 ? { detail: "transport" } : {} };
15207
+ }
15208
+ entry.body = once(new TextEncoder().encode(text))[Symbol.asyncIterator]();
15209
+ return this.headOf(handle, req.url, {
15210
+ status,
15211
+ statusText: "",
15212
+ headers: [["content-type", "text/plain; charset=utf-8"]]
15213
+ });
15214
+ }
15136
15215
  headOf(handle, url, response) {
15137
15216
  const { status, statusText, headers } = response;
15138
15217
  return { ok: true, kind: "json", json: { handle, status, statusText, url, headers } };
@@ -17032,23 +17111,23 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
17032
17111
  let pending = 0;
17033
17112
  let busy = false;
17034
17113
  let timer;
17035
- let failed3;
17114
+ let failed4;
17036
17115
  const waiters = /* @__PURE__ */ new Set();
17037
17116
  const quiet3 = () => !busy && pending === 0;
17038
17117
  const wake = () => {
17039
17118
  for (const waiter of [...waiters]) waiter();
17040
17119
  };
17041
17120
  const raise = () => {
17042
- if (failed3 === void 0) return;
17043
- const err = failed3;
17044
- failed3 = void 0;
17121
+ if (failed4 === void 0) return;
17122
+ const err = failed4;
17123
+ failed4 = void 0;
17045
17124
  throw err;
17046
17125
  };
17047
17126
  const later = (bytes) => {
17048
17127
  if (bytes.length === 0) return;
17049
17128
  pending += 1;
17050
17129
  tail2 = tail2.then(() => writeAll2(file, bytes)).catch((err) => {
17051
- if (failed3 === void 0) failed3 = err;
17130
+ if (failed4 === void 0) failed4 = err;
17052
17131
  }).finally(() => {
17053
17132
  pending -= 1;
17054
17133
  if (quiet3()) wake();
@@ -18230,7 +18309,7 @@ var HEAD_MAX = 16 * 1024;
18230
18309
  var ANY_PLACEHOLDER = /fnox_proxy_[0-9a-f]{24}/g;
18231
18310
  var REQUEST_LINE2 = /^([!#$%&'*+.^_`|~0-9A-Za-z-]+) ([\x21-\x7e]+) HTTP\/1\.[01]$/;
18232
18311
  var HEADER_LINE = /^([!#$%&'*+.^_`|~0-9A-Za-z-]+):[ \t]*(.*?)[ \t]*$/;
18233
- var BASIC = /^(Basic[ \t]+)([A-Za-z0-9+/]+={0,2})$/i;
18312
+ var BASIC2 = /^(Basic[ \t]+)([A-Za-z0-9+/]+={0,2})$/i;
18234
18313
  var encoder8 = new TextEncoder();
18235
18314
  function binary(text) {
18236
18315
  let out = "";
@@ -18246,7 +18325,7 @@ function sniffHttp1(start2) {
18246
18325
  return "no";
18247
18326
  }
18248
18327
  function basicOf(value2) {
18249
- const m = BASIC.exec(value2);
18328
+ const m = BASIC2.exec(value2);
18250
18329
  if (!m) return void 0;
18251
18330
  try {
18252
18331
  return { scheme: m[1], decoded: atob(m[2]) };
@@ -25179,6 +25258,234 @@ function enableDnsStub(net, stub) {
25179
25258
  });
25180
25259
  }
25181
25260
 
25261
+ // src/kernel/net/remote-transport.ts
25262
+ var TransportError = class extends Error {
25263
+ status;
25264
+ code;
25265
+ detail;
25266
+ constructor(message3, status, code, detail) {
25267
+ super(message3);
25268
+ this.status = status;
25269
+ this.code = code;
25270
+ this.detail = detail === "transport" ? detail : void 0;
25271
+ }
25272
+ };
25273
+ function unfollowed2(redirect) {
25274
+ return redirect !== void 0 && redirect !== "follow";
25275
+ }
25276
+ function honors(traits, redirect) {
25277
+ if (traits.redirectPolicy === true) return true;
25278
+ const hops = traits.redirectHops;
25279
+ const raw2 = traits.manualRedirects === true && (hops === void 0 || hops === false);
25280
+ return redirect === "manual" && raw2;
25281
+ }
25282
+ function refused3(message3) {
25283
+ return new TransportError(message3, 502, void 0, "transport");
25284
+ }
25285
+ var RemoteTransport = class {
25286
+ traits;
25287
+ port;
25288
+ waiting = /* @__PURE__ */ new Map();
25289
+ nextId = 0;
25290
+ sockets = /* @__PURE__ */ new Map();
25291
+ uploads = /* @__PURE__ */ new Map();
25292
+ failure;
25293
+ constructor(port, traits) {
25294
+ this.port = port;
25295
+ this.traits = traits;
25296
+ }
25297
+ receive(reply) {
25298
+ if (reply.net === "body-pull") {
25299
+ void this.pull(reply.nid);
25300
+ return;
25301
+ }
25302
+ const socket = this.sockets.get(reply.nid);
25303
+ if (socket && this.toSocket(reply, socket)) return;
25304
+ const waiter = this.waiting.get(reply.nid);
25305
+ if (!waiter) return;
25306
+ this.waiting.delete(reply.nid);
25307
+ if (reply.net === "error") {
25308
+ waiter.reject(new TransportError(reply.message, reply.status, reply.code, reply.detail));
25309
+ } else waiter.resolve(reply);
25310
+ }
25311
+ toSocket(reply, socket) {
25312
+ if (reply.net === "ws-message") socket.queue.push(reply.data);
25313
+ else if (reply.net === "ws-acked") socket.acked(reply.n);
25314
+ else if (reply.net === "ws-closed") {
25315
+ this.sockets.delete(reply.nid);
25316
+ socket.queue.end({ code: reply.code, reason: reply.reason });
25317
+ } else return false;
25318
+ return true;
25319
+ }
25320
+ async pull(nid) {
25321
+ const upload = this.uploads.get(nid);
25322
+ if (!upload) {
25323
+ this.port.postMessage({ net: "body-error", nid, message: "the request body is gone" });
25324
+ return;
25325
+ }
25326
+ try {
25327
+ const next3 = await upload.next();
25328
+ if (next3.done) {
25329
+ this.uploads.delete(nid);
25330
+ this.port.postMessage({ net: "body-end", nid });
25331
+ } else this.port.postMessage({ net: "body-chunk", nid, bytes: next3.value });
25332
+ } catch (e) {
25333
+ this.uploads.delete(nid);
25334
+ this.port.postMessage({
25335
+ net: "body-error",
25336
+ nid,
25337
+ message: e instanceof Error ? e.message : String(e)
25338
+ });
25339
+ }
25340
+ }
25341
+ dropUpload(nid) {
25342
+ const upload = this.uploads.get(nid);
25343
+ this.uploads.delete(nid);
25344
+ void upload?.return?.().catch(() => void 0);
25345
+ }
25346
+ fail(error) {
25347
+ this.failure = error;
25348
+ for (const nid of [...this.uploads.keys()]) this.dropUpload(nid);
25349
+ for (const waiter of this.waiting.values()) waiter.reject(error);
25350
+ this.waiting.clear();
25351
+ for (const socket of this.sockets.values())
25352
+ socket.queue.end({ code: 1011, reason: error.message });
25353
+ this.sockets.clear();
25354
+ }
25355
+ ask(call) {
25356
+ if (this.failure) return Promise.reject(this.failure);
25357
+ return new Promise((resolve2, reject) => {
25358
+ this.waiting.set(call.nid, { resolve: resolve2, reject });
25359
+ this.port.postMessage(call);
25360
+ });
25361
+ }
25362
+ cancel(nid) {
25363
+ const waiter = this.waiting.get(nid);
25364
+ this.waiting.delete(nid);
25365
+ waiter?.reject(new TransportError("the request was cancelled"));
25366
+ this.port.postMessage({ net: "cancel", nid });
25367
+ }
25368
+ async fetch(request) {
25369
+ const nid = ++this.nextId;
25370
+ const { signal, redirect } = request;
25371
+ signal.throwIfAborted();
25372
+ if (unfollowed2(redirect) && !honors(this.traits, redirect)) {
25373
+ throw refused3(`this transport cannot honor redirect: ${redirect}`);
25374
+ }
25375
+ let finished = false;
25376
+ const abort = () => {
25377
+ finished = true;
25378
+ this.dropUpload(nid);
25379
+ this.cancel(nid);
25380
+ };
25381
+ signal.addEventListener("abort", abort, { once: true });
25382
+ const finish = () => {
25383
+ this.dropUpload(nid);
25384
+ if (finished) return;
25385
+ finished = true;
25386
+ signal.removeEventListener("abort", abort);
25387
+ };
25388
+ if (request.bodyStream) this.uploads.set(nid, request.bodyStream[Symbol.asyncIterator]());
25389
+ let head;
25390
+ try {
25391
+ head = await this.ask({
25392
+ net: "fetch",
25393
+ nid,
25394
+ url: request.url,
25395
+ method: request.method,
25396
+ headers: request.headers,
25397
+ ...request.bodyStream ? { streamed: true } : request.body ? { body: request.body } : {},
25398
+ ...redirect !== void 0 ? { redirect } : {}
25399
+ });
25400
+ } catch (e) {
25401
+ finish();
25402
+ throw e;
25403
+ }
25404
+ const { status, statusText, headers, url, redirected: redirected2, redirects, opaqueRedirect } = head;
25405
+ const read = async () => {
25406
+ if (finished) return void 0;
25407
+ const reply = await this.ask({ net: "read", nid }).catch((e) => {
25408
+ finish();
25409
+ throw e;
25410
+ });
25411
+ if (reply.net === "chunk") return reply.bytes;
25412
+ finish();
25413
+ return void 0;
25414
+ };
25415
+ const cancel = async () => {
25416
+ if (finished) return;
25417
+ finish();
25418
+ this.cancel(nid);
25419
+ };
25420
+ if (unfollowed2(redirect) && (redirected2 || redirects?.length)) {
25421
+ await cancel();
25422
+ throw refused3(`this transport followed a redirect despite redirect: ${redirect}`);
25423
+ }
25424
+ return {
25425
+ ...url !== void 0 ? { url } : {},
25426
+ ...redirected2 !== void 0 ? { redirected: redirected2 } : {},
25427
+ ...redirects ? { redirects } : {},
25428
+ ...opaqueRedirect ? { opaqueRedirect } : {},
25429
+ status,
25430
+ statusText,
25431
+ headers,
25432
+ cancel,
25433
+ body: {
25434
+ [Symbol.asyncIterator]: () => ({
25435
+ next: async () => {
25436
+ const bytes = await read();
25437
+ return bytes ? { value: bytes, done: false } : { value: void 0, done: true };
25438
+ },
25439
+ return: async () => {
25440
+ await cancel();
25441
+ return { value: void 0, done: true };
25442
+ }
25443
+ })
25444
+ }
25445
+ };
25446
+ }
25447
+ async websocket(request) {
25448
+ const nid = ++this.nextId;
25449
+ const { signal } = request;
25450
+ signal.throwIfAborted();
25451
+ const abort = () => this.cancel(nid);
25452
+ signal.addEventListener("abort", abort, { once: true });
25453
+ let opened2;
25454
+ try {
25455
+ opened2 = await this.ask({
25456
+ net: "ws-open",
25457
+ nid,
25458
+ url: request.url,
25459
+ protocols: request.protocols,
25460
+ headers: request.headers
25461
+ });
25462
+ } finally {
25463
+ signal.removeEventListener("abort", abort);
25464
+ }
25465
+ const close = (code, reason) => this.port.postMessage({ net: "ws-close", nid, code, reason });
25466
+ const queue = new MessageQueue(
25467
+ () => close(1e3, "a message is too big"),
25468
+ Number.POSITIVE_INFINITY,
25469
+ (n) => this.port.postMessage({ net: "ws-consumed", nid, n })
25470
+ );
25471
+ let unacked = 0;
25472
+ this.sockets.set(nid, { queue, acked: (n) => void (unacked -= n) });
25473
+ return {
25474
+ protocol: opened2.protocol,
25475
+ get buffered() {
25476
+ return unacked;
25477
+ },
25478
+ send: (data) => {
25479
+ unacked += payloadSize(data);
25480
+ this.port.postMessage({ net: "ws-send", nid, data });
25481
+ },
25482
+ messages: queue,
25483
+ close,
25484
+ closed: queue.closed
25485
+ };
25486
+ }
25487
+ };
25488
+
25182
25489
  // src/kernel/net/redirect-hops.ts
25183
25490
  var HELD_MS = 3e4;
25184
25491
  var HELD_MAX2 = 64;
@@ -25254,7 +25561,10 @@ function replayingHops(inner, heldMs = HELD_MS) {
25254
25561
  return { ...inner.traits, manualRedirects: true };
25255
25562
  },
25256
25563
  ...inner.websocket ? { websocket: inner.websocket.bind(inner) } : {},
25257
- async fetch(request, owner = KERNEL) {
25564
+ async fetch(request, ...rest2) {
25565
+ if (unfollowed2(request.redirect)) return inner.fetch(request, ...rest2);
25566
+ const [caller] = rest2;
25567
+ const owner = caller ?? KERNEL;
25258
25568
  const key = keyOf3(owner, request.method, request.url);
25259
25569
  const waiting = held.get(key);
25260
25570
  if (waiting && waiting.fingerprint === fingerprint(request.headers)) {
@@ -25263,7 +25573,7 @@ function replayingHops(inner, heldMs = HELD_MS) {
25263
25573
  return replay(waiting);
25264
25574
  }
25265
25575
  if (waiting) drop(key);
25266
- const response = await inner.fetch(request);
25576
+ const response = await inner.fetch(request, ...rest2);
25267
25577
  if (!response.redirects?.length) return response;
25268
25578
  return replay({
25269
25579
  owner,
@@ -25448,11 +25758,11 @@ var PackageLocks = class {
25448
25758
  if (!locks) return Promise.resolve(() => {
25449
25759
  });
25450
25760
  const { promise: held, resolve: release } = Promise.withResolvers();
25451
- return new Promise((granted2, failed3) => {
25761
+ return new Promise((granted2, failed4) => {
25452
25762
  locks.request(replacingLock(this.metadata, root), { mode: "exclusive" }, () => {
25453
25763
  granted2(() => release());
25454
25764
  return held;
25455
- }).catch(failed3);
25765
+ }).catch(failed4);
25456
25766
  });
25457
25767
  }
25458
25768
  };
@@ -25702,9 +26012,9 @@ function serveFilesystem(port, handlers, capabilities = {}, options = {}) {
25702
26012
  let open = false;
25703
26013
  const reply = (message3, transfer = []) => port.postMessage(message3, transfer);
25704
26014
  async function hello(message3) {
25705
- const refused3 = driverVersionError(message3.protocol);
25706
- if (refused3) {
25707
- reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused3, errno: "EPROTO" } });
26015
+ const refused4 = driverVersionError(message3.protocol);
26016
+ if (refused4) {
26017
+ reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused4, errno: "EPROTO" } });
25708
26018
  return;
25709
26019
  }
25710
26020
  try {
@@ -27946,12 +28256,12 @@ var OpfsFs = class {
27946
28256
  if (!move) return false;
27947
28257
  for (let attempt = 0; ; attempt++) {
27948
28258
  this.others++;
27949
- const failed3 = await move.call(handle, dir, name2).then(
28259
+ const failed4 = await move.call(handle, dir, name2).then(
27950
28260
  () => void 0,
27951
28261
  (err) => ({ err })
27952
28262
  );
27953
- if (!failed3) return true;
27954
- const error = failed3.err?.name;
28263
+ if (!failed4) return true;
28264
+ const error = failed4.err?.name;
27955
28265
  if (handle.kind !== "file" || !LOCKED.has(error)) return false;
27956
28266
  const delay2 = SYNC_BACKOFF_MS[attempt];
27957
28267
  if (delay2 === void 0) throw fsError("EBUSY", name2);
@@ -28514,22 +28824,22 @@ async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discar
28514
28824
  );
28515
28825
  }
28516
28826
  async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
28517
- let failed3 = invalid;
28827
+ let failed4 = invalid;
28518
28828
  for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
28519
28829
  try {
28520
28830
  const entry = await mount(spec);
28521
28831
  if (!signal.aborted) return { line, target: spec.target, entry };
28522
28832
  discard(entry);
28523
- failed3 = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
28833
+ failed4 = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
28524
28834
  break;
28525
28835
  } catch (err) {
28526
- failed3 = err;
28836
+ failed4 = err;
28527
28837
  if (FINAL.has(codeOf6(err)) || attempt === retries.length) break;
28528
28838
  await wait(retries[attempt], signal);
28529
28839
  }
28530
28840
  }
28531
- const code = codeOf6(failed3);
28532
- return { line, target: spec.target, code, error: String(failed3?.message ?? code) };
28841
+ const code = codeOf6(failed4);
28842
+ return { line, target: spec.target, code, error: String(failed4?.message ?? code) };
28533
28843
  }
28534
28844
 
28535
28845
  // src/mount/hostfs.ts
@@ -30990,7 +31300,7 @@ var Launcher = class {
30990
31300
  ...options.env
30991
31301
  };
30992
31302
  this.ca = kernelCa(options.caStore ?? memoryCaStore());
30993
- this.transport = replayingHops(options.transport ?? missingTransport());
31303
+ this.transport = placeholderFloor(replayingHops(options.transport ?? missingTransport()));
30994
31304
  this.announcedTraits = JSON.stringify(this.transport.traits);
30995
31305
  this.uplink = options.uplink;
30996
31306
  this.routes = new Routes(options.uplink?.traits.ipv6 === true);
@@ -31145,16 +31455,16 @@ var Launcher = class {
31145
31455
  if (!found) {
31146
31456
  found = this.swaps.settled().then(() => this.trustedAppended()).then(async (appended2) => {
31147
31457
  const tiers = await this.tiers(scope, appended2);
31148
- const refused3 = [];
31458
+ const refused4 = [];
31149
31459
  const commands = await scanCommands(
31150
31460
  this.scanned,
31151
31461
  tiers,
31152
31462
  tiers.length - appended2.length,
31153
31463
  void 0,
31154
- refused3,
31464
+ refused4,
31155
31465
  await this.globalRoots(scope)
31156
31466
  );
31157
- this.refusedOf.set(commands, refused3.map(refusing));
31467
+ this.refusedOf.set(commands, refused4.map(refusing));
31158
31468
  return commands;
31159
31469
  }).then(
31160
31470
  (commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND)
@@ -31360,14 +31670,14 @@ var Launcher = class {
31360
31670
  return await this.commandAt(path, scope) ?? await this.refusedAt(path, name2, scope);
31361
31671
  }
31362
31672
  async refusedAt(path, name2, scope) {
31363
- const refused3 = this.refusedOf.get(await this.commands(scope)) ?? [];
31364
- if (refused3.length === 0) return void 0;
31673
+ const refused4 = this.refusedOf.get(await this.commands(scope)) ?? [];
31674
+ if (refused4.length === 0) return void 0;
31365
31675
  const st = await this.base.stat(path).catch(() => void 0);
31366
31676
  if (!st?.isFile) return void 0;
31367
31677
  const real = await physicalPathAsync(this.base, path).catch(() => path);
31368
31678
  const same2 = async (own) => own === path || await physicalPathAsync(this.base, own).catch(() => own) === real;
31369
31679
  const owners = [];
31370
- for (const c of refused3) {
31680
+ for (const c of refused4) {
31371
31681
  const origin = c.origin;
31372
31682
  for (const own of [origin.glue, origin.wasm, ...origin.variants ?? []]) {
31373
31683
  if (!await same2(own)) continue;
@@ -31377,11 +31687,11 @@ var Launcher = class {
31377
31687
  }
31378
31688
  const stem = name2.replace(/\.wasm$/, "");
31379
31689
  const named = owners.find((c) => c.name === name2) ?? owners.find((c) => c.name === stem);
31380
- return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused3);
31690
+ return named ?? owners[0] ?? await this.refusedCopy(real, name2, refused4);
31381
31691
  }
31382
- async refusedCopy(real, name2, refused3) {
31692
+ async refusedCopy(real, name2, refused4) {
31383
31693
  const dir = real.slice(0, real.lastIndexOf("/"));
31384
- for (const c of refused3) {
31694
+ for (const c of refused4) {
31385
31695
  const origin = c.origin;
31386
31696
  if (origin.abi !== "emscripten" || origin.script || c.name !== name2) continue;
31387
31697
  const own = await physicalPathAsync(this.base, origin.glue).catch(() => origin.glue);
@@ -31589,12 +31899,12 @@ var Launcher = class {
31589
31899
  return void 0;
31590
31900
  }
31591
31901
  async planOne(req, { ppid, cred, scope }, catalogOnly = false) {
31592
- const refused3 = (err) => {
31902
+ const refused4 = (err) => {
31593
31903
  throw codeOf8(err) === "EACCES" ? new SpawnError("EACCES") : err;
31594
31904
  };
31595
- const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused3);
31905
+ const file = await this.execFile(req.file, req.cwd, this.execGuard(cred, ppid)).catch(refused4);
31596
31906
  const planned = await this.plan(file, req.argv, req.cwd, scope, req.argv0, catalogOnly);
31597
- await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused3);
31907
+ await this.mayExec(cred, { ...req, file, pid: ppid }, planned).catch(refused4);
31598
31908
  if (planned) return planned;
31599
31909
  throw new SpawnError(catalogOnly ? "ENOENT" : await this.unrunnable(file, req.cwd, scope));
31600
31910
  }
@@ -32882,8 +33192,8 @@ var Launcher = class {
32882
33192
  for (const name2 of names2 ?? []) {
32883
33193
  const path = joinName(real, name2);
32884
33194
  if (await this.base.hasOwner(path)) continue;
32885
- const failed3 = this.inUse(path) ? "busy" : await this.base.rm(path, { recursive: true, force: true }).catch(String);
32886
- if (failed3 === void 0) removed++;
33195
+ const failed4 = this.inUse(path) ? "busy" : await this.base.rm(path, { recursive: true, force: true }).catch(String);
33196
+ if (failed4 === void 0) removed++;
32887
33197
  else left++;
32888
33198
  }
32889
33199
  if (removed > 0) {
@@ -33676,10 +33986,11 @@ function fetchTransport(options = {}) {
33676
33986
  const send2 = options.fetch ?? globalThis.fetch.bind(globalThis);
33677
33987
  const Socket = options.webSocket === true ? globalThis.WebSocket : options.webSocket || void 0;
33678
33988
  const headers = options.webSocketHeaders === true;
33679
- const redirect = options.redirect ?? "follow";
33989
+ const instance = options.redirect ?? "follow";
33680
33990
  return {
33681
33991
  traits: {
33682
- manualRedirects: redirect === "manual",
33992
+ manualRedirects: instance === "manual",
33993
+ redirectPolicy: true,
33683
33994
  encodedBodies: false,
33684
33995
  maxRequestBody: options.maxRequestBody ?? MAX_REQUEST_BODY,
33685
33996
  crossOrigin: "cors",
@@ -33695,7 +34006,7 @@ function fetchTransport(options = {}) {
33695
34006
  headers: joinHeaders(request.headers),
33696
34007
  ...requestBody(request),
33697
34008
  signal: request.signal,
33698
- redirect,
34009
+ redirect: request.redirect ?? instance,
33699
34010
  credentials: "omit",
33700
34011
  mode: "cors"
33701
34012
  });
@@ -33719,6 +34030,7 @@ function fetchTransport(options = {}) {
33719
34030
  const body = chunks(response.body, options.bodyIdleMs ?? BODY_IDLE_MS);
33720
34031
  return {
33721
34032
  ...response.redirected ? { url: response.url, redirected: true } : {},
34033
+ ...response.type === "opaqueredirect" ? { opaqueRedirect: true } : {},
33722
34034
  status: response.status,
33723
34035
  statusText: response.statusText,
33724
34036
  headers: headers2,
@@ -33754,6 +34066,19 @@ function uploadsFor(peer) {
33754
34066
  });
33755
34067
  return { of, answer: (call) => pulls.get(call.nid)?.(call) };
33756
34068
  }
34069
+ function headOf(nid, response) {
34070
+ return {
34071
+ net: "head",
34072
+ nid,
34073
+ ...response.url !== void 0 ? { url: response.url } : {},
34074
+ ...response.redirected !== void 0 ? { redirected: response.redirected } : {},
34075
+ ...response.redirects ? { redirects: response.redirects } : {},
34076
+ ...response.opaqueRedirect ? { opaqueRedirect: true } : {},
34077
+ status: response.status,
34078
+ statusText: response.statusText,
34079
+ headers: response.headers
34080
+ };
34081
+ }
33757
34082
  function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS3 } = {}) {
33758
34083
  const open = /* @__PURE__ */ new Map();
33759
34084
  const fail3 = (nid, e) => {
@@ -33779,6 +34104,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS3 } = {})
33779
34104
  method: call.method,
33780
34105
  headers: call.headers,
33781
34106
  ...call.streamed ? { bodyStream: uploads.of(call.nid, abort.signal) } : call.body ? { body: call.body } : {},
34107
+ ...call.redirect !== void 0 ? { redirect: call.redirect } : {},
33782
34108
  signal: abort.signal
33783
34109
  });
33784
34110
  if (!open.has(call.nid)) {
@@ -33786,15 +34112,7 @@ function serveTransport(peer, transport, { closeWaitMs = CLOSE_WAIT_MS3 } = {})
33786
34112
  return;
33787
34113
  }
33788
34114
  open.set(call.nid, { abort, body: response.body[Symbol.asyncIterator](), response });
33789
- peer.postMessage({
33790
- net: "head",
33791
- nid: call.nid,
33792
- ...response.redirected ? { url: response.url, redirected: true } : {},
33793
- ...response.redirects ? { redirects: response.redirects } : {},
33794
- status: response.status,
33795
- statusText: response.statusText,
33796
- headers: response.headers
33797
- });
34115
+ peer.postMessage(headOf(call.nid, response));
33798
34116
  } catch (e) {
33799
34117
  fail3(call.nid, e);
33800
34118
  }
@@ -34294,9 +34612,9 @@ function serveClient(port, host) {
34294
34612
  ended.resolve();
34295
34613
  }
34296
34614
  async function hello(message3) {
34297
- const refused3 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
34298
- if (refused3) {
34299
- send2({ hello: { protocol: PROTOCOL, error: refused3 } });
34615
+ const refused4 = versionError(message3.protocol) ?? await hosts.requests.accept(host, message3);
34616
+ if (refused4) {
34617
+ send2({ hello: { protocol: PROTOCOL, error: refused4 } });
34300
34618
  detach();
34301
34619
  return;
34302
34620
  }
@@ -34440,6 +34758,60 @@ import { AsyncLocalStorage } from "node:async_hooks";
34440
34758
  import { subscribe } from "node:diagnostics_channel";
34441
34759
  import { lookup as lookup2 } from "node:dns/promises";
34442
34760
  import { isIP as isIP3 } from "node:net";
34761
+
34762
+ // src/node/follow.ts
34763
+ var MAX_REDIRECTS = 20;
34764
+ var REDIRECTS = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
34765
+ var BODY_HEADERS = /* @__PURE__ */ new Set([
34766
+ "content-encoding",
34767
+ "content-language",
34768
+ "content-length",
34769
+ "content-location",
34770
+ "content-type"
34771
+ ]);
34772
+ var CREDENTIALS = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2"]);
34773
+ function failed3(message3) {
34774
+ return Object.assign(new Error(message3), { status: 502, detail: "transport" });
34775
+ }
34776
+ function locationOf(headers) {
34777
+ return headers.find(([name2]) => name2.toLowerCase() === "location")?.[1];
34778
+ }
34779
+ function nextHop(hop, status, location) {
34780
+ if (!URL.canParse(location, hop.url)) throw failed3(`a bad redirect from ${hop.url}`);
34781
+ const next3 = new URL(location, hop.url);
34782
+ if (next3.protocol !== "http:" && next3.protocol !== "https:") {
34783
+ throw failed3(`a redirect from ${hop.url} to ${next3.protocol}`);
34784
+ }
34785
+ next3.hash = "";
34786
+ const toGet = (status === 301 || status === 302) && hop.method === "POST" || status === 303 && hop.method !== "GET" && hop.method !== "HEAD";
34787
+ if (hop.bodyStream && !toGet) throw failed3(`a ${status} redirect would resend a streamed body`);
34788
+ const crossOrigin = next3.origin !== new URL(hop.url).origin;
34789
+ const { body, bodyStream: bodyStream2, ...rest2 } = hop;
34790
+ return {
34791
+ ...rest2,
34792
+ ...body && !toGet ? { body } : {},
34793
+ url: next3.href,
34794
+ method: toGet ? "GET" : hop.method,
34795
+ headers: hop.headers.filter(
34796
+ ([name2]) => !(toGet && BODY_HEADERS.has(name2.toLowerCase())) && !(crossOrigin && CREDENTIALS.has(name2.toLowerCase()))
34797
+ )
34798
+ };
34799
+ }
34800
+ async function followRedirects(send2, request) {
34801
+ let hop = { ...request, redirect: "manual" };
34802
+ for (let hops = 0; ; hops++) {
34803
+ const response = await send2(hop);
34804
+ const location = locationOf(response.headers);
34805
+ if (!REDIRECTS.has(response.status) || location === void 0) {
34806
+ return hops > 0 ? { ...response, url: hop.url, redirected: true } : response;
34807
+ }
34808
+ await response.cancel();
34809
+ if (hops >= MAX_REDIRECTS) throw failed3(`too many redirects from ${request.url}`);
34810
+ hop = nextHop(hop, response.status, location);
34811
+ }
34812
+ }
34813
+
34814
+ // src/node/loopback.ts
34443
34815
  var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
34444
34816
  var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
34445
34817
  var resolveHost = async (host) => (await lookup2(host, { all: true, verbatim: true })).map((a) => a.address);
@@ -34604,19 +34976,20 @@ function ensureSubscribed() {
34604
34976
  function guardedLoopback(transport, policy, resolve2) {
34605
34977
  ensureSubscribed();
34606
34978
  const open = transport.websocket?.bind(transport);
34979
+ const send2 = async (request, owner) => {
34980
+ await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
34981
+ const entry = { policy };
34982
+ try {
34983
+ return await loopbackStore.run(entry, () => transport.fetch(request, owner));
34984
+ } catch (err) {
34985
+ throw entry.refused ?? err;
34986
+ }
34987
+ };
34607
34988
  return {
34608
34989
  get traits() {
34609
34990
  return transport.traits;
34610
34991
  },
34611
- fetch: async (request, owner) => {
34612
- await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
34613
- const entry = { policy };
34614
- try {
34615
- return await loopbackStore.run(entry, () => transport.fetch(request, owner));
34616
- } catch (err) {
34617
- throw entry.refused ?? err;
34618
- }
34619
- },
34992
+ fetch: (request, owner) => request.redirect === "follow" ? followRedirects((hop) => send2(hop, owner), request) : send2(request, owner),
34620
34993
  ...open ? {
34621
34994
  websocket: async (request) => {
34622
34995
  await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
@@ -35002,212 +35375,6 @@ function diagnosing(transport, check = byDefault) {
35002
35375
  };
35003
35376
  }
35004
35377
 
35005
- // src/kernel/net/remote-transport.ts
35006
- var TransportError = class extends Error {
35007
- status;
35008
- code;
35009
- detail;
35010
- constructor(message3, status, code, detail) {
35011
- super(message3);
35012
- this.status = status;
35013
- this.code = code;
35014
- this.detail = detail === "transport" ? detail : void 0;
35015
- }
35016
- };
35017
- var RemoteTransport = class {
35018
- traits;
35019
- port;
35020
- waiting = /* @__PURE__ */ new Map();
35021
- nextId = 0;
35022
- sockets = /* @__PURE__ */ new Map();
35023
- uploads = /* @__PURE__ */ new Map();
35024
- failure;
35025
- constructor(port, traits) {
35026
- this.port = port;
35027
- this.traits = traits;
35028
- }
35029
- receive(reply) {
35030
- if (reply.net === "body-pull") {
35031
- void this.pull(reply.nid);
35032
- return;
35033
- }
35034
- const socket = this.sockets.get(reply.nid);
35035
- if (socket && this.toSocket(reply, socket)) return;
35036
- const waiter = this.waiting.get(reply.nid);
35037
- if (!waiter) return;
35038
- this.waiting.delete(reply.nid);
35039
- if (reply.net === "error") {
35040
- waiter.reject(new TransportError(reply.message, reply.status, reply.code, reply.detail));
35041
- } else waiter.resolve(reply);
35042
- }
35043
- toSocket(reply, socket) {
35044
- if (reply.net === "ws-message") socket.queue.push(reply.data);
35045
- else if (reply.net === "ws-acked") socket.acked(reply.n);
35046
- else if (reply.net === "ws-closed") {
35047
- this.sockets.delete(reply.nid);
35048
- socket.queue.end({ code: reply.code, reason: reply.reason });
35049
- } else return false;
35050
- return true;
35051
- }
35052
- async pull(nid) {
35053
- const upload = this.uploads.get(nid);
35054
- if (!upload) {
35055
- this.port.postMessage({ net: "body-error", nid, message: "the request body is gone" });
35056
- return;
35057
- }
35058
- try {
35059
- const next3 = await upload.next();
35060
- if (next3.done) {
35061
- this.uploads.delete(nid);
35062
- this.port.postMessage({ net: "body-end", nid });
35063
- } else this.port.postMessage({ net: "body-chunk", nid, bytes: next3.value });
35064
- } catch (e) {
35065
- this.uploads.delete(nid);
35066
- this.port.postMessage({
35067
- net: "body-error",
35068
- nid,
35069
- message: e instanceof Error ? e.message : String(e)
35070
- });
35071
- }
35072
- }
35073
- dropUpload(nid) {
35074
- const upload = this.uploads.get(nid);
35075
- this.uploads.delete(nid);
35076
- void upload?.return?.().catch(() => void 0);
35077
- }
35078
- fail(error) {
35079
- this.failure = error;
35080
- for (const nid of [...this.uploads.keys()]) this.dropUpload(nid);
35081
- for (const waiter of this.waiting.values()) waiter.reject(error);
35082
- this.waiting.clear();
35083
- for (const socket of this.sockets.values())
35084
- socket.queue.end({ code: 1011, reason: error.message });
35085
- this.sockets.clear();
35086
- }
35087
- ask(call) {
35088
- if (this.failure) return Promise.reject(this.failure);
35089
- return new Promise((resolve2, reject) => {
35090
- this.waiting.set(call.nid, { resolve: resolve2, reject });
35091
- this.port.postMessage(call);
35092
- });
35093
- }
35094
- cancel(nid) {
35095
- const waiter = this.waiting.get(nid);
35096
- this.waiting.delete(nid);
35097
- waiter?.reject(new TransportError("the request was cancelled"));
35098
- this.port.postMessage({ net: "cancel", nid });
35099
- }
35100
- async fetch(request) {
35101
- const nid = ++this.nextId;
35102
- const { signal } = request;
35103
- signal.throwIfAborted();
35104
- let finished = false;
35105
- const abort = () => {
35106
- finished = true;
35107
- this.dropUpload(nid);
35108
- this.cancel(nid);
35109
- };
35110
- signal.addEventListener("abort", abort, { once: true });
35111
- const finish = () => {
35112
- this.dropUpload(nid);
35113
- if (finished) return;
35114
- finished = true;
35115
- signal.removeEventListener("abort", abort);
35116
- };
35117
- if (request.bodyStream) this.uploads.set(nid, request.bodyStream[Symbol.asyncIterator]());
35118
- let head;
35119
- try {
35120
- head = await this.ask({
35121
- net: "fetch",
35122
- nid,
35123
- url: request.url,
35124
- method: request.method,
35125
- headers: request.headers,
35126
- ...request.bodyStream ? { streamed: true } : request.body ? { body: request.body } : {}
35127
- });
35128
- } catch (e) {
35129
- finish();
35130
- throw e;
35131
- }
35132
- const { status, statusText, headers, url, redirected: redirected2, redirects } = head;
35133
- const read = async () => {
35134
- if (finished) return void 0;
35135
- const reply = await this.ask({ net: "read", nid }).catch((e) => {
35136
- finish();
35137
- throw e;
35138
- });
35139
- if (reply.net === "chunk") return reply.bytes;
35140
- finish();
35141
- return void 0;
35142
- };
35143
- const cancel = async () => {
35144
- if (finished) return;
35145
- finish();
35146
- this.cancel(nid);
35147
- };
35148
- return {
35149
- ...redirected2 ? { url, redirected: redirected2 } : {},
35150
- ...redirects ? { redirects } : {},
35151
- status,
35152
- statusText,
35153
- headers,
35154
- cancel,
35155
- body: {
35156
- [Symbol.asyncIterator]: () => ({
35157
- next: async () => {
35158
- const bytes = await read();
35159
- return bytes ? { value: bytes, done: false } : { value: void 0, done: true };
35160
- },
35161
- return: async () => {
35162
- await cancel();
35163
- return { value: void 0, done: true };
35164
- }
35165
- })
35166
- }
35167
- };
35168
- }
35169
- async websocket(request) {
35170
- const nid = ++this.nextId;
35171
- const { signal } = request;
35172
- signal.throwIfAborted();
35173
- const abort = () => this.cancel(nid);
35174
- signal.addEventListener("abort", abort, { once: true });
35175
- let opened2;
35176
- try {
35177
- opened2 = await this.ask({
35178
- net: "ws-open",
35179
- nid,
35180
- url: request.url,
35181
- protocols: request.protocols,
35182
- headers: request.headers
35183
- });
35184
- } finally {
35185
- signal.removeEventListener("abort", abort);
35186
- }
35187
- const close = (code, reason) => this.port.postMessage({ net: "ws-close", nid, code, reason });
35188
- const queue = new MessageQueue(
35189
- () => close(1e3, "a message is too big"),
35190
- Number.POSITIVE_INFINITY,
35191
- (n) => this.port.postMessage({ net: "ws-consumed", nid, n })
35192
- );
35193
- let unacked = 0;
35194
- this.sockets.set(nid, { queue, acked: (n) => void (unacked -= n) });
35195
- return {
35196
- protocol: opened2.protocol,
35197
- get buffered() {
35198
- return unacked;
35199
- },
35200
- send: (data) => {
35201
- unacked += payloadSize(data);
35202
- this.port.postMessage({ net: "ws-send", nid, data });
35203
- },
35204
- messages: queue,
35205
- close,
35206
- closed: queue.closed
35207
- };
35208
- }
35209
- };
35210
-
35211
35378
  // src/serve.ts
35212
35379
  function signalNumber(name2) {
35213
35380
  const key = name2.slice(3);
@@ -35841,9 +36008,9 @@ async function greet(channel, lock, timeoutMs, requests) {
35841
36008
  } finally {
35842
36009
  clearTimeout(timer);
35843
36010
  }
35844
- const refused3 = hello.error ?? versionError(hello.protocol);
35845
- if (refused3) {
35846
- const error = new Error(refused3);
36011
+ const refused4 = hello.error ?? versionError(hello.protocol);
36012
+ if (refused4) {
36013
+ const error = new Error(refused4);
35847
36014
  channel.end(error);
35848
36015
  throw error;
35849
36016
  }