@ai-ecoverse/slicc-kernel 1.172.2 → 1.173.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,149 @@
1
+ // src/fs/meta-reads.ts
2
+ var parentOf = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
3
+ var within = (path, root) => root === "/" || path === root || path.startsWith(`${root}/`);
4
+ var Invalidations = class {
5
+ seq = 0;
6
+ log = [];
7
+ open = /* @__PURE__ */ new Map();
8
+ start() {
9
+ const at = this.seq;
10
+ this.open.set(at, (this.open.get(at) ?? 0) + 1);
11
+ return at;
12
+ }
13
+ end(at) {
14
+ const left = (this.open.get(at) ?? 1) - 1;
15
+ if (left > 0) this.open.set(at, left);
16
+ else this.open.delete(at);
17
+ const oldest = this.open.keys().next();
18
+ if (oldest.done) this.log.length = 0;
19
+ else while (this.log.length > 0 && this.log[0].at <= oldest.value) this.log.shift();
20
+ }
21
+ note({ paths = [], trees = [] }) {
22
+ this.seq++;
23
+ if (this.open.size > 0) this.log.push({ at: this.seq, paths, trees });
24
+ }
25
+ since(at, hit) {
26
+ for (let i = this.log.length - 1; i >= 0; i--) {
27
+ const noted2 = this.log[i];
28
+ if (noted2.at <= at) return false;
29
+ if (hit(noted2)) return true;
30
+ }
31
+ return false;
32
+ }
33
+ entryStale(at, path) {
34
+ return this.since(
35
+ at,
36
+ ({ paths, trees }) => paths.includes(path) || trees.some((tree2) => within(path, tree2))
37
+ );
38
+ }
39
+ listingStale(at, dir) {
40
+ return this.since(
41
+ at,
42
+ ({ paths, trees }) => paths.some((path) => path !== "/" && parentOf(path) === dir) || trees.some((tree2) => within(dir, tree2) || tree2 !== "/" && parentOf(tree2) === dir)
43
+ );
44
+ }
45
+ };
46
+ var META_READS_IN_FLIGHT = 4;
47
+ var BatchedReads = class {
48
+ run;
49
+ invalidations;
50
+ keep;
51
+ queued = {
52
+ entry: /* @__PURE__ */ new Map(),
53
+ links: /* @__PURE__ */ new Map()
54
+ };
55
+ reading = {
56
+ entry: /* @__PURE__ */ new Map(),
57
+ links: /* @__PURE__ */ new Map()
58
+ };
59
+ inFlight = 0;
60
+ constructor(invalidations, run2, keep2) {
61
+ this.invalidations = invalidations;
62
+ this.run = run2;
63
+ this.keep = keep2;
64
+ }
65
+ entries(paths) {
66
+ const wanted = paths.map((path) => [path, this.want("entry", path)]);
67
+ this.pump();
68
+ return Promise.all(wanted.map(([, w]) => w.promise)).then(
69
+ (found) => new Map(wanted.map(([path], i) => [path, found[i]]))
70
+ );
71
+ }
72
+ links(dir) {
73
+ const wanted = this.want("links", dir);
74
+ this.pump();
75
+ return wanted.promise;
76
+ }
77
+ stale(kind, key, at) {
78
+ return kind === "entry" ? this.invalidations.entryStale(at, key) : this.invalidations.listingStale(at, key);
79
+ }
80
+ want(kind, key) {
81
+ const reading = this.reading[kind].get(key);
82
+ if (reading && !this.stale(kind, key, reading.at)) return reading;
83
+ const queued = this.queued[kind].get(key);
84
+ if (queued) return queued;
85
+ const made2 = Promise.withResolvers();
86
+ made2.promise.catch(() => void 0);
87
+ this.queued[kind].set(key, made2);
88
+ return made2;
89
+ }
90
+ pump() {
91
+ while (this.inFlight < META_READS_IN_FLIGHT && (this.queued.entry.size > 0 || this.queued.links.size > 0)) {
92
+ this.flush();
93
+ }
94
+ }
95
+ flush() {
96
+ const entries = this.queued.entry;
97
+ const links = this.queued.links;
98
+ this.queued.entry = /* @__PURE__ */ new Map();
99
+ this.queued.links = /* @__PURE__ */ new Map();
100
+ const at = this.invalidations.start();
101
+ for (const [kind, taken] of [
102
+ ["entry", entries],
103
+ ["links", links]
104
+ ]) {
105
+ for (const [key, wanted] of taken) {
106
+ wanted.at = at;
107
+ this.reading[kind].set(key, wanted);
108
+ }
109
+ }
110
+ this.inFlight++;
111
+ const settled = (result, error) => {
112
+ this.inFlight--;
113
+ for (const [kind, taken, found] of [
114
+ ["entry", entries, result?.entries],
115
+ ["links", links, result?.links]
116
+ ]) {
117
+ for (const [key, wanted] of taken) {
118
+ if (this.reading[kind].get(key) === wanted) this.reading[kind].delete(key);
119
+ if (!found) {
120
+ wanted.reject(error);
121
+ continue;
122
+ }
123
+ const value2 = found.get(key) ?? (kind === "entry" ? null : []);
124
+ if (!this.stale(kind, key, at)) this.keep(kind, key, value2);
125
+ wanted.resolve(value2);
126
+ }
127
+ }
128
+ this.invalidations.end(at);
129
+ this.pump();
130
+ };
131
+ let reading;
132
+ try {
133
+ reading = this.run({ entries: [...entries.keys()], links: [...links.keys()] });
134
+ } catch (error) {
135
+ settled(void 0, error);
136
+ return;
137
+ }
138
+ reading.then(
139
+ (result) => settled(result),
140
+ (error) => settled(void 0, error)
141
+ );
142
+ }
143
+ };
144
+
1
145
  // src/fs/path-map.ts
2
- function parentOf(path) {
146
+ function parentOf2(path) {
3
147
  if (path === "/" || path === "") return void 0;
4
148
  const at = path.lastIndexOf("/");
5
149
  return at <= 0 ? "/" : path.slice(0, at);
@@ -57,24 +201,24 @@ var PathMap = class {
57
201
  this.children.clear();
58
202
  }
59
203
  link(path) {
60
- for (let child = path, parent = parentOf(path); parent !== void 0; ) {
204
+ for (let child = path, parent = parentOf2(path); parent !== void 0; ) {
61
205
  const set3 = this.children.get(parent);
62
206
  if (set3?.has(child)) return;
63
207
  if (set3) set3.add(child);
64
208
  else this.children.set(parent, /* @__PURE__ */ new Set([child]));
65
209
  child = parent;
66
- parent = parentOf(parent);
210
+ parent = parentOf2(parent);
67
211
  }
68
212
  }
69
213
  prune(path) {
70
- for (let child = path, parent = parentOf(path); parent !== void 0; ) {
214
+ for (let child = path, parent = parentOf2(path); parent !== void 0; ) {
71
215
  if (this.values.has(child) || this.children.has(child)) return;
72
216
  const set3 = this.children.get(parent);
73
217
  set3?.delete(child);
74
218
  if (set3 && set3.size > 0) return;
75
219
  this.children.delete(parent);
76
220
  child = parent;
77
- parent = parentOf(parent);
221
+ parent = parentOf2(parent);
78
222
  }
79
223
  }
80
224
  };
@@ -83,11 +227,11 @@ var PathMap = class {
83
227
  var META_DB = "slicc-kernel";
84
228
  var META_VERSION = 1;
85
229
  var META_STORE = "entries";
86
- function parentOf2(path) {
230
+ function parentOf3(path) {
87
231
  return path.slice(0, path.lastIndexOf("/")) || "/";
88
232
  }
89
233
  function moved(entry, path) {
90
- return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf2(path) };
234
+ return entry.link === void 0 ? { ...entry, path } : { ...entry, path, dir: parentOf3(path) };
91
235
  }
92
236
  function under(path, root) {
93
237
  return path === root || path.startsWith(`${root}/`);
@@ -146,11 +290,21 @@ function settle(transaction, value2) {
146
290
  }
147
291
  var REMOVE_BATCH = 256;
148
292
  var REMOVE_WAIT_MS = 5;
293
+ var LISTINGS_KEPT = 4096;
149
294
  var IndexedDbMeta = class {
150
295
  db;
151
296
  cache = new PathMap();
297
+ listings = new PathMap();
152
298
  channel;
153
- generation = 0;
299
+ invalidations = new Invalidations();
300
+ reads = new BatchedReads(
301
+ this.invalidations,
302
+ (batch) => this.read(batch),
303
+ (kind, key, value2) => {
304
+ if (kind === "entry") this.cache.set(key, value2);
305
+ else this.keepListing(key, value2);
306
+ }
307
+ );
154
308
  listeners = /* @__PURE__ */ new Set();
155
309
  listen(listener) {
156
310
  this.listeners.add(listener);
@@ -169,32 +323,56 @@ var IndexedDbMeta = class {
169
323
  this.drop({ trees: [...trees] });
170
324
  }
171
325
  drop({ paths = [], trees = [] }) {
172
- this.generation++;
173
- for (const path of paths) this.cache.delete(path);
174
- for (const tree2 of trees) this.cache.deleteTree(tree2);
326
+ this.invalidations.note({ paths, trees });
327
+ for (const path of paths) {
328
+ this.cache.delete(path);
329
+ if (path !== "/") this.listings.delete(parentOf3(path));
330
+ }
331
+ for (const tree2 of trees) {
332
+ this.cache.deleteTree(tree2);
333
+ this.listings.deleteTree(tree2);
334
+ if (tree2 !== "/") this.listings.delete(parentOf3(tree2));
335
+ }
336
+ }
337
+ keepListing(dir, links) {
338
+ if (this.listings.size >= LISTINGS_KEPT) this.listings.clear();
339
+ this.listings.set(dir, links);
340
+ }
341
+ async read({ entries, links }) {
342
+ this.settleRemoves();
343
+ const [transaction, store] = this.transaction("readonly");
344
+ const result = { entries: /* @__PURE__ */ new Map(), links: /* @__PURE__ */ new Map() };
345
+ for (const path of entries) {
346
+ const request3 = store.get(path);
347
+ request3.onsuccess = () => void result.entries.set(path, request3.result ?? null);
348
+ }
349
+ if (links.length > 0) {
350
+ const index = store.index("dir");
351
+ for (const dir of links) {
352
+ const request3 = index.getAll(dir);
353
+ request3.onsuccess = () => void result.links.set(dir, request3.result);
354
+ }
355
+ }
356
+ return settle(transaction, () => result);
175
357
  }
176
358
  changed(invalidation2) {
177
359
  this.drop(invalidation2);
178
360
  this.channel.postMessage(invalidation2);
179
361
  }
362
+ counted = { reads: 0, writes: 0 };
363
+ transactions() {
364
+ return { ...this.counted };
365
+ }
180
366
  transaction(mode) {
367
+ if (mode === "readonly") this.counted.reads++;
368
+ else this.counted.writes++;
181
369
  const transaction = this.db.transaction(META_STORE, mode);
182
370
  return [transaction, transaction.objectStore(META_STORE)];
183
371
  }
184
372
  async get(paths) {
185
373
  const missing6 = paths.filter((path) => !this.cache.has(path));
186
374
  if (missing6.length > 0) {
187
- this.settleRemoves();
188
- const generation = this.generation;
189
- const [transaction, store] = this.transaction("readonly");
190
- const found = /* @__PURE__ */ new Map();
191
- for (const path of missing6) {
192
- const request3 = store.get(path);
193
- request3.onsuccess = () => void found.set(path, request3.result ?? null);
194
- }
195
- await settle(transaction, () => void 0);
196
- if (generation === this.generation)
197
- for (const [path, entry] of found) this.cache.set(path, entry);
375
+ const found = await this.reads.entries(missing6);
198
376
  return paths.map(
199
377
  (path) => (found.has(path) ? found.get(path) : this.cache.get(path)) ?? void 0
200
378
  );
@@ -276,7 +454,8 @@ var IndexedDbMeta = class {
276
454
  async removeFile(path) {
277
455
  const known = this.cache.get(path);
278
456
  if (!known) return this.remove([path]);
279
- this.generation++;
457
+ this.invalidations.note({ paths: [path] });
458
+ if (path !== "/") this.listings.delete(parentOf3(path));
280
459
  this.cache.set(path, null);
281
460
  this.removing.set(path, JSON.stringify(known));
282
461
  if (this.removing.size >= REMOVE_BATCH) return this.flushRemoves();
@@ -316,10 +495,8 @@ var IndexedDbMeta = class {
316
495
  return settle(transaction, () => void this.channel.postMessage({ paths }));
317
496
  }
318
497
  async links(dir) {
319
- this.settleRemoves();
320
- const [transaction, store] = this.transaction("readonly");
321
- const request3 = store.index("dir").getAll(dir);
322
- return settle(transaction, () => request3.result);
498
+ const known = this.listings.get(dir);
499
+ return [...known ?? await this.reads.links(dir)];
323
500
  }
324
501
  async under(path) {
325
502
  this.settleRemoves();
@@ -803,14 +980,14 @@ function protectedPrefixes(paths) {
803
980
  }
804
981
  return [...new Set(paths.map((p) => normalizePath(p)))];
805
982
  }
806
- function within(path, prefix) {
983
+ function within2(path, prefix) {
807
984
  return prefix === "/" || path === prefix || path.startsWith(`${prefix}/`);
808
985
  }
809
986
  function isProtected(prefixes2, path) {
810
- return prefixes2.some((prefix) => within(path, prefix));
987
+ return prefixes2.some((prefix) => within2(path, prefix));
811
988
  }
812
989
  function holdsProtected(prefixes2, path) {
813
- return prefixes2.some((prefix) => within(prefix, path));
990
+ return prefixes2.some((prefix) => within2(prefix, path));
814
991
  }
815
992
  var MAX_LINKS = 40;
816
993
  function partsOf(path) {
@@ -1537,6 +1714,7 @@ function sidecar(meta) {
1537
1714
  };
1538
1715
  if (meta.absent) ops.absent = meta.absent.bind(meta);
1539
1716
  if (meta.forgetTrees) ops.forgetTrees = meta.forgetTrees.bind(meta);
1717
+ if (meta.transactions) ops.transactions = meta.transactions.bind(meta);
1540
1718
  const { removeFile } = meta;
1541
1719
  if (removeFile)
1542
1720
  ops.removeFile = (path) => removeFile.call(meta, path).catch((err) => {
@@ -1559,7 +1737,7 @@ function missing2(err) {
1559
1737
  function split(path) {
1560
1738
  return path.split("/").filter(Boolean);
1561
1739
  }
1562
- function parentOf3(path) {
1740
+ function parentOf4(path) {
1563
1741
  return path.slice(0, path.lastIndexOf("/")) || "/";
1564
1742
  }
1565
1743
  function baseOf(path) {
@@ -1569,7 +1747,7 @@ function prefixes(path) {
1569
1747
  const parts2 = split(path);
1570
1748
  return parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`);
1571
1749
  }
1572
- function within2(path, root) {
1750
+ function within3(path, root) {
1573
1751
  return path === root || path.startsWith(`${root}/`);
1574
1752
  }
1575
1753
  var unstored = () => Object.assign(new Error("the file system kept fewer bytes than were written"), {
@@ -1931,7 +2109,7 @@ var OpfsFs = class {
1931
2109
  async ownerAt(path, entry) {
1932
2110
  const own = ownOf(entry);
1933
2111
  if (own) return own;
1934
- return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf3(path));
2112
+ return path === "/" ? { ...ROOT_OWNER } : this.handedTo(parentOf4(path));
1935
2113
  }
1936
2114
  async owned(stat, path, entry) {
1937
2115
  return this.owners ? { ...stat, ...await this.ownerAt(path, entry) } : stat;
@@ -2026,7 +2204,7 @@ var OpfsFs = class {
2026
2204
  async chown(path, change) {
2027
2205
  if (!this.owners) return;
2028
2206
  if (change.kind === "new") {
2029
- const at = resolvePath((await this.locate(parentOf3(path), true)).path, baseOf(path));
2207
+ const at = resolvePath((await this.locate(parentOf4(path), true)).path, baseOf(path));
2030
2208
  this.refuse(at);
2031
2209
  const { uid = 0, gid = 0, mode } = change;
2032
2210
  return this.stampNew(at, { uid, gid, ...mode !== void 0 ? { mode } : {} }, false);
@@ -2107,6 +2285,9 @@ var OpfsFs = class {
2107
2285
  opfsOps() {
2108
2286
  return this.calls + this.others;
2109
2287
  }
2288
+ metaTransactions() {
2289
+ return this.meta.transactions?.();
2290
+ }
2110
2291
  io(request3) {
2111
2292
  this.others++;
2112
2293
  return request3;
@@ -2229,7 +2410,7 @@ var OpfsFs = class {
2229
2410
  for (const entry of await this.meta.get(prefixes(path))) {
2230
2411
  if (entry?.link === void 0) continue;
2231
2412
  if (entry.path !== path && await this.real(entry.path)) continue;
2232
- if (await this.real(parentOf3(entry.path))) return entry;
2413
+ if (await this.real(parentOf4(entry.path))) return entry;
2233
2414
  }
2234
2415
  return void 0;
2235
2416
  }
@@ -2261,11 +2442,11 @@ var OpfsFs = class {
2261
2442
  if (this.rejectLinks) throw fsError("ELOOP", path);
2262
2443
  const target = `${link2.link}${current.slice(link2.path.length)}`;
2263
2444
  if (target.split("/").includes("..")) {
2264
- const resolved2 = await this.physical(parentOf3(link2.path), target, hops + 1, path);
2445
+ const resolved2 = await this.physical(parentOf4(link2.path), target, hops + 1, path);
2265
2446
  current = resolved2.path;
2266
2447
  hops = resolved2.hops - 1;
2267
2448
  } else {
2268
- current = resolvePath(parentOf3(link2.path), target);
2449
+ current = resolvePath(parentOf4(link2.path), target);
2269
2450
  }
2270
2451
  this.crossing(path, current);
2271
2452
  }
@@ -2356,11 +2537,11 @@ var OpfsFs = class {
2356
2537
  movePending(from, to) {
2357
2538
  if (to !== void 0) {
2358
2539
  for (const path of [...this.fileTimes.keys()]) {
2359
- if (within2(path, to)) this.fileTimes.delete(path);
2540
+ if (within3(path, to)) this.fileTimes.delete(path);
2360
2541
  }
2361
2542
  }
2362
2543
  for (const [path, ms] of [...this.fileTimes]) {
2363
- if (!within2(path, from)) continue;
2544
+ if (!within3(path, from)) continue;
2364
2545
  this.fileTimes.delete(path);
2365
2546
  if (to !== void 0) this.fileTimes.set(to + path.slice(from.length), ms);
2366
2547
  }
@@ -2637,8 +2818,8 @@ var OpfsFs = class {
2637
2818
  const file = this.movingFiles.get(at);
2638
2819
  if (file !== void 0) return { done: file, away: true };
2639
2820
  for (const m of this.renamingDirs) {
2640
- if (within2(at, m.from)) return { done: m.done, away: true };
2641
- if (within2(at, m.to)) return { done: m.done, away: false };
2821
+ if (within3(at, m.from)) return { done: m.done, away: true };
2822
+ if (within3(at, m.to)) return { done: m.done, away: false };
2642
2823
  }
2643
2824
  return void 0;
2644
2825
  }
@@ -2674,7 +2855,7 @@ var OpfsFs = class {
2674
2855
  idle2.resolve();
2675
2856
  }
2676
2857
  async writesDrained(tree2) {
2677
- while ([...this.fileWrites.keys()].some((at) => within2(at, tree2))) {
2858
+ while ([...this.fileWrites.keys()].some((at) => within3(at, tree2))) {
2678
2859
  this.writesIdle ??= Promise.withResolvers();
2679
2860
  await this.writesIdle.promise;
2680
2861
  }
@@ -2790,8 +2971,8 @@ var OpfsFs = class {
2790
2971
  if (!missing2(err)) throw err;
2791
2972
  }
2792
2973
  const [entry] = await this.meta.get([path]);
2793
- const wanted = entry?.link === void 0 ? path : resolvePath(parentOf3(path), entry.link);
2794
- return [resolvePath((await this.locate(parentOf3(wanted), true)).path, baseOf(wanted)), false];
2974
+ const wanted = entry?.link === void 0 ? path : resolvePath(parentOf4(path), entry.link);
2975
+ return [resolvePath((await this.locate(parentOf4(wanted), true)).path, baseOf(wanted)), false];
2795
2976
  }
2796
2977
  async fileFor(path, create = true) {
2797
2978
  const [target, existed] = await this.writeTarget(path);
@@ -2806,7 +2987,7 @@ var OpfsFs = class {
2806
2987
  dir,
2807
2988
  (parent) => this.io(parent.getFileHandle(name2, { create }))
2808
2989
  );
2809
- if (!existed) this.touch(Date.now(), parentOf3(target));
2990
+ if (!existed) this.touch(Date.now(), parentOf4(target));
2810
2991
  return [handle, existed, target];
2811
2992
  } catch (err) {
2812
2993
  const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
@@ -2895,8 +3076,8 @@ var OpfsFs = class {
2895
3076
  gid: owner.gid,
2896
3077
  ...mode !== void 0 ? { mode, parents: mode } : {}
2897
3078
  };
2898
- await this.mkdir(parentOf3(path), { recursive: true, ...above ? { owner: above } : {} });
2899
- const parent = await this.locate(parentOf3(path), true);
3079
+ await this.mkdir(parentOf4(path), { recursive: true, ...above ? { owner: above } : {} });
3080
+ const parent = await this.locate(parentOf4(path), true);
2900
3081
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
2901
3082
  this.refuse(resolvePath(parent.path, baseOf(path)));
2902
3083
  await this.io(parent.handle.getDirectoryHandle(baseOf(path), { create: true }));
@@ -2907,7 +3088,7 @@ var OpfsFs = class {
2907
3088
  }
2908
3089
  async mkdirExcl(path, owner) {
2909
3090
  if (normalizePath(path) === "/") throw fsError("EEXIST", path);
2910
- const parent = await this.locate(parentOf3(path), true);
3091
+ const parent = await this.locate(parentOf4(path), true);
2911
3092
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
2912
3093
  const dir = parent.handle;
2913
3094
  const name2 = baseOf(path);
@@ -2929,7 +3110,7 @@ var OpfsFs = class {
2929
3110
  }
2930
3111
  async createExclusive(path, owner) {
2931
3112
  if (normalizePath(path) === "/") throw fsError("EEXIST", path);
2932
- const parent = await this.locate(parentOf3(path), true);
3113
+ const parent = await this.locate(parentOf4(path), true);
2933
3114
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
2934
3115
  const dir = parent.handle;
2935
3116
  const name2 = baseOf(path);
@@ -2991,12 +3172,12 @@ var OpfsFs = class {
2991
3172
  else this.forget(found.path);
2992
3173
  this.movePending(found.path);
2993
3174
  if (file && this.meta.absent?.(found.path)) {
2994
- this.touch(now, parentOf3(found.path));
3175
+ this.touch(now, parentOf4(found.path));
2995
3176
  return;
2996
3177
  }
2997
3178
  if (file && this.meta.removeFile) {
2998
3179
  await this.meta.removeFile(found.path);
2999
- this.touch(now, parentOf3(found.path));
3180
+ this.touch(now, parentOf4(found.path));
3000
3181
  return;
3001
3182
  }
3002
3183
  }
@@ -3004,14 +3185,14 @@ var OpfsFs = class {
3004
3185
  [found.path],
3005
3186
  found.handle?.kind === "directory" ? vacatedAt(found.path) : void 0
3006
3187
  );
3007
- this.touch(Date.now(), parentOf3(found.path));
3188
+ this.touch(Date.now(), parentOf4(found.path));
3008
3189
  }
3009
3190
  links() {
3010
3191
  return false;
3011
3192
  }
3012
3193
  async link(from, to, follow2) {
3013
3194
  await (follow2 ? this.stat(from) : this.lstat(from));
3014
- if (!(await this.stat(parentOf3(to))).isDirectory) throw fsError("ENOTDIR", to);
3195
+ if (!(await this.stat(parentOf4(to))).isDirectory) throw fsError("ENOTDIR", to);
3015
3196
  const taken = await this.exactly(() => this.lstat(to)).then(
3016
3197
  () => true,
3017
3198
  (err) => {
@@ -3038,14 +3219,14 @@ var OpfsFs = class {
3038
3219
  const source = await this.locate(from, false);
3039
3220
  this.refuse(source.path, true);
3040
3221
  this.forget(source.path);
3041
- const parent = await this.locate(parentOf3(to), true);
3222
+ const parent = await this.locate(parentOf4(to), true);
3042
3223
  const destination = resolvePath(parent.path, baseOf(to));
3043
3224
  this.refuse(destination, true);
3044
3225
  await this.entering(destination, () => this.moveTo(source, parent, destination, to, call));
3045
3226
  }
3046
3227
  async moveTo(source, parent, destination, to, call) {
3047
3228
  const sourceStat = await this.describe(source);
3048
- if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
3229
+ if (sourceStat.isDirectory && within3(destination, source.path)) throw fsError("EINVAL", to);
3049
3230
  const target = await this.exactly(() => this.locate(destination, false)).catch((err) => {
3050
3231
  if (missing2(err)) return null;
3051
3232
  throw err;
@@ -3072,7 +3253,7 @@ var OpfsFs = class {
3072
3253
  if (source.link) {
3073
3254
  if (target?.handle) await this.rm(destination);
3074
3255
  await this.meta.move(source.path, destination, carry, left);
3075
- this.touch(Date.now(), parentOf3(source.path), parent.path);
3256
+ this.touch(Date.now(), parentOf4(source.path), parent.path);
3076
3257
  return;
3077
3258
  }
3078
3259
  const dir = parent.handle;
@@ -3121,7 +3302,7 @@ var OpfsFs = class {
3121
3302
  if (second) await this.meta.update(second, () => void 0);
3122
3303
  await this.restamp(destination, copies);
3123
3304
  this.changed(source.path, destination);
3124
- this.touch(Date.now(), parentOf3(source.path), parent.path);
3305
+ this.touch(Date.now(), parentOf4(source.path), parent.path);
3125
3306
  };
3126
3307
  if (!copying) return sourceStat.isDirectory ? place() : this.movingFile(source.path, place);
3127
3308
  const { promise, resolve: resolve2 } = Promise.withResolvers();
@@ -3318,7 +3499,7 @@ var OpfsFs = class {
3318
3499
  for (const move of this.peerMoves.values()) {
3319
3500
  for (const tree2 of move.trees) {
3320
3501
  for (const mine of [from, to]) {
3321
- if (tree2.some((theirs) => within2(mine, theirs) || within2(theirs, mine))) return move;
3502
+ if (tree2.some((theirs) => within3(mine, theirs) || within3(theirs, mine))) return move;
3322
3503
  }
3323
3504
  }
3324
3505
  }
@@ -3376,7 +3557,7 @@ var OpfsFs = class {
3376
3557
  }
3377
3558
  async resolvedForms(at) {
3378
3559
  if (at === "/") return [];
3379
- const parent = await PLAIN.realpath.call(this, parentOf3(at), true).catch(() => parentOf3(at));
3560
+ const parent = await PLAIN.realpath.call(this, parentOf4(at), true).catch(() => parentOf4(at));
3380
3561
  const named = resolvePath(parent, baseOf(at));
3381
3562
  const target = await PLAIN.realpath.call(this, at, true).catch(() => named);
3382
3563
  return [.../* @__PURE__ */ new Set([named, target])].filter((form) => form !== at);
@@ -3420,7 +3601,7 @@ var OpfsFs = class {
3420
3601
  try {
3421
3602
  if (await this.real(from)) {
3422
3603
  const moved2 = await this.locate(from, false);
3423
- const back = await this.locate(parentOf3(to), true);
3604
+ const back = await this.locate(parentOf4(to), true);
3424
3605
  await this.copy(
3425
3606
  moved2.handle,
3426
3607
  back.handle,
@@ -3474,7 +3655,7 @@ var OpfsFs = class {
3474
3655
  await PLAIN.rm.call(this, to, { recursive: true });
3475
3656
  if (left) {
3476
3657
  const source = await this.locate(from, false);
3477
- const target = await this.locate(parentOf3(to), true);
3658
+ const target = await this.locate(parentOf4(to), true);
3478
3659
  await this.copy(
3479
3660
  source.handle,
3480
3661
  target.handle,
@@ -3516,7 +3697,7 @@ var OpfsFs = class {
3516
3697
  continue;
3517
3698
  }
3518
3699
  if (real !== void 0 || at === "/") return;
3519
- const parent = await this.realpath(parentOf3(at), true).catch(() => parentOf3(at));
3700
+ const parent = await this.realpath(parentOf4(at), true).catch(() => parentOf4(at));
3520
3701
  real = resolvePath(parent, baseOf(at));
3521
3702
  if (real === at) return;
3522
3703
  }
@@ -3530,7 +3711,7 @@ var OpfsFs = class {
3530
3711
  if (source.handle?.kind !== "file") throw fsError("EINVAL", `${from}: not a regular file`);
3531
3712
  const [entry] = await this.meta.get([source.path]);
3532
3713
  if (entry?.fifo || entry?.socket) throw fsError("EINVAL", `${from}: not a regular file`);
3533
- const parent = await this.locate(parentOf3(to), true);
3714
+ const parent = await this.locate(parentOf4(to), true);
3534
3715
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", to);
3535
3716
  const destination = resolvePath(parent.path, baseOf(to));
3536
3717
  if (destination === source.path) throw fsError("EINVAL", `${from}: copied onto itself`);
@@ -3585,7 +3766,7 @@ var OpfsFs = class {
3585
3766
  }
3586
3767
  renameAt(at) {
3587
3768
  for (const m of this.renamingDirs) {
3588
- if (within2(at, m.from) || within2(at, m.to) || at === parentOf3(m.from) || at === parentOf3(m.to))
3769
+ if (within3(at, m.from) || within3(at, m.to) || at === parentOf4(m.from) || at === parentOf4(m.to))
3589
3770
  return m;
3590
3771
  }
3591
3772
  return void 0;
@@ -3678,7 +3859,7 @@ var OpfsFs = class {
3678
3859
  }
3679
3860
  }
3680
3861
  async symlink(target, path, owner) {
3681
- const parent = await this.locate(parentOf3(path), true);
3862
+ const parent = await this.locate(parentOf4(path), true);
3682
3863
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
3683
3864
  const at = resolvePath(parent.path, baseOf(path));
3684
3865
  this.refuse(at);
@@ -3769,7 +3950,7 @@ var OpfsFs = class {
3769
3950
  const known = listed.get(dir);
3770
3951
  if (known !== void 0) return known;
3771
3952
  const listing = (async () => {
3772
- const above = dir === "/" ? void 0 : await list(parentOf3(dir));
3953
+ const above = dir === "/" ? void 0 : await list(parentOf4(dir));
3773
3954
  if (above === null) return null;
3774
3955
  const handle = dir === "/" ? this.root : above?.get(baseOf(dir));
3775
3956
  if (handle?.kind !== "directory") return void 0;
@@ -3789,7 +3970,7 @@ var OpfsFs = class {
3789
3970
  listed.set(dir, listing);
3790
3971
  return listing;
3791
3972
  };
3792
- const dirs = [...new Set(paths.filter((p) => split(p).length > 0).map(parentOf3))];
3973
+ const dirs = [...new Set(paths.filter((p) => split(p).length > 0).map(parentOf4))];
3793
3974
  let next3 = 0;
3794
3975
  const lister = async () => {
3795
3976
  while (next3 < dirs.length) await list(dirs[next3++]);
@@ -3797,7 +3978,7 @@ var OpfsFs = class {
3797
3978
  await Promise.all(Array.from({ length: Math.min(RECONCILE_LISTINGS, dirs.length) }, lister));
3798
3979
  return async (path) => {
3799
3980
  if (split(path).length === 0) return true;
3800
- const listing = done5.get(parentOf3(path));
3981
+ const listing = done5.get(parentOf4(path));
3801
3982
  if (listing === null) return this.real(path);
3802
3983
  return listing?.has(baseOf(path)) === true;
3803
3984
  };
@@ -3813,10 +3994,10 @@ var OpfsFs = class {
3813
3994
  const carried = new Set(entries.filter((e) => !e.vacated).map((e) => e.ino));
3814
3995
  const checked = entries.filter((e) => !(e.vacated && carried.has(inodeOf(e.path))));
3815
3996
  const real = await this.present(
3816
- checked.flatMap((e) => e.link === void 0 ? [e.path] : [e.path, parentOf3(e.path)])
3997
+ checked.flatMap((e) => e.link === void 0 ? [e.path] : [e.path, parentOf4(e.path)])
3817
3998
  );
3818
3999
  for (const entry of checked) {
3819
- const stale = entry.link === void 0 ? !await real(entry.path) : await real(entry.path) || !await real(parentOf3(entry.path));
4000
+ const stale = entry.link === void 0 ? !await real(entry.path) : await real(entry.path) || !await real(parentOf4(entry.path));
3820
4001
  if (!stale) continue;
3821
4002
  const seen = JSON.stringify(entry);
3822
4003
  await this.meta.update(
@@ -3837,7 +4018,7 @@ function nothing() {
3837
4018
  }
3838
4019
  function touches(at, trees, listing = true, removing = false) {
3839
4020
  return trees.some(
3840
- ([from, to]) => within2(at, from) || within2(at, to) || listing && (at === parentOf3(from) || at === parentOf3(to)) || removing && (at === "/" || within2(from, at) || within2(to, at))
4021
+ ([from, to]) => within3(at, from) || within3(at, to) || listing && (at === parentOf4(from) || at === parentOf4(to)) || removing && (at === "/" || within3(from, at) || within3(to, at))
3841
4022
  );
3842
4023
  }
3843
4024
  function byIntent(a, b) {
@@ -5023,7 +5204,7 @@ function portFacadeHook(ask, gone) {
5023
5204
 
5024
5205
  // src/fs/walk.ts
5025
5206
  var MAX_LINKS3 = 40;
5026
- function parentOf4(path) {
5207
+ function parentOf5(path) {
5027
5208
  return path.slice(0, path.lastIndexOf("/")) || "/";
5028
5209
  }
5029
5210
  async function mustBeDir(fs, path) {
@@ -5041,7 +5222,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
5041
5222
  const part = queue.shift();
5042
5223
  if (part === ".") continue;
5043
5224
  if (part === "..") {
5044
- at = parentOf4(at);
5225
+ at = parentOf5(at);
5045
5226
  continue;
5046
5227
  }
5047
5228
  const next3 = at === "/" ? `/${part}` : `${at}/${part}`;
@@ -5063,7 +5244,7 @@ function joinName(dir, name2) {
5063
5244
  }
5064
5245
  async function realTarget(fs, path) {
5065
5246
  const budget = { hops: 0 };
5066
- let dir = await realDir(fs, parentOf4(path), budget);
5247
+ let dir = await realDir(fs, parentOf5(path), budget);
5067
5248
  let name2 = path.slice(path.lastIndexOf("/") + 1);
5068
5249
  for (; ; ) {
5069
5250
  const link2 = await linkOf(fs, joinName(dir, name2));
@@ -5071,7 +5252,7 @@ async function realTarget(fs, path) {
5071
5252
  if (budget.hops >= MAX_LINKS3) throw fsError("ELOOP", path);
5072
5253
  budget.hops++;
5073
5254
  const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
5074
- dir = await realDir(fs, parentOf4(target), budget);
5255
+ dir = await realDir(fs, parentOf5(target), budget);
5075
5256
  name2 = target.slice(target.lastIndexOf("/") + 1);
5076
5257
  }
5077
5258
  return joinName(dir, name2);
@@ -5148,7 +5329,7 @@ async function walk(fs, cred, path, follow2, links) {
5148
5329
  if (!dir) return { path: joinName(at, [part, ...queue].join("/")), perm: void 0 };
5149
5330
  if (!permits(cred, dir, X_OK)) return { path, perm: void 0, denied: at };
5150
5331
  if (part === "..") {
5151
- at = parentOf4(at);
5332
+ at = parentOf5(at);
5152
5333
  continue;
5153
5334
  }
5154
5335
  const next3 = joinName(at, part);
@@ -5269,7 +5450,7 @@ function deny(g, op, path, errno3 = "EACCES") {
5269
5450
  }
5270
5451
  var FENCE_HOPS = 40;
5271
5452
  async function entryAt(g, path) {
5272
- const parent = parentOf4(path);
5453
+ const parent = parentOf5(path);
5273
5454
  const real = await g.fs.realpath?.(parent, true).catch(() => void 0);
5274
5455
  return real ? joinName(real, path.slice(path.lastIndexOf("/") + 1)) : path;
5275
5456
  }
@@ -5280,7 +5461,7 @@ async function fencedNames(g, path) {
5280
5461
  names2.push(at);
5281
5462
  const link2 = await g.fs.readlink(at).catch(() => void 0);
5282
5463
  if (link2 === void 0) break;
5283
- at = await entryAt(g, normalizePath(g.fs.resolvePath(parentOf4(at), link2)));
5464
+ at = await entryAt(g, normalizePath(g.fs.resolvePath(parentOf5(at), link2)));
5284
5465
  }
5285
5466
  return names2;
5286
5467
  }
@@ -5331,7 +5512,7 @@ async function unwritable(g, op, path, dir) {
5331
5512
  }
5332
5513
  async function inParent(g, op, path, old = false) {
5333
5514
  await fence(g, op, path);
5334
- const dir = await access(g.fs, g.cred, parentOf4(path), W_OK | X_OK, linked(g, DIR));
5515
+ const dir = await access(g.fs, g.cred, parentOf5(path), W_OK | X_OK, linked(g, DIR));
5335
5516
  if (old !== true) {
5336
5517
  if (await unwritable(g, op, path, dir)) deny(g, op, path);
5337
5518
  else if (old === "replaced" && dir.denied === void 0) await stickyEntry(g, op, path, dir);
@@ -5343,7 +5524,7 @@ async function inParent(g, op, path, old = false) {
5343
5524
  if (!lookup2 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
5344
5525
  }
5345
5526
  async function makeIn(g, op, path, parents = true) {
5346
- for (let dir = parentOf4(path); ; dir = parentOf4(dir)) {
5527
+ for (let dir = parentOf5(path); ; dir = parentOf5(dir)) {
5347
5528
  const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, linked(g, DIR));
5348
5529
  if (await unwritable(g, op, path, walked)) {
5349
5530
  if (!await present(g.fs, path, true)) deny(g, op, path);
@@ -5371,7 +5552,7 @@ async function renamable(g, from, to) {
5371
5552
  if (from === to) return void await need(g, "rename", from, 0, { follow: false });
5372
5553
  await inParent(g, "rename", from, true);
5373
5554
  await inParent(g, "rename", to, "replaced");
5374
- if (g.cred.euid === 0 || parentOf4(from) === parentOf4(to)) return;
5555
+ if (g.cred.euid === 0 || parentOf5(from) === parentOf5(to)) return;
5375
5556
  const st = await g.fs.lstat(from).catch(() => void 0);
5376
5557
  if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
5377
5558
  }
@@ -5438,7 +5619,7 @@ async function writable(g, op, path) {
5438
5619
  }
5439
5620
  async function creatable(g, op, path) {
5440
5621
  const target = await need(g, op, path, 0);
5441
- if (target.denied === void 0) await need(g, op, parentOf4(target.path), W_OK | X_OK, DIR, path);
5622
+ if (target.denied === void 0) await need(g, op, parentOf5(target.path), W_OK | X_OK, DIR, path);
5442
5623
  }
5443
5624
  async function openable(g, op, path, want, create) {
5444
5625
  const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
@@ -6194,7 +6375,7 @@ function parsePrefix(text) {
6194
6375
  if (length2 < 0 || length2 > max) return void 0;
6195
6376
  return { ...parsed2, length: length2 };
6196
6377
  }
6197
- function within3(prefix, addr) {
6378
+ function within4(prefix, addr) {
6198
6379
  if (prefix.v6 !== addr.v6) return false;
6199
6380
  const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
6200
6381
  return addr.bits >> shift === prefix.bits >> shift;
@@ -6234,9 +6415,9 @@ var Routes = class {
6234
6415
  if (!addr) return "unreachable";
6235
6416
  if (addr.v6 && addr.bits === 1n) return "loopback";
6236
6417
  if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
6237
- if (RESERVED.some((p) => within3(p, addr))) return "reserved";
6418
+ if (RESERVED.some((p) => within4(p, addr))) return "reserved";
6238
6419
  if (addr.v6 && !this.ipv6) return "unreachable";
6239
- if (this.exit || this.table.some((p) => within3(p, addr))) return "uplink";
6420
+ if (this.exit || this.table.some((p) => within4(p, addr))) return "uplink";
6240
6421
  return "unreachable";
6241
6422
  }
6242
6423
  };
@@ -8188,7 +8369,7 @@ function hostRegistries(send2, gone, port) {
8188
8369
  function visibleTo(g, notify) {
8189
8370
  if (!g?.dac.enforcing) return notify;
8190
8371
  let queue = Promise.resolve();
8191
- const seen = async (path) => (await access(g.fs, g.cred, parentOf4(path), R_OK | X_OK, { directory: true })).denied === void 0;
8372
+ const seen = async (path) => (await access(g.fs, g.cred, parentOf5(path), R_OK | X_OK, { directory: true })).denied === void 0;
8192
8373
  return (change) => {
8193
8374
  queue = queue.then(async () => {
8194
8375
  if (!("paths" in change)) return notify(change);
@@ -8206,7 +8387,7 @@ async function watchOp(l, req, watches, send2, g) {
8206
8387
  }
8207
8388
  if (g) for (const path of req.paths) await readable(g, "watch", l.fs.resolvePath("/", path));
8208
8389
  const notify = visibleTo(g, (change) => send2({ watch: req.id, change }));
8209
- const unwatch = l.watchers.watch(req.paths, { recursive: req.recursive }, notify);
8390
+ const unwatch = l.watchers.watch(req.paths, { recursive: req.recursive, peers: true }, notify);
8210
8391
  watches.set(req.id, unwatch);
8211
8392
  return { result: req.id };
8212
8393
  }
@@ -8559,7 +8740,7 @@ function satisfies(version, range2) {
8559
8740
  }
8560
8741
 
8561
8742
  // src/version.ts
8562
- var KERNEL_VERSION = true ? "1.172.2" : "0.0.0-dev";
8743
+ var KERNEL_VERSION = true ? "1.173.1" : "0.0.0-dev";
8563
8744
  var OS_RELEASE = "6.0.0-slicc";
8564
8745
  function procVersion() {
8565
8746
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9470,7 +9651,7 @@ var PathMarks = class {
9470
9651
  }
9471
9652
  changedSince(path, gen) {
9472
9653
  if (gen < this.floor) return true;
9473
- for (let p = path; this.touched.size > 0; p = parentOf5(p)) {
9654
+ for (let p = path; this.touched.size > 0; p = parentOf6(p)) {
9474
9655
  if ((this.touched.get(p) ?? 0) > gen) return true;
9475
9656
  if (p === "/") return false;
9476
9657
  }
@@ -9505,7 +9686,7 @@ var PathMarks = class {
9505
9686
  });
9506
9687
  }
9507
9688
  inFlight(path) {
9508
- for (let p = path; this.flying.size > 0; p = parentOf5(p)) {
9689
+ for (let p = path; this.flying.size > 0; p = parentOf6(p)) {
9509
9690
  if (this.flying.has(p)) return true;
9510
9691
  if (p === "/") return false;
9511
9692
  }
@@ -9525,9 +9706,9 @@ var LOOKUP_OPS = [
9525
9706
  "readlink",
9526
9707
  "realpath"
9527
9708
  ];
9528
- var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
9709
+ var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
9529
9710
  var TOUCHED_MAX = 4096;
9530
- var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
9711
+ var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
9531
9712
  var PEER_RM_MS = 1e3;
9532
9713
  var SHARED_ACK_MS = 24 * 60 * 60 * 1e3;
9533
9714
  var delay = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
@@ -9715,7 +9896,7 @@ var UnlinkedKeeper = class {
9715
9896
  };
9716
9897
  }
9717
9898
  async settled(path) {
9718
- for (let p = path; this.replacing.size > 0; p = parentOf5(p)) {
9899
+ for (let p = path; this.replacing.size > 0; p = parentOf6(p)) {
9719
9900
  await this.replacing.get(p)?.done;
9720
9901
  if (p === "/") return;
9721
9902
  }
@@ -9733,7 +9914,7 @@ var UnlinkedKeeper = class {
9733
9914
  return this.marks.inFlight(path);
9734
9915
  }
9735
9916
  missed(path) {
9736
- for (let p = path; this.replacing.size > 0; p = parentOf5(p)) {
9917
+ for (let p = path; this.replacing.size > 0; p = parentOf6(p)) {
9737
9918
  if (this.replacing.get(p)?.sealed) return true;
9738
9919
  if (p === "/") return false;
9739
9920
  }
@@ -9753,7 +9934,7 @@ var UnlinkedKeeper = class {
9753
9934
  for (const holder of this.holders) {
9754
9935
  if (holder.owns(target)) continue;
9755
9936
  for (const path of holder.held()) {
9756
- if (!within4(path, target) || holder.isKept(path)) continue;
9937
+ if (!within5(path, target) || holder.isKept(path)) continue;
9757
9938
  held.set(path, [...held.get(path) ?? [], holder]);
9758
9939
  }
9759
9940
  }
@@ -9833,7 +10014,7 @@ var UnlinkedKeeper = class {
9833
10014
  await this.serial(async () => {
9834
10015
  await Promise.allSettled([this.fs.rename(file.hidden, path)]);
9835
10016
  this.unlinkedName(file.hidden);
9836
- await this.tidy(parentOf5(file.hidden));
10017
+ await this.tidy(parentOf6(file.hidden));
9837
10018
  });
9838
10019
  }
9839
10020
  done5();
@@ -10072,7 +10253,7 @@ var UnlinkedKeeper = class {
10072
10253
  }
10073
10254
  await this.fs.rm(hidden).catch(() => void 0);
10074
10255
  this.unlinkedName(hidden);
10075
- await this.tidy(parentOf5(hidden));
10256
+ await this.tidy(parentOf6(hidden));
10076
10257
  }
10077
10258
  async linkedName(hidden) {
10078
10259
  if (!mayLink(this.fs, hidden)) return;
@@ -11621,7 +11802,7 @@ function devCreation(path, cred) {
11621
11802
  var HOSTNAME_PATH = "/etc/hostname";
11622
11803
  var S_IFREG3 = 32768;
11623
11804
  var MAX_HOPS2 = 40;
11624
- var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
11805
+ var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
11625
11806
  var baseOf2 = (path) => path.slice(path.lastIndexOf("/") + 1);
11626
11807
  function missing5(err) {
11627
11808
  return err?.code === "ENOENT";
@@ -11651,7 +11832,7 @@ function linkFollower(fs, files) {
11651
11832
  );
11652
11833
  const viaParent = async (path) => {
11653
11834
  if (!bases.has(baseOf2(path)) || !fs.realpath) return path;
11654
- const parent = parentOf6(path);
11835
+ const parent = parentOf7(path);
11655
11836
  const dir = await fs.realpath(parent, true).catch(() => parent);
11656
11837
  return fs.resolvePath(dir, baseOf2(path));
11657
11838
  };
@@ -11660,7 +11841,7 @@ function linkFollower(fs, files) {
11660
11841
  for (let hop = 0; hop < MAX_HOPS2 && !files.has(current); hop++) {
11661
11842
  const link2 = await linkOf2(current);
11662
11843
  if (link2 === void 0) return viaParent(current);
11663
- current = fs.resolvePath(parentOf6(current), link2);
11844
+ current = fs.resolvePath(parentOf7(current), link2);
11664
11845
  }
11665
11846
  return current;
11666
11847
  };
@@ -11741,7 +11922,7 @@ var DEFAULT_UMASK = 18;
11741
11922
  function isModule(bytes) {
11742
11923
  return MAGIC.every((b, i) => bytes[i] === b);
11743
11924
  }
11744
- function within5(path, dir) {
11925
+ function within6(path, dir) {
11745
11926
  return path.startsWith(dir === "/" ? "/" : `${dir}/`);
11746
11927
  }
11747
11928
  var FreshModules = class {
@@ -11765,10 +11946,10 @@ var FreshModules = class {
11765
11946
  const target = normalizePath(to);
11766
11947
  if (source === target) return;
11767
11948
  for (const key of [...this.fresh.keys()]) {
11768
- if (key === target || within5(key, target)) this.fresh.delete(key);
11949
+ if (key === target || within6(key, target)) this.fresh.delete(key);
11769
11950
  }
11770
11951
  for (const [key, fresh3] of [...this.fresh]) {
11771
- if (key !== source && !within5(key, source)) continue;
11952
+ if (key !== source && !within6(key, source)) continue;
11772
11953
  this.fresh.delete(key);
11773
11954
  this.fresh.set(target + key.slice(source.length), fresh3);
11774
11955
  }
@@ -11810,7 +11991,7 @@ var FreshModules = class {
11810
11991
  await fs.rm(path, options);
11811
11992
  const at = normalizePath(path);
11812
11993
  for (const key of [...this.fresh.keys()]) {
11813
- if (key === at || within5(key, at)) this.fresh.delete(key);
11994
+ if (key === at || within6(key, at)) this.fresh.delete(key);
11814
11995
  }
11815
11996
  },
11816
11997
  rename: async (from, to) => {
@@ -11915,10 +12096,10 @@ var WATCH_NOTES_LIMIT = 1e3;
11915
12096
  var baseOf3 = (path) => path.slice(path.lastIndexOf("/") + 1);
11916
12097
  var WATCH_BATCH_LIMIT = 1e3;
11917
12098
  var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
11918
- var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
12099
+ var parentOf8 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
11919
12100
  function touches2(watcher, changed) {
11920
12101
  return watcher.paths.some(
11921
- (watched) => changed === watched || under3(watched, changed) || (watcher.recursive ? under3(changed, watched) : parentOf7(changed) === watched)
12102
+ (watched) => changed === watched || under3(watched, changed) || (watcher.recursive ? under3(changed, watched) : parentOf8(changed) === watched)
11922
12103
  );
11923
12104
  }
11924
12105
  var REALS_KEPT = 4096;
@@ -11946,7 +12127,7 @@ function routed(watch, root, type, path, seq2) {
11946
12127
  if (type === "rename") noted(watch, { ended: true, seq: seq2 });
11947
12128
  return true;
11948
12129
  }
11949
- if (parentOf7(path) === root) noted(watch, { type, filename: baseOf3(path), seq: seq2 });
12130
+ if (parentOf8(path) === root) noted(watch, { type, filename: baseOf3(path), seq: seq2 });
11950
12131
  else if (watch.recursive && under3(path, root)) {
11951
12132
  noted(watch, { type, filename: path.slice(root === "/" ? 1 : root.length + 1), seq: seq2 });
11952
12133
  } else return false;
@@ -11978,6 +12159,7 @@ var FsWatchers = class {
11978
12159
  const watcher = {
11979
12160
  paths: paths.map(normalizePath),
11980
12161
  recursive: options.recursive === true,
12162
+ peers: options.peers === true,
11981
12163
  notify,
11982
12164
  pending: /* @__PURE__ */ new Set(),
11983
12165
  scheduled: false
@@ -12031,7 +12213,7 @@ var FsWatchers = class {
12031
12213
  covers(path) {
12032
12214
  for (const watch of this.typed) {
12033
12215
  for (const root of watch.roots) {
12034
- if (path === root || parentOf7(path) === root || watch.recursive && under3(path, root))
12216
+ if (path === root || parentOf8(path) === root || watch.recursive && under3(path, root))
12035
12217
  return true;
12036
12218
  }
12037
12219
  }
@@ -12099,10 +12281,16 @@ var FsWatchers = class {
12099
12281
  this.report(paths);
12100
12282
  this.emit("change", paths);
12101
12283
  }
12284
+ peerChanged(paths) {
12285
+ if (paths.length > 0) this.notify(paths, true);
12286
+ }
12102
12287
  report(paths, notify = true) {
12103
12288
  for (const tap of this.taps) tap(paths);
12104
- if (!notify) return;
12289
+ if (notify) this.notify(paths);
12290
+ }
12291
+ notify(paths, peer = false) {
12105
12292
  for (const watcher of this.watchers) {
12293
+ if (peer && !watcher.peers) continue;
12106
12294
  for (const path of paths) {
12107
12295
  const changed = normalizePath(path);
12108
12296
  if (touches2(watcher, changed)) watcher.pending.add(changed);
@@ -13571,7 +13759,7 @@ function linkedTo(mount, path) {
13571
13759
  return own?.kind !== "directory" && (attr2.nlink ?? 1) > 1;
13572
13760
  };
13573
13761
  }
13574
- var within6 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
13762
+ var within7 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
13575
13763
  var HeldSet = class extends Set {
13576
13764
  idle;
13577
13765
  moved;
@@ -13607,7 +13795,7 @@ var HeldSet = class extends Set {
13607
13795
  }
13608
13796
  };
13609
13797
  function heldUnder(held, target) {
13610
- for (const paths of held) for (const path of paths.keys()) if (within6(path, target)) return true;
13798
+ for (const paths of held) for (const path of paths.keys()) if (within7(path, target)) return true;
13611
13799
  return false;
13612
13800
  }
13613
13801
  var HeldPaths = class extends Map {
@@ -13648,7 +13836,7 @@ var HeldPaths = class extends Map {
13648
13836
  if (this.bound.size === 0) return;
13649
13837
  for (const [handle, key] of this.handles) {
13650
13838
  if (!this.bound.has(handle) || this.keptHandles.has(handle)) continue;
13651
- if (exact ? key === over : within6(key, over)) this.doubted.set(handle, ++this.doubts);
13839
+ if (exact ? key === over : within7(key, over)) this.doubted.set(handle, ++this.doubts);
13652
13840
  }
13653
13841
  }
13654
13842
  boundIno(handle) {
@@ -13784,24 +13972,24 @@ var HeldPaths = class extends Map {
13784
13972
  return this.owning === path;
13785
13973
  }
13786
13974
  revoke(prefix) {
13787
- for (const path of this.keys()) if (within6(path, prefix)) this.revoked.add(path);
13975
+ for (const path of this.keys()) if (within7(path, prefix)) this.revoked.add(path);
13788
13976
  }
13789
13977
  renamed(from, to) {
13790
13978
  for (const [path, count2] of [...this]) {
13791
- if (!within6(path, from)) continue;
13979
+ if (!within7(path, from)) continue;
13792
13980
  const moved2 = to + path.slice(from.length);
13793
13981
  this.delete(path);
13794
13982
  this.set(moved2, (this.get(moved2) ?? 0) + count2);
13795
13983
  if (this.revoked.delete(path)) this.revoked.add(moved2);
13796
13984
  }
13797
13985
  for (const [path, count2] of [...this.opens]) {
13798
- if (!within6(path, from)) continue;
13986
+ if (!within7(path, from)) continue;
13799
13987
  const moved2 = to + path.slice(from.length);
13800
13988
  this.opens.delete(path);
13801
13989
  this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
13802
13990
  }
13803
13991
  for (const [id2, key] of this.handles) {
13804
- if (within6(key, from)) this.handles.set(id2, to + key.slice(from.length));
13992
+ if (within7(key, from)) this.handles.set(id2, to + key.slice(from.length));
13805
13993
  }
13806
13994
  this.onMoved?.(from, to);
13807
13995
  }
@@ -13809,7 +13997,7 @@ var HeldPaths = class extends Map {
13809
13997
  return this.revoked.has(path);
13810
13998
  }
13811
13999
  };
13812
- var parentOf8 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
14000
+ var parentOf9 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
13813
14001
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
13814
14002
  function inodeless(mount, attr2) {
13815
14003
  if (attr2.ino !== void 0) return false;
@@ -13916,13 +14104,13 @@ var MountTable = class {
13916
14104
  });
13917
14105
  }
13918
14106
  under(path) {
13919
- for (const mount of this.mounts.values()) if (within6(mount.target, path)) return true;
14107
+ for (const mount of this.mounts.values()) if (within7(mount.target, path)) return true;
13920
14108
  return false;
13921
14109
  }
13922
14110
  locate(path) {
13923
14111
  let best;
13924
14112
  for (const mount of this.mounts.values()) {
13925
- if (within6(path, mount.target) && (!best || mount.target.length > best.target.length))
14113
+ if (within7(path, mount.target) && (!best || mount.target.length > best.target.length))
13926
14114
  best = mount;
13927
14115
  }
13928
14116
  if (!best) return void 0;
@@ -14069,7 +14257,7 @@ var MountTable = class {
14069
14257
  async commitUnder(prefix) {
14070
14258
  const at = normalizePath(prefix);
14071
14259
  await Promise.all(
14072
- [...this.sessions.keys()].filter((p) => within6(p, at)).map((p) => this.commit(p))
14260
+ [...this.sessions.keys()].filter((p) => within7(p, at)).map((p) => this.commit(p))
14073
14261
  );
14074
14262
  }
14075
14263
  async commitOverlapping(real, offset, length2) {
@@ -14107,11 +14295,11 @@ var MountTable = class {
14107
14295
  mount.epoch++;
14108
14296
  const linked2 = mount.caps.links ? linkedTo(mount, path) : () => false;
14109
14297
  for (const [key, cached] of [...mount.attrs])
14110
- if (within6(key, path) || linked2(cached.value)) mount.attrs.delete(key);
14298
+ if (within7(key, path) || linked2(cached.value)) mount.attrs.delete(key);
14111
14299
  mount.lists.delete(path);
14112
- mount.lists.delete(parentOf8(path));
14300
+ mount.lists.delete(parentOf9(path));
14113
14301
  for (const [key, cached] of [...mount.lists])
14114
- if (within6(key, path) || cached.value.some((e) => linked2(e.attr))) mount.lists.delete(key);
14302
+ if (within7(key, path) || cached.value.some((e) => linked2(e.attr))) mount.lists.delete(key);
14115
14303
  }
14116
14304
  async getattr(mount, path, rel2, settled = false) {
14117
14305
  for (let round = 1; ; round++) {
@@ -14230,7 +14418,7 @@ var MountFs = class _MountFs {
14230
14418
  const real = normalizePath(path);
14231
14419
  if (this.at(real)) return real;
14232
14420
  try {
14233
- await this.base.stat(parentOf8(real));
14421
+ await this.base.stat(parentOf9(real));
14234
14422
  return real;
14235
14423
  } catch (err) {
14236
14424
  const moved2 = redirectOf(err);
@@ -14252,7 +14440,7 @@ var MountFs = class _MountFs {
14252
14440
  if (redirects >= MAX_LINKS7) throw errnoError("ELOOP", paths[0]);
14253
14441
  const at = paths.map((path) => {
14254
14442
  const real = normalizePath(path);
14255
- return within6(real, moved2.of) ? normalizePath(moved2.to + real.slice(moved2.of.length)) : real;
14443
+ return within7(real, moved2.of) ? normalizePath(moved2.to + real.slice(moved2.of.length)) : real;
14256
14444
  });
14257
14445
  return again(at, redirects + 1);
14258
14446
  }
@@ -14272,7 +14460,7 @@ var MountFs = class _MountFs {
14272
14460
  if (this.rejectLinks) throw errnoError("ELOOP", path);
14273
14461
  if (hops >= MAX_LINKS7) throw errnoError("ELOOP", path);
14274
14462
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
14275
- return this.follow(this.base.resolvePath(parentOf8(real), target), hops + 1, create);
14463
+ return this.follow(this.base.resolvePath(parentOf9(real), target), hops + 1, create);
14276
14464
  }
14277
14465
  async open(mount, rel2, write, owner, create = write) {
14278
14466
  return await mount.conn.call({
@@ -14288,8 +14476,8 @@ var MountFs = class _MountFs {
14288
14476
  async driverOwner(found, real, owner, directory) {
14289
14477
  const { mount, rel: rel2 } = found;
14290
14478
  if (!owner || !mount.owners || !mount.caps.owners) return void 0;
14291
- const dir = parentOf8(real);
14292
- const above = statOf2(await this.table.getattr(mount, dir, parentOf8(rel2)), dir, mount);
14479
+ const dir = parentOf9(real);
14480
+ const above = statOf2(await this.table.getattr(mount, dir, parentOf9(rel2)), dir, mount);
14293
14481
  const setgid = (above.mode & S_ISGID4) !== 0;
14294
14482
  let mode = owner.mode;
14295
14483
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
@@ -14808,7 +14996,7 @@ var MountFs = class _MountFs {
14808
14996
  }
14809
14997
  async named(path) {
14810
14998
  const at = normalizePath(path);
14811
- return joinName(await realDir(this, parentOf8(at)), at.slice(at.lastIndexOf("/") + 1));
14999
+ return joinName(await realDir(this, parentOf9(at)), at.slice(at.lastIndexOf("/") + 1));
14812
15000
  }
14813
15001
  async link(source, target, follow2 = false) {
14814
15002
  const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
@@ -16482,6 +16670,8 @@ var SYS_TREE = {
16482
16670
  slicc_opfs_calls: (listing) => `${listing.opfsCalls ?? 0}
16483
16671
  `,
16484
16672
  slicc_opfs_ops: (listing) => `${listing.opfsOps ?? 0}
16673
+ `,
16674
+ slicc_meta_transactions: ({ meta }) => `${meta?.reads ?? 0} ${meta?.writes ?? 0}
16485
16675
  `,
16486
16676
  slicc_cwd_checks: (listing) => `${listing.cwdChecks ?? 0}
16487
16677
  `,
@@ -22682,7 +22872,7 @@ function ranges(text) {
22682
22872
  return [Number.parseInt(lo, 16), Number.parseInt(hi, 16)];
22683
22873
  });
22684
22874
  }
22685
- function within7(table, code) {
22875
+ function within8(table, code) {
22686
22876
  let lo = 0;
22687
22877
  let hi = table.length - 1;
22688
22878
  while (lo <= hi) {
@@ -22705,10 +22895,10 @@ var LCAT = ranges(LCAT_TEXT);
22705
22895
  function prepFails(prepared) {
22706
22896
  const codes = Array.from(prepared, (ch) => ch.codePointAt(0) ?? 0);
22707
22897
  if (codes.length === 0) return true;
22708
- if (codes.some((c) => within7(PROHIBITED, c) || within7(UNASSIGNED, c))) return true;
22709
- if (!codes.some((c) => within7(RANDAL, c))) return false;
22710
- if (codes.some((c) => within7(LCAT, c))) return true;
22711
- return !within7(RANDAL, codes[0] ?? 0) || !within7(RANDAL, codes.at(-1) ?? 0);
22898
+ if (codes.some((c) => within8(PROHIBITED, c) || within8(UNASSIGNED, c))) return true;
22899
+ if (!codes.some((c) => within8(RANDAL, c))) return false;
22900
+ if (codes.some((c) => within8(LCAT, c))) return true;
22901
+ return !within8(RANDAL, codes[0] ?? 0) || !within8(RANDAL, codes.at(-1) ?? 0);
22712
22902
  }
22713
22903
 
22714
22904
  // src/kernel/net/sealed-scram.ts
@@ -24683,7 +24873,7 @@ function isMissing(err) {
24683
24873
  if (err?.code === "ENOENT") return true;
24684
24874
  return err instanceof Error && err.message.startsWith("ENOENT");
24685
24875
  }
24686
- function within8(path, root) {
24876
+ function within9(path, root) {
24687
24877
  return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
24688
24878
  }
24689
24879
  var VfsNode = class {
@@ -25064,7 +25254,7 @@ var VfsNodes = class {
25064
25254
  return run2;
25065
25255
  }
25066
25256
  async entryKey(path) {
25067
- const parent = parentOf4(path);
25257
+ const parent = parentOf5(path);
25068
25258
  const dir = await this.real(parent, true, () => realDir(this.fs, parent));
25069
25259
  return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
25070
25260
  }
@@ -25220,12 +25410,12 @@ var VfsNodes = class {
25220
25410
  return node;
25221
25411
  }
25222
25412
  holds(prefix) {
25223
- for (const path of this.byPath.keys()) if (within8(path, prefix)) return true;
25413
+ for (const path of this.byPath.keys()) if (within9(path, prefix)) return true;
25224
25414
  return false;
25225
25415
  }
25226
25416
  writes(prefix) {
25227
25417
  for (const [path, node] of this.byPath)
25228
- if (node.writers > 0 && within8(path, prefix)) return true;
25418
+ if (node.writers > 0 && within9(path, prefix)) return true;
25229
25419
  return false;
25230
25420
  }
25231
25421
  closed(node, writable2 = false) {
@@ -25308,17 +25498,17 @@ var VfsNodes = class {
25308
25498
  this.byPath.get(path)?.reread();
25309
25499
  }
25310
25500
  rewrittenUnder(tree2) {
25311
- for (const [p, node] of this.byPath) if (within8(p, tree2)) node.reread();
25501
+ for (const [p, node] of this.byPath) if (within9(p, tree2)) node.reread();
25312
25502
  }
25313
25503
  doubted(path) {
25314
- for (const [p, node] of this.byPath) if (within8(p, path)) node.doubted();
25504
+ for (const [p, node] of this.byPath) if (within9(p, path)) node.doubted();
25315
25505
  }
25316
25506
  async flush(path) {
25317
- for (const [p, node] of this.byPath) if (within8(p, path)) await node.serial(() => node.flush());
25507
+ for (const [p, node] of this.byPath) if (within9(p, path)) await node.serial(() => node.flush());
25318
25508
  }
25319
25509
  async unlinking(path) {
25320
25510
  for (const [held, node] of [...this.byPath]) {
25321
- if (!within8(held, path)) continue;
25511
+ if (!within9(held, path)) continue;
25322
25512
  await node.serial(async () => {
25323
25513
  await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
25324
25514
  });
@@ -25326,14 +25516,14 @@ var VfsNodes = class {
25326
25516
  }
25327
25517
  revoke(prefix) {
25328
25518
  for (const [path, node] of [...this.byPath]) {
25329
- if (!within8(path, prefix)) continue;
25519
+ if (!within9(path, prefix)) continue;
25330
25520
  node.revoke();
25331
25521
  this.forget(node);
25332
25522
  }
25333
25523
  }
25334
25524
  unlinked(path) {
25335
25525
  for (const [held, node] of [...this.byPath]) {
25336
- if (!within8(held, path) || this.byPath.get(held) !== node) continue;
25526
+ if (!within9(held, path) || this.byPath.get(held) !== node) continue;
25337
25527
  if (!this.dropName(held, node)) this.orphan(node);
25338
25528
  }
25339
25529
  }
@@ -25341,8 +25531,8 @@ var VfsNodes = class {
25341
25531
  if (from === to) return;
25342
25532
  const moved2 = [];
25343
25533
  for (const [p, node] of [...this.byPath]) {
25344
- if (within8(p, from)) moved2.push([p, node]);
25345
- else if (within8(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
25534
+ if (within9(p, from)) moved2.push([p, node]);
25535
+ else if (within9(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
25346
25536
  this.orphan(node);
25347
25537
  }
25348
25538
  }
@@ -25368,7 +25558,7 @@ function errCode(err) {
25368
25558
  return err?.code;
25369
25559
  }
25370
25560
  async function targetDir(fs, path) {
25371
- return parentOf4(await realTarget(fs, path).catch(() => path));
25561
+ return parentOf5(await realTarget(fs, path).catch(() => path));
25372
25562
  }
25373
25563
  async function refuseReadonly(fs, path, flags2, opts = {}) {
25374
25564
  const writes = (flags2 & O_ACCMODE) !== 0 || opts.truncate === true;
@@ -25631,7 +25821,7 @@ function isWatchSyscall(req) {
25631
25821
  return WATCHING.has(req.op);
25632
25822
  }
25633
25823
  var WATCHES_MAX = 8192;
25634
- var parentOf9 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
25824
+ var parentOf10 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
25635
25825
  function visibleTo2(watch, root, visible) {
25636
25826
  return {
25637
25827
  close: () => watch.close(),
@@ -25642,7 +25832,7 @@ function visibleTo2(watch, root, visible) {
25642
25832
  notes.map(async (note) => {
25643
25833
  if (!("filename" in note) || !note.filename.includes("/")) return true;
25644
25834
  const at = watch.root?.() ?? root;
25645
- const dir = parentOf9(`${at === "/" ? "" : at}/${note.filename}`);
25835
+ const dir = parentOf10(`${at === "/" ? "" : at}/${note.filename}`);
25646
25836
  if (!seen.has(dir)) seen.set(dir, visible(dir));
25647
25837
  return seen.get(dir);
25648
25838
  })
@@ -29587,7 +29777,7 @@ function sealedFetcher(hook, stop, idleMs = BODY_IDLE_MS) {
29587
29777
  var SWAP_GAP_MS = 2e3;
29588
29778
  var STAGES = ["_tmp_", "_pacquet-stage_"];
29589
29779
  function staged(source, target) {
29590
- if (parentOf4(source) !== parentOf4(target)) return false;
29780
+ if (parentOf5(source) !== parentOf5(target)) return false;
29591
29781
  const name2 = target.slice(target.lastIndexOf("/") + 1);
29592
29782
  const from = source.slice(source.lastIndexOf("/") + 1);
29593
29783
  return from.startsWith(`.${name2}-`) || STAGES.some((stage) => from.startsWith(`${name2}${stage}`));
@@ -29684,7 +29874,7 @@ var PackageSwaps = class {
29684
29874
 
29685
29875
  // src/kernel/rename-log.ts
29686
29876
  var RENAME_LOG_SIZE = 4096;
29687
- function within9(path, root) {
29877
+ function within10(path, root) {
29688
29878
  return path === root || path.startsWith(root === "/" ? "/" : `${root}/`);
29689
29879
  }
29690
29880
  var RenameLog = class {
@@ -29711,9 +29901,9 @@ var RenameLog = class {
29711
29901
  const renames = [];
29712
29902
  let paths = [...held];
29713
29903
  for (const { seq: seq2, from, to } of this.entries) {
29714
- if (seq2 <= seen || !paths.some((path) => within9(path, from))) continue;
29904
+ if (seq2 <= seen || !paths.some((path) => within10(path, from))) continue;
29715
29905
  renames.push([from, to]);
29716
- paths = paths.map((path) => within9(path, from) ? to + path.slice(from.length) : path);
29906
+ paths = paths.map((path) => within10(path, from) ? to + path.slice(from.length) : path);
29717
29907
  }
29718
29908
  return { now, renames };
29719
29909
  }
@@ -31587,6 +31777,104 @@ function splitWords(text) {
31587
31777
  function quoteWord(word) {
31588
31778
  return PLAIN3.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
31589
31779
  }
31780
+ function addPart(word, part) {
31781
+ const last = word.length - 1;
31782
+ if (part === null) {
31783
+ if (word[last] !== null) word.push(null);
31784
+ } else if (typeof word[last] === "string") word[last] += part;
31785
+ else word.push(part);
31786
+ }
31787
+ function addPlain(word, plain3) {
31788
+ for (const [i, part] of plain3.split("*").entries()) {
31789
+ if (i > 0) addPart(word, null);
31790
+ if (part !== "") addPart(word, part);
31791
+ }
31792
+ }
31793
+ function splitPatterns(text) {
31794
+ const piece = /'([^']*)'|"((?:[^"\\]|\\[\s\S])*)"|\\([\s\S])|([^\s'"\\]+)|(\s+)/y;
31795
+ const words2 = [];
31796
+ let word;
31797
+ while (piece.lastIndex < text.length) {
31798
+ const m = piece.exec(text);
31799
+ if (!m) return void 0;
31800
+ const [, single, double, escaped, plain3, space] = m;
31801
+ if (space !== void 0) {
31802
+ if (word !== void 0) words2.push(word);
31803
+ word = void 0;
31804
+ continue;
31805
+ }
31806
+ word ??= [];
31807
+ if (plain3 !== void 0) addPlain(word, plain3);
31808
+ else addPart(word, single ?? (double !== void 0 ? unescaped(double) : escaped));
31809
+ }
31810
+ if (word !== void 0) words2.push(word);
31811
+ return words2;
31812
+ }
31813
+ var literal2 = (pattern) => pattern.map((p) => p ?? "*").join("");
31814
+ var isAny = (pattern) => pattern?.length === 1 && pattern[0] === null;
31815
+ function literalWord(word) {
31816
+ if (!word.includes("*")) return quoteWord(word);
31817
+ const bare = word.replaceAll("*", "");
31818
+ return bare === "" || PLAIN3.test(bare) ? word.replaceAll("*", "\\*") : quoteWord(word);
31819
+ }
31820
+ function patternWord(pattern) {
31821
+ if (pattern.length === 1 && typeof pattern[0] === "string") return literalWord(pattern[0]);
31822
+ return pattern.map((p) => p === null ? "*" : literalWord(p)).join("");
31823
+ }
31824
+ function globMatch(items, subjects, star, one) {
31825
+ let i = 0;
31826
+ let j = 0;
31827
+ let back = -1;
31828
+ let from = 0;
31829
+ while (j < subjects) {
31830
+ if (i < items && star(i)) {
31831
+ back = i++;
31832
+ from = j;
31833
+ } else if (i < items && one(i, j)) {
31834
+ i++;
31835
+ j++;
31836
+ } else if (back >= 0) {
31837
+ i = back + 1;
31838
+ j = ++from;
31839
+ } else return false;
31840
+ }
31841
+ while (i < items && star(i)) i++;
31842
+ return i === items;
31843
+ }
31844
+ function wordMatch(pattern, arg) {
31845
+ if (pattern.every((p) => p !== null)) return literal2(pattern) === arg;
31846
+ const chars = pattern.flatMap((p) => p === null ? [null] : [...p]);
31847
+ const subject = [...arg];
31848
+ return globMatch(
31849
+ chars.length,
31850
+ subject.length,
31851
+ (i) => chars[i] === null,
31852
+ (i, j) => chars[i] === subject[j]
31853
+ );
31854
+ }
31855
+ function commandMatches(cmnd, argv) {
31856
+ if (cmnd === "*") return true;
31857
+ const [path, ...args] = argv;
31858
+ if (path !== cmnd.path) return false;
31859
+ const want = cmnd.args;
31860
+ if (want === void 0) return true;
31861
+ return globMatch(
31862
+ want.length,
31863
+ args.length,
31864
+ (i) => isAny(want[i]),
31865
+ (i, j) => wordMatch(want[i], args[j])
31866
+ );
31867
+ }
31868
+ function commandText(cmnd) {
31869
+ const path = quoteWord(cmnd.path);
31870
+ if (cmnd.args === void 0) return path;
31871
+ if (cmnd.args.length === 0) return `${path} ""`;
31872
+ return [path, ...cmnd.args.map(patternWord)].join(" ");
31873
+ }
31874
+ function recordedCommand(argv) {
31875
+ const [path = "", ...args] = argv;
31876
+ return commandText({ path, args: args.map((a) => [a]) });
31877
+ }
31590
31878
  function validWho(who) {
31591
31879
  if (who === "ALL") return true;
31592
31880
  return NAME3.test(who.startsWith("%") ? who.slice(1) : who);
@@ -31607,9 +31895,23 @@ function parseRule(line) {
31607
31895
  if (cwd === "") return void 0;
31608
31896
  const rule = { nopasswd: !!nopasswd, who, target, ...cwd !== void 0 ? { cwd } : {} };
31609
31897
  if (rest2.trim() === "*") return { ...rule, cmnd: "*" };
31610
- const cmnd = splitWords(rest2);
31611
- if (!cmnd?.[0]?.startsWith("/")) return void 0;
31612
- return { ...rule, cmnd };
31898
+ const words2 = splitPatterns(rest2);
31899
+ const [first, ...args] = words2 ?? [];
31900
+ const path = first ? literal2(first) : "";
31901
+ if (!path.startsWith("/")) return void 0;
31902
+ const only = args.length === 1 && args[0]?.length === 1 && args[0][0] === "";
31903
+ if (only && /\s""$/.test(rest2.trim())) return { ...rule, cmnd: { path, args: [] } };
31904
+ return { ...rule, cmnd: args.length === 0 ? { path } : { path, args } };
31905
+ }
31906
+ function recordedBare(line, file, rule) {
31907
+ if (rule.cmnd === "*" || rule.cmnd.args !== void 0 || !rule.nopasswd) return false;
31908
+ if (file !== `${SUDOERS_D}/${rule.who}` || !NAME3.test(rule.who) || !NAME3.test(rule.target)) {
31909
+ return false;
31910
+ }
31911
+ const head = `NOPASSWD ${rule.who} (${rule.target}) `;
31912
+ const cmnd = `Cmnd ${quoteWord(rule.cmnd.path)}`;
31913
+ const cwd = rule.cwd === void 0 ? "" : `Cwd ${quoteWord(rule.cwd)} `;
31914
+ return line === `${head}${cwd}${cmnd}`;
31613
31915
  }
31614
31916
  function flags(parsed2, arg, next3) {
31615
31917
  for (let j = 1; j < arg.length; j++) {
@@ -31647,7 +31949,7 @@ function parseArgs(args) {
31647
31949
  return parsed2;
31648
31950
  }
31649
31951
  function described(rule) {
31650
- const cmnd = rule.cmnd === "*" ? "ALL" : rule.cmnd.map(quoteWord).join(" ");
31952
+ const cmnd = rule.cmnd === "*" ? "ALL" : commandText(rule.cmnd);
31651
31953
  const cwd = rule.cwd === void 0 ? "" : `CWD=${quoteWord(rule.cwd)} `;
31652
31954
  return ` (${rule.target}) ${cwd}${rule.nopasswd ? "NOPASSWD: " : ""}${cmnd}`;
31653
31955
  }
@@ -31693,7 +31995,9 @@ var Sudo = class {
31693
31995
  const rules = [];
31694
31996
  for (const [n, line] of text.split("\n").entries()) {
31695
31997
  const rule = parseRule(line);
31696
- if (rule) rules.push(rule);
31998
+ if (rule && recordedBare(line, path, rule) && rule.cmnd !== "*") {
31999
+ rules.push({ ...rule, cmnd: { path: rule.cmnd.path, args: [] } });
32000
+ } else if (rule) rules.push(rule);
31697
32001
  else if (rule === void 0) this.warn(`sudo: ${path}:${n + 1}: bad line skipped: ${line}`);
31698
32002
  }
31699
32003
  return rules;
@@ -31789,7 +32093,7 @@ var Sudo = class {
31789
32093
  if (untrusted2) return untrusted2;
31790
32094
  const argv = [path, ...args];
31791
32095
  const matching = rules.filter(
31792
- (rule) => applies(rule) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && (rule.cmnd === "*" || rule.cmnd.length === argv.length && rule.cmnd.every((w, i) => w === argv[i]))
32096
+ (rule) => applies(rule) && (rule.target === "ALL" || rule.target === target.name) && (rule.cwd === void 0 || rule.cwd === cwd) && commandMatches(rule.cmnd, argv)
31793
32097
  );
31794
32098
  const grant = () => this.grant(self, target, cred, parsed2.command, path, cwd, call.term);
31795
32099
  if (root || matching.some((rule) => rule.nopasswd)) return grant();
@@ -31886,7 +32190,7 @@ ${lines2}
31886
32190
  const st = await fs.lstat(path).catch(() => void 0);
31887
32191
  if (st && !await this.trusted(path)) return false;
31888
32192
  const old = st ? await fs.readFile(path) : "";
31889
- const words2 = argv.map(quoteWord).join(" ");
32193
+ const words2 = recordedCommand(argv);
31890
32194
  const line = `NOPASSWD ${self.name} (${target.name}) Cwd ${quoteWord(cwd)} Cmnd ${words2}
31891
32195
  `;
31892
32196
  const text = old === "" || old.endsWith("\n") ? `${old}${line}` : `${old}
@@ -32694,6 +32998,7 @@ var Launcher = class {
32694
32998
  this.rewritten(change.paths, change.trees);
32695
32999
  this.watchers.heard(change.content ? "change" : "rename", change.paths);
32696
33000
  this.watchers.heard("rename", change.trees ?? []);
33001
+ this.watchers.peerChanged([...change.paths, ...change.trees ?? []]);
32697
33002
  if (change.outside && this.catalogChanged([...change.paths, ...change.trees ?? []])) {
32698
33003
  this.resetCatalogs();
32699
33004
  }
@@ -33447,9 +33752,9 @@ var Launcher = class {
33447
33752
  }
33448
33753
  packageRootIs(path) {
33449
33754
  const at = normalizePath(path);
33450
- const within10 = `${at}/`;
33755
+ const within11 = `${at}/`;
33451
33756
  const roots = [this.modulesDir, ...this.appended];
33452
- const root = PACKAGE_ROOT.exec(within10)?.[1] ?? packageRootOf(within10, roots);
33757
+ const root = PACKAGE_ROOT.exec(within11)?.[1] ?? packageRootOf(within11, roots);
33453
33758
  return root === at;
33454
33759
  }
33455
33760
  packageRoots(planned) {
@@ -33648,6 +33953,7 @@ var Launcher = class {
33648
33953
  fsCalls: servedFsCalls(),
33649
33954
  opfsCalls: this.rootFs.opfsCalls?.() ?? 0,
33650
33955
  opfsOps: this.rootFs.opfsOps?.() ?? 0,
33956
+ meta: this.rootFs.metaTransactions?.(),
33651
33957
  cwdChecks: this.cwds.checks,
33652
33958
  renameAcks: this.rootFs.renameWaits?.(),
33653
33959
  swaps: this.swaps.counts(),
@@ -34391,7 +34697,7 @@ var Launcher = class {
34391
34697
  const path = normalizePath(given);
34392
34698
  if (!await this.base.exists(path)) return void 0;
34393
34699
  if (path === "/") return path;
34394
- return joinName(await realDir(this.base, parentOf4(path)), baseName2(path));
34700
+ return joinName(await realDir(this.base, parentOf5(path)), baseName2(path));
34395
34701
  }
34396
34702
  pnpmReady() {
34397
34703
  if (this.pnpmMade !== void 0) return this.pnpmMade;