@ai-ecoverse/slicc-kernel 1.172.1 → 1.173.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.
@@ -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,18 +2204,18 @@ 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);
2033
2211
  }
2034
2212
  const found = await this.locate(path, change.follow !== false);
2035
2213
  this.refuse(found.path);
2036
- const [stored] = found.link ? [found.link] : await this.meta.get([found.path]);
2037
- const current = await this.ownerAt(found.path, stored);
2214
+ const [stored2] = found.link ? [found.link] : await this.meta.get([found.path]);
2215
+ const current = await this.ownerAt(found.path, stored2);
2038
2216
  const next3 = { uid: change.uid ?? current.uid, gid: change.gid ?? current.gid };
2039
2217
  const handing = found.handle?.kind === "directory" && !sameOwner(next3, current);
2040
- const heir = change.kind === "adopt" ? void 0 : stored?.heir ?? (handing ? [current.uid, current.gid] : void 0);
2218
+ const heir = change.kind === "adopt" ? void 0 : stored2?.heir ?? (handing ? [current.uid, current.gid] : void 0);
2041
2219
  const ctimeMs = Date.now();
2042
2220
  await this.meta.update(found.path, (entry) => {
2043
2221
  const { heir: _, ...rest2 } = entry ?? found.link ?? { path: found.path };
@@ -2053,8 +2231,8 @@ var OpfsFs = class {
2053
2231
  }
2054
2232
  async movedOwner(source) {
2055
2233
  if (!this.owners) return void 0;
2056
- const [stored] = source.link ? [source.link] : await this.meta.get([source.path]);
2057
- return ownOf(stored) ? void 0 : this.ownerAt(source.path, stored);
2234
+ const [stored2] = source.link ? [source.link] : await this.meta.get([source.path]);
2235
+ return ownOf(stored2) ? void 0 : this.ownerAt(source.path, stored2);
2058
2236
  }
2059
2237
  async walk(parts2, from, start2) {
2060
2238
  let dir = start2;
@@ -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) {
@@ -3905,6 +4086,38 @@ for (const [names2, op] of [
3905
4086
  }
3906
4087
  }
3907
4088
 
4089
+ // src/fs/portable.ts
4090
+ var privateTop = () => navigator.storage.getDirectory();
4091
+ function clonable(handle) {
4092
+ const { port1, port2 } = new MessageChannel();
4093
+ try {
4094
+ port1.postMessage(handle);
4095
+ return true;
4096
+ } catch (err) {
4097
+ if (err?.name === "DataCloneError") return false;
4098
+ throw err;
4099
+ } finally {
4100
+ port1.close();
4101
+ port2.close();
4102
+ }
4103
+ }
4104
+ async function portable(handle, top = privateTop) {
4105
+ if (clonable(handle)) return { handle };
4106
+ const path = await (await top()).resolve(handle);
4107
+ if (path === null) {
4108
+ throw new Error(
4109
+ "this browser cannot hand a folder to a worker unless it is in the origin private file system"
4110
+ );
4111
+ }
4112
+ return { path };
4113
+ }
4114
+ async function directoryAt(top, path) {
4115
+ let dir = top;
4116
+ for (const name2 of path) dir = await dir.getDirectoryHandle(name2);
4117
+ return dir;
4118
+ }
4119
+ var samePath = (a, b) => a.length === b.length && a.every((name2, at) => name2 === b[at]);
4120
+
3908
4121
  // src/fs/roots.ts
3909
4122
  var ROOTS_DB = "slicc-kernel-roots";
3910
4123
  var ROOTS_STORE = "roots";
@@ -3926,6 +4139,10 @@ async function same(a, b) {
3926
4139
  return false;
3927
4140
  }
3928
4141
  }
4142
+ async function matches(record2, root, at) {
4143
+ if (record2.handle) return same(record2.handle, root);
4144
+ return "path" in at && record2.path !== void 0 && samePath(record2.path, at.path);
4145
+ }
3929
4146
  var nameOf = (id2) => `${META_DB}:${id2}`;
3930
4147
  function done(tx) {
3931
4148
  return new Promise((resolve2, reject) => {
@@ -3959,18 +4176,19 @@ async function seed(factory, name2) {
3959
4176
  to.close();
3960
4177
  }
3961
4178
  }
3962
- async function rootId(factory, root, locks = globalThis.navigator?.locks) {
4179
+ async function rootId(factory, root, locks = globalThis.navigator?.locks, top) {
3963
4180
  const find = async () => {
4181
+ const at = await portable(root, top);
3964
4182
  const db = await open(factory);
3965
4183
  try {
3966
4184
  const all = await request(
3967
4185
  db.transaction(ROOTS_STORE, "readonly").objectStore(ROOTS_STORE).getAll()
3968
4186
  );
3969
- for (const record2 of all) if (await same(record2.handle, root)) return record2.id;
4187
+ for (const record2 of all) if (await matches(record2, root, at)) return record2.id;
3970
4188
  const id2 = crypto.randomUUID();
3971
4189
  if (await used(root)) await seed(factory, nameOf(id2));
3972
4190
  const tx = db.transaction(ROOTS_STORE, "readwrite");
3973
- tx.objectStore(ROOTS_STORE).put({ id: id2, handle: root });
4191
+ tx.objectStore(ROOTS_STORE).put({ id: id2, ...at });
3974
4192
  await done(tx);
3975
4193
  return id2;
3976
4194
  } finally {
@@ -4986,7 +5204,7 @@ function portFacadeHook(ask, gone) {
4986
5204
 
4987
5205
  // src/fs/walk.ts
4988
5206
  var MAX_LINKS3 = 40;
4989
- function parentOf4(path) {
5207
+ function parentOf5(path) {
4990
5208
  return path.slice(0, path.lastIndexOf("/")) || "/";
4991
5209
  }
4992
5210
  async function mustBeDir(fs, path) {
@@ -5004,7 +5222,7 @@ async function realDir(fs, dir, budget = { hops: 0 }) {
5004
5222
  const part = queue.shift();
5005
5223
  if (part === ".") continue;
5006
5224
  if (part === "..") {
5007
- at = parentOf4(at);
5225
+ at = parentOf5(at);
5008
5226
  continue;
5009
5227
  }
5010
5228
  const next3 = at === "/" ? `/${part}` : `${at}/${part}`;
@@ -5026,7 +5244,7 @@ function joinName(dir, name2) {
5026
5244
  }
5027
5245
  async function realTarget(fs, path) {
5028
5246
  const budget = { hops: 0 };
5029
- let dir = await realDir(fs, parentOf4(path), budget);
5247
+ let dir = await realDir(fs, parentOf5(path), budget);
5030
5248
  let name2 = path.slice(path.lastIndexOf("/") + 1);
5031
5249
  for (; ; ) {
5032
5250
  const link2 = await linkOf(fs, joinName(dir, name2));
@@ -5034,7 +5252,7 @@ async function realTarget(fs, path) {
5034
5252
  if (budget.hops >= MAX_LINKS3) throw fsError("ELOOP", path);
5035
5253
  budget.hops++;
5036
5254
  const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
5037
- dir = await realDir(fs, parentOf4(target), budget);
5255
+ dir = await realDir(fs, parentOf5(target), budget);
5038
5256
  name2 = target.slice(target.lastIndexOf("/") + 1);
5039
5257
  }
5040
5258
  return joinName(dir, name2);
@@ -5111,7 +5329,7 @@ async function walk(fs, cred, path, follow2, links) {
5111
5329
  if (!dir) return { path: joinName(at, [part, ...queue].join("/")), perm: void 0 };
5112
5330
  if (!permits(cred, dir, X_OK)) return { path, perm: void 0, denied: at };
5113
5331
  if (part === "..") {
5114
- at = parentOf4(at);
5332
+ at = parentOf5(at);
5115
5333
  continue;
5116
5334
  }
5117
5335
  const next3 = joinName(at, part);
@@ -5232,7 +5450,7 @@ function deny(g, op, path, errno3 = "EACCES") {
5232
5450
  }
5233
5451
  var FENCE_HOPS = 40;
5234
5452
  async function entryAt(g, path) {
5235
- const parent = parentOf4(path);
5453
+ const parent = parentOf5(path);
5236
5454
  const real = await g.fs.realpath?.(parent, true).catch(() => void 0);
5237
5455
  return real ? joinName(real, path.slice(path.lastIndexOf("/") + 1)) : path;
5238
5456
  }
@@ -5243,7 +5461,7 @@ async function fencedNames(g, path) {
5243
5461
  names2.push(at);
5244
5462
  const link2 = await g.fs.readlink(at).catch(() => void 0);
5245
5463
  if (link2 === void 0) break;
5246
- at = await entryAt(g, normalizePath(g.fs.resolvePath(parentOf4(at), link2)));
5464
+ at = await entryAt(g, normalizePath(g.fs.resolvePath(parentOf5(at), link2)));
5247
5465
  }
5248
5466
  return names2;
5249
5467
  }
@@ -5294,7 +5512,7 @@ async function unwritable(g, op, path, dir) {
5294
5512
  }
5295
5513
  async function inParent(g, op, path, old = false) {
5296
5514
  await fence(g, op, path);
5297
- 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));
5298
5516
  if (old !== true) {
5299
5517
  if (await unwritable(g, op, path, dir)) deny(g, op, path);
5300
5518
  else if (old === "replaced" && dir.denied === void 0) await stickyEntry(g, op, path, dir);
@@ -5306,7 +5524,7 @@ async function inParent(g, op, path, old = false) {
5306
5524
  if (!lookup2 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
5307
5525
  }
5308
5526
  async function makeIn(g, op, path, parents = true) {
5309
- for (let dir = parentOf4(path); ; dir = parentOf4(dir)) {
5527
+ for (let dir = parentOf5(path); ; dir = parentOf5(dir)) {
5310
5528
  const walked = await access(g.fs, g.cred, dir, W_OK | X_OK, linked(g, DIR));
5311
5529
  if (await unwritable(g, op, path, walked)) {
5312
5530
  if (!await present(g.fs, path, true)) deny(g, op, path);
@@ -5334,7 +5552,7 @@ async function renamable(g, from, to) {
5334
5552
  if (from === to) return void await need(g, "rename", from, 0, { follow: false });
5335
5553
  await inParent(g, "rename", from, true);
5336
5554
  await inParent(g, "rename", to, "replaced");
5337
- if (g.cred.euid === 0 || parentOf4(from) === parentOf4(to)) return;
5555
+ if (g.cred.euid === 0 || parentOf5(from) === parentOf5(to)) return;
5338
5556
  const st = await g.fs.lstat(from).catch(() => void 0);
5339
5557
  if (st?.isDirectory && !permits(g.cred, permOf(st), W_OK)) deny(g, "rename", from);
5340
5558
  }
@@ -5401,7 +5619,7 @@ async function writable(g, op, path) {
5401
5619
  }
5402
5620
  async function creatable(g, op, path) {
5403
5621
  const target = await need(g, op, path, 0);
5404
- 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);
5405
5623
  }
5406
5624
  async function openable(g, op, path, want, create) {
5407
5625
  const st = create ? await g.fs.stat(path).catch(() => void 0) : void 0;
@@ -6157,7 +6375,7 @@ function parsePrefix(text) {
6157
6375
  if (length2 < 0 || length2 > max) return void 0;
6158
6376
  return { ...parsed2, length: length2 };
6159
6377
  }
6160
- function within3(prefix, addr) {
6378
+ function within4(prefix, addr) {
6161
6379
  if (prefix.v6 !== addr.v6) return false;
6162
6380
  const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
6163
6381
  return addr.bits >> shift === prefix.bits >> shift;
@@ -6197,9 +6415,9 @@ var Routes = class {
6197
6415
  if (!addr) return "unreachable";
6198
6416
  if (addr.v6 && addr.bits === 1n) return "loopback";
6199
6417
  if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
6200
- if (RESERVED.some((p) => within3(p, addr))) return "reserved";
6418
+ if (RESERVED.some((p) => within4(p, addr))) return "reserved";
6201
6419
  if (addr.v6 && !this.ipv6) return "unreachable";
6202
- 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";
6203
6421
  return "unreachable";
6204
6422
  }
6205
6423
  };
@@ -8151,7 +8369,7 @@ function hostRegistries(send2, gone, port) {
8151
8369
  function visibleTo(g, notify) {
8152
8370
  if (!g?.dac.enforcing) return notify;
8153
8371
  let queue = Promise.resolve();
8154
- 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;
8155
8373
  return (change) => {
8156
8374
  queue = queue.then(async () => {
8157
8375
  if (!("paths" in change)) return notify(change);
@@ -8169,7 +8387,7 @@ async function watchOp(l, req, watches, send2, g) {
8169
8387
  }
8170
8388
  if (g) for (const path of req.paths) await readable(g, "watch", l.fs.resolvePath("/", path));
8171
8389
  const notify = visibleTo(g, (change) => send2({ watch: req.id, change }));
8172
- 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);
8173
8391
  watches.set(req.id, unwatch);
8174
8392
  return { result: req.id };
8175
8393
  }
@@ -8522,7 +8740,7 @@ function satisfies(version, range2) {
8522
8740
  }
8523
8741
 
8524
8742
  // src/version.ts
8525
- var KERNEL_VERSION = true ? "1.172.1" : "0.0.0-dev";
8743
+ var KERNEL_VERSION = true ? "1.173.0" : "0.0.0-dev";
8526
8744
  var OS_RELEASE = "6.0.0-slicc";
8527
8745
  function procVersion() {
8528
8746
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -9433,7 +9651,7 @@ var PathMarks = class {
9433
9651
  }
9434
9652
  changedSince(path, gen) {
9435
9653
  if (gen < this.floor) return true;
9436
- for (let p = path; this.touched.size > 0; p = parentOf5(p)) {
9654
+ for (let p = path; this.touched.size > 0; p = parentOf6(p)) {
9437
9655
  if ((this.touched.get(p) ?? 0) > gen) return true;
9438
9656
  if (p === "/") return false;
9439
9657
  }
@@ -9468,7 +9686,7 @@ var PathMarks = class {
9468
9686
  });
9469
9687
  }
9470
9688
  inFlight(path) {
9471
- for (let p = path; this.flying.size > 0; p = parentOf5(p)) {
9689
+ for (let p = path; this.flying.size > 0; p = parentOf6(p)) {
9472
9690
  if (this.flying.has(p)) return true;
9473
9691
  if (p === "/") return false;
9474
9692
  }
@@ -9488,9 +9706,9 @@ var LOOKUP_OPS = [
9488
9706
  "readlink",
9489
9707
  "realpath"
9490
9708
  ];
9491
- var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
9709
+ var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
9492
9710
  var TOUCHED_MAX = 4096;
9493
- var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
9711
+ var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
9494
9712
  var PEER_RM_MS = 1e3;
9495
9713
  var SHARED_ACK_MS = 24 * 60 * 60 * 1e3;
9496
9714
  var delay = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
@@ -9570,14 +9788,14 @@ var UnlinkedKeeper = class {
9570
9788
  linked = /* @__PURE__ */ new Map();
9571
9789
  dirOwner;
9572
9790
  peers;
9573
- constructor(fs, holders, rootOf, options = {}) {
9791
+ constructor(fs, holders, rootOf2, options = {}) {
9574
9792
  this.peers = options.peers;
9575
9793
  this.marks = options.marks ?? new PathMarks();
9576
9794
  const { locks, owner } = options;
9577
9795
  this.dirOwner = options.dirOwner;
9578
9796
  this.fs = fs;
9579
9797
  this.holders = holders;
9580
- this.rootOf = rootOf;
9798
+ this.rootOf = rootOf2;
9581
9799
  this.locks = locks;
9582
9800
  this.busy = options.busy ?? (() => false);
9583
9801
  this.owner = owner;
@@ -9678,7 +9896,7 @@ var UnlinkedKeeper = class {
9678
9896
  };
9679
9897
  }
9680
9898
  async settled(path) {
9681
- for (let p = path; this.replacing.size > 0; p = parentOf5(p)) {
9899
+ for (let p = path; this.replacing.size > 0; p = parentOf6(p)) {
9682
9900
  await this.replacing.get(p)?.done;
9683
9901
  if (p === "/") return;
9684
9902
  }
@@ -9696,7 +9914,7 @@ var UnlinkedKeeper = class {
9696
9914
  return this.marks.inFlight(path);
9697
9915
  }
9698
9916
  missed(path) {
9699
- for (let p = path; this.replacing.size > 0; p = parentOf5(p)) {
9917
+ for (let p = path; this.replacing.size > 0; p = parentOf6(p)) {
9700
9918
  if (this.replacing.get(p)?.sealed) return true;
9701
9919
  if (p === "/") return false;
9702
9920
  }
@@ -9716,7 +9934,7 @@ var UnlinkedKeeper = class {
9716
9934
  for (const holder of this.holders) {
9717
9935
  if (holder.owns(target)) continue;
9718
9936
  for (const path of holder.held()) {
9719
- if (!within4(path, target) || holder.isKept(path)) continue;
9937
+ if (!within5(path, target) || holder.isKept(path)) continue;
9720
9938
  held.set(path, [...held.get(path) ?? [], holder]);
9721
9939
  }
9722
9940
  }
@@ -9796,7 +10014,7 @@ var UnlinkedKeeper = class {
9796
10014
  await this.serial(async () => {
9797
10015
  await Promise.allSettled([this.fs.rename(file.hidden, path)]);
9798
10016
  this.unlinkedName(file.hidden);
9799
- await this.tidy(parentOf5(file.hidden));
10017
+ await this.tidy(parentOf6(file.hidden));
9800
10018
  });
9801
10019
  }
9802
10020
  done5();
@@ -10035,7 +10253,7 @@ var UnlinkedKeeper = class {
10035
10253
  }
10036
10254
  await this.fs.rm(hidden).catch(() => void 0);
10037
10255
  this.unlinkedName(hidden);
10038
- await this.tidy(parentOf5(hidden));
10256
+ await this.tidy(parentOf6(hidden));
10039
10257
  }
10040
10258
  async linkedName(hidden) {
10041
10259
  if (!mayLink(this.fs, hidden)) return;
@@ -11584,7 +11802,7 @@ function devCreation(path, cred) {
11584
11802
  var HOSTNAME_PATH = "/etc/hostname";
11585
11803
  var S_IFREG3 = 32768;
11586
11804
  var MAX_HOPS2 = 40;
11587
- var parentOf6 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
11805
+ var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
11588
11806
  var baseOf2 = (path) => path.slice(path.lastIndexOf("/") + 1);
11589
11807
  function missing5(err) {
11590
11808
  return err?.code === "ENOENT";
@@ -11614,7 +11832,7 @@ function linkFollower(fs, files) {
11614
11832
  );
11615
11833
  const viaParent = async (path) => {
11616
11834
  if (!bases.has(baseOf2(path)) || !fs.realpath) return path;
11617
- const parent = parentOf6(path);
11835
+ const parent = parentOf7(path);
11618
11836
  const dir = await fs.realpath(parent, true).catch(() => parent);
11619
11837
  return fs.resolvePath(dir, baseOf2(path));
11620
11838
  };
@@ -11623,7 +11841,7 @@ function linkFollower(fs, files) {
11623
11841
  for (let hop = 0; hop < MAX_HOPS2 && !files.has(current); hop++) {
11624
11842
  const link2 = await linkOf2(current);
11625
11843
  if (link2 === void 0) return viaParent(current);
11626
- current = fs.resolvePath(parentOf6(current), link2);
11844
+ current = fs.resolvePath(parentOf7(current), link2);
11627
11845
  }
11628
11846
  return current;
11629
11847
  };
@@ -11704,7 +11922,7 @@ var DEFAULT_UMASK = 18;
11704
11922
  function isModule(bytes) {
11705
11923
  return MAGIC.every((b, i) => bytes[i] === b);
11706
11924
  }
11707
- function within5(path, dir) {
11925
+ function within6(path, dir) {
11708
11926
  return path.startsWith(dir === "/" ? "/" : `${dir}/`);
11709
11927
  }
11710
11928
  var FreshModules = class {
@@ -11728,10 +11946,10 @@ var FreshModules = class {
11728
11946
  const target = normalizePath(to);
11729
11947
  if (source === target) return;
11730
11948
  for (const key of [...this.fresh.keys()]) {
11731
- if (key === target || within5(key, target)) this.fresh.delete(key);
11949
+ if (key === target || within6(key, target)) this.fresh.delete(key);
11732
11950
  }
11733
11951
  for (const [key, fresh3] of [...this.fresh]) {
11734
- if (key !== source && !within5(key, source)) continue;
11952
+ if (key !== source && !within6(key, source)) continue;
11735
11953
  this.fresh.delete(key);
11736
11954
  this.fresh.set(target + key.slice(source.length), fresh3);
11737
11955
  }
@@ -11773,7 +11991,7 @@ var FreshModules = class {
11773
11991
  await fs.rm(path, options);
11774
11992
  const at = normalizePath(path);
11775
11993
  for (const key of [...this.fresh.keys()]) {
11776
- if (key === at || within5(key, at)) this.fresh.delete(key);
11994
+ if (key === at || within6(key, at)) this.fresh.delete(key);
11777
11995
  }
11778
11996
  },
11779
11997
  rename: async (from, to) => {
@@ -11878,10 +12096,10 @@ var WATCH_NOTES_LIMIT = 1e3;
11878
12096
  var baseOf3 = (path) => path.slice(path.lastIndexOf("/") + 1);
11879
12097
  var WATCH_BATCH_LIMIT = 1e3;
11880
12098
  var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
11881
- var parentOf7 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
12099
+ var parentOf8 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
11882
12100
  function touches2(watcher, changed) {
11883
12101
  return watcher.paths.some(
11884
- (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)
11885
12103
  );
11886
12104
  }
11887
12105
  var REALS_KEPT = 4096;
@@ -11909,7 +12127,7 @@ function routed(watch, root, type, path, seq2) {
11909
12127
  if (type === "rename") noted(watch, { ended: true, seq: seq2 });
11910
12128
  return true;
11911
12129
  }
11912
- 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 });
11913
12131
  else if (watch.recursive && under3(path, root)) {
11914
12132
  noted(watch, { type, filename: path.slice(root === "/" ? 1 : root.length + 1), seq: seq2 });
11915
12133
  } else return false;
@@ -11941,6 +12159,7 @@ var FsWatchers = class {
11941
12159
  const watcher = {
11942
12160
  paths: paths.map(normalizePath),
11943
12161
  recursive: options.recursive === true,
12162
+ peers: options.peers === true,
11944
12163
  notify,
11945
12164
  pending: /* @__PURE__ */ new Set(),
11946
12165
  scheduled: false
@@ -11994,7 +12213,7 @@ var FsWatchers = class {
11994
12213
  covers(path) {
11995
12214
  for (const watch of this.typed) {
11996
12215
  for (const root of watch.roots) {
11997
- if (path === root || parentOf7(path) === root || watch.recursive && under3(path, root))
12216
+ if (path === root || parentOf8(path) === root || watch.recursive && under3(path, root))
11998
12217
  return true;
11999
12218
  }
12000
12219
  }
@@ -12062,10 +12281,16 @@ var FsWatchers = class {
12062
12281
  this.report(paths);
12063
12282
  this.emit("change", paths);
12064
12283
  }
12284
+ peerChanged(paths) {
12285
+ if (paths.length > 0) this.notify(paths, true);
12286
+ }
12065
12287
  report(paths, notify = true) {
12066
12288
  for (const tap of this.taps) tap(paths);
12067
- if (!notify) return;
12289
+ if (notify) this.notify(paths);
12290
+ }
12291
+ notify(paths, peer = false) {
12068
12292
  for (const watcher of this.watchers) {
12293
+ if (peer && !watcher.peers) continue;
12069
12294
  for (const path of paths) {
12070
12295
  const changed = normalizePath(path);
12071
12296
  if (touches2(watcher, changed)) watcher.pending.add(changed);
@@ -13534,7 +13759,7 @@ function linkedTo(mount, path) {
13534
13759
  return own?.kind !== "directory" && (attr2.nlink ?? 1) > 1;
13535
13760
  };
13536
13761
  }
13537
- var within6 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
13762
+ var within7 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
13538
13763
  var HeldSet = class extends Set {
13539
13764
  idle;
13540
13765
  moved;
@@ -13570,7 +13795,7 @@ var HeldSet = class extends Set {
13570
13795
  }
13571
13796
  };
13572
13797
  function heldUnder(held, target) {
13573
- 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;
13574
13799
  return false;
13575
13800
  }
13576
13801
  var HeldPaths = class extends Map {
@@ -13611,7 +13836,7 @@ var HeldPaths = class extends Map {
13611
13836
  if (this.bound.size === 0) return;
13612
13837
  for (const [handle, key] of this.handles) {
13613
13838
  if (!this.bound.has(handle) || this.keptHandles.has(handle)) continue;
13614
- 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);
13615
13840
  }
13616
13841
  }
13617
13842
  boundIno(handle) {
@@ -13747,24 +13972,24 @@ var HeldPaths = class extends Map {
13747
13972
  return this.owning === path;
13748
13973
  }
13749
13974
  revoke(prefix) {
13750
- 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);
13751
13976
  }
13752
13977
  renamed(from, to) {
13753
13978
  for (const [path, count2] of [...this]) {
13754
- if (!within6(path, from)) continue;
13979
+ if (!within7(path, from)) continue;
13755
13980
  const moved2 = to + path.slice(from.length);
13756
13981
  this.delete(path);
13757
13982
  this.set(moved2, (this.get(moved2) ?? 0) + count2);
13758
13983
  if (this.revoked.delete(path)) this.revoked.add(moved2);
13759
13984
  }
13760
13985
  for (const [path, count2] of [...this.opens]) {
13761
- if (!within6(path, from)) continue;
13986
+ if (!within7(path, from)) continue;
13762
13987
  const moved2 = to + path.slice(from.length);
13763
13988
  this.opens.delete(path);
13764
13989
  this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
13765
13990
  }
13766
13991
  for (const [id2, key] of this.handles) {
13767
- 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));
13768
13993
  }
13769
13994
  this.onMoved?.(from, to);
13770
13995
  }
@@ -13772,7 +13997,7 @@ var HeldPaths = class extends Map {
13772
13997
  return this.revoked.has(path);
13773
13998
  }
13774
13999
  };
13775
- var parentOf8 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
14000
+ var parentOf9 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
13776
14001
  var readonly = (mount) => mount.caps.readonly === true || mount.options.ro !== void 0;
13777
14002
  function inodeless(mount, attr2) {
13778
14003
  if (attr2.ino !== void 0) return false;
@@ -13879,13 +14104,13 @@ var MountTable = class {
13879
14104
  });
13880
14105
  }
13881
14106
  under(path) {
13882
- 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;
13883
14108
  return false;
13884
14109
  }
13885
14110
  locate(path) {
13886
14111
  let best;
13887
14112
  for (const mount of this.mounts.values()) {
13888
- 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))
13889
14114
  best = mount;
13890
14115
  }
13891
14116
  if (!best) return void 0;
@@ -14032,7 +14257,7 @@ var MountTable = class {
14032
14257
  async commitUnder(prefix) {
14033
14258
  const at = normalizePath(prefix);
14034
14259
  await Promise.all(
14035
- [...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))
14036
14261
  );
14037
14262
  }
14038
14263
  async commitOverlapping(real, offset, length2) {
@@ -14070,11 +14295,11 @@ var MountTable = class {
14070
14295
  mount.epoch++;
14071
14296
  const linked2 = mount.caps.links ? linkedTo(mount, path) : () => false;
14072
14297
  for (const [key, cached] of [...mount.attrs])
14073
- if (within6(key, path) || linked2(cached.value)) mount.attrs.delete(key);
14298
+ if (within7(key, path) || linked2(cached.value)) mount.attrs.delete(key);
14074
14299
  mount.lists.delete(path);
14075
- mount.lists.delete(parentOf8(path));
14300
+ mount.lists.delete(parentOf9(path));
14076
14301
  for (const [key, cached] of [...mount.lists])
14077
- 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);
14078
14303
  }
14079
14304
  async getattr(mount, path, rel2, settled = false) {
14080
14305
  for (let round = 1; ; round++) {
@@ -14193,7 +14418,7 @@ var MountFs = class _MountFs {
14193
14418
  const real = normalizePath(path);
14194
14419
  if (this.at(real)) return real;
14195
14420
  try {
14196
- await this.base.stat(parentOf8(real));
14421
+ await this.base.stat(parentOf9(real));
14197
14422
  return real;
14198
14423
  } catch (err) {
14199
14424
  const moved2 = redirectOf(err);
@@ -14215,7 +14440,7 @@ var MountFs = class _MountFs {
14215
14440
  if (redirects >= MAX_LINKS7) throw errnoError("ELOOP", paths[0]);
14216
14441
  const at = paths.map((path) => {
14217
14442
  const real = normalizePath(path);
14218
- 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;
14219
14444
  });
14220
14445
  return again(at, redirects + 1);
14221
14446
  }
@@ -14235,7 +14460,7 @@ var MountFs = class _MountFs {
14235
14460
  if (this.rejectLinks) throw errnoError("ELOOP", path);
14236
14461
  if (hops >= MAX_LINKS7) throw errnoError("ELOOP", path);
14237
14462
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
14238
- 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);
14239
14464
  }
14240
14465
  async open(mount, rel2, write, owner, create = write) {
14241
14466
  return await mount.conn.call({
@@ -14251,8 +14476,8 @@ var MountFs = class _MountFs {
14251
14476
  async driverOwner(found, real, owner, directory) {
14252
14477
  const { mount, rel: rel2 } = found;
14253
14478
  if (!owner || !mount.owners || !mount.caps.owners) return void 0;
14254
- const dir = parentOf8(real);
14255
- 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);
14256
14481
  const setgid = (above.mode & S_ISGID4) !== 0;
14257
14482
  let mode = owner.mode;
14258
14483
  if (directory && setgid) mode = (mode ?? 493) | S_ISGID4;
@@ -14771,7 +14996,7 @@ var MountFs = class _MountFs {
14771
14996
  }
14772
14997
  async named(path) {
14773
14998
  const at = normalizePath(path);
14774
- 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));
14775
15000
  }
14776
15001
  async link(source, target, follow2 = false) {
14777
15002
  const from = follow2 ? await realTarget(this, normalizePath(source)) : await this.named(source);
@@ -15266,7 +15491,7 @@ async function timesOf(entry, path, req) {
15266
15491
  const st = await look.call(entry.fs, path).catch(() => void 0);
15267
15492
  if (!st || (st.uid ?? 0) === g.cred.euid) return given;
15268
15493
  const now = /* @__PURE__ */ new Date();
15269
- const kept = (ms, stored) => ms === stored.getTime() ? stored : now;
15494
+ const kept = (ms, stored2) => ms === stored2.getTime() ? stored2 : now;
15270
15495
  return [kept(req.atimeMs, st.atime), kept(req.mtimeMs, st.mtime)];
15271
15496
  }
15272
15497
  function guardOf(entry) {
@@ -16445,6 +16670,8 @@ var SYS_TREE = {
16445
16670
  slicc_opfs_calls: (listing) => `${listing.opfsCalls ?? 0}
16446
16671
  `,
16447
16672
  slicc_opfs_ops: (listing) => `${listing.opfsOps ?? 0}
16673
+ `,
16674
+ slicc_meta_transactions: ({ meta }) => `${meta?.reads ?? 0} ${meta?.writes ?? 0}
16448
16675
  `,
16449
16676
  slicc_cwd_checks: (listing) => `${listing.cwdChecks ?? 0}
16450
16677
  `,
@@ -18286,19 +18513,19 @@ function indexedDbCaStore(dbName = "slicc-realm-ca") {
18286
18513
  async swap(owner, expected, record2) {
18287
18514
  const tx = (await open2()).transaction("ca", "readwrite");
18288
18515
  const store = tx.objectStore("ca");
18289
- let stored = record2;
18516
+ let stored2 = record2;
18290
18517
  const read = store.get(owner);
18291
18518
  read.onsuccess = () => {
18292
18519
  const current = read.result;
18293
18520
  const kept = winner(current, expected, record2);
18294
18521
  if (kept === record2) store.put(record2, owner);
18295
- else stored = kept;
18522
+ else stored2 = kept;
18296
18523
  };
18297
18524
  await new Promise((resolve2, reject) => {
18298
18525
  tx.oncomplete = () => resolve2();
18299
18526
  tx.onerror = () => reject(tx.error);
18300
18527
  });
18301
- return stored;
18528
+ return stored2;
18302
18529
  }
18303
18530
  };
18304
18531
  }
@@ -18315,10 +18542,10 @@ var RealmCa = class _RealmCa {
18315
18542
  this.record = record2;
18316
18543
  }
18317
18544
  static async open(owner, store, now = Date.now()) {
18318
- const stored = await store.get(owner).catch(() => void 0);
18319
- if (stored && stored.notAfter - now > CA_RENEW_BEFORE) return new _RealmCa(stored);
18545
+ const stored2 = await store.get(owner).catch(() => void 0);
18546
+ if (stored2 && stored2.notAfter - now > CA_RENEW_BEFORE) return new _RealmCa(stored2);
18320
18547
  const record2 = await _RealmCa.create(owner, now);
18321
- return new _RealmCa(await store.swap(owner, stored, record2));
18548
+ return new _RealmCa(await store.swap(owner, stored2, record2));
18322
18549
  }
18323
18550
  static async create(owner, now) {
18324
18551
  const pair = await crypto.subtle.generateKey(ECDSA, false, ["sign", "verify"]);
@@ -22645,7 +22872,7 @@ function ranges(text) {
22645
22872
  return [Number.parseInt(lo, 16), Number.parseInt(hi, 16)];
22646
22873
  });
22647
22874
  }
22648
- function within7(table, code) {
22875
+ function within8(table, code) {
22649
22876
  let lo = 0;
22650
22877
  let hi = table.length - 1;
22651
22878
  while (lo <= hi) {
@@ -22668,10 +22895,10 @@ var LCAT = ranges(LCAT_TEXT);
22668
22895
  function prepFails(prepared) {
22669
22896
  const codes = Array.from(prepared, (ch) => ch.codePointAt(0) ?? 0);
22670
22897
  if (codes.length === 0) return true;
22671
- if (codes.some((c) => within7(PROHIBITED, c) || within7(UNASSIGNED, c))) return true;
22672
- if (!codes.some((c) => within7(RANDAL, c))) return false;
22673
- if (codes.some((c) => within7(LCAT, c))) return true;
22674
- 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);
22675
22902
  }
22676
22903
 
22677
22904
  // src/kernel/net/sealed-scram.ts
@@ -22763,15 +22990,15 @@ async function scramKeys(password, x) {
22763
22990
  )
22764
22991
  );
22765
22992
  const clientKey = await hmac(salted, encoder9.encode("Client Key"));
22766
- const stored = new Uint8Array(
22993
+ const stored2 = new Uint8Array(
22767
22994
  await crypto.subtle.digest("SHA-256", clientKey)
22768
22995
  );
22769
22996
  const auth = encoder9.encode(`${x.clientFirstBare},${x.serverFirst},${x.clientFinalBare}`);
22770
- const signature = await hmac(stored, auth);
22997
+ const signature = await hmac(stored2, auth);
22771
22998
  const proof = clientKey.map((b, i) => b ^ signature[i]);
22772
22999
  const serverKey = await hmac(salted, encoder9.encode("Server Key"));
22773
23000
  const serverSignature = await hmac(serverKey, auth);
22774
- for (const k of [salted, clientKey, stored, signature, serverKey]) k.fill(0);
23001
+ for (const k of [salted, clientKey, stored2, signature, serverKey]) k.fill(0);
22775
23002
  return { proof, serverSignature };
22776
23003
  }
22777
23004
  var SPACES = /[\u00A0\u1680\u2000-\u200B\u202F\u205F\u3000]/g;
@@ -24646,7 +24873,7 @@ function isMissing(err) {
24646
24873
  if (err?.code === "ENOENT") return true;
24647
24874
  return err instanceof Error && err.message.startsWith("ENOENT");
24648
24875
  }
24649
- function within8(path, root) {
24876
+ function within9(path, root) {
24650
24877
  return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
24651
24878
  }
24652
24879
  var VfsNode = class {
@@ -25027,7 +25254,7 @@ var VfsNodes = class {
25027
25254
  return run2;
25028
25255
  }
25029
25256
  async entryKey(path) {
25030
- const parent = parentOf4(path);
25257
+ const parent = parentOf5(path);
25031
25258
  const dir = await this.real(parent, true, () => realDir(this.fs, parent));
25032
25259
  return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
25033
25260
  }
@@ -25183,12 +25410,12 @@ var VfsNodes = class {
25183
25410
  return node;
25184
25411
  }
25185
25412
  holds(prefix) {
25186
- 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;
25187
25414
  return false;
25188
25415
  }
25189
25416
  writes(prefix) {
25190
25417
  for (const [path, node] of this.byPath)
25191
- if (node.writers > 0 && within8(path, prefix)) return true;
25418
+ if (node.writers > 0 && within9(path, prefix)) return true;
25192
25419
  return false;
25193
25420
  }
25194
25421
  closed(node, writable2 = false) {
@@ -25271,17 +25498,17 @@ var VfsNodes = class {
25271
25498
  this.byPath.get(path)?.reread();
25272
25499
  }
25273
25500
  rewrittenUnder(tree2) {
25274
- 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();
25275
25502
  }
25276
25503
  doubted(path) {
25277
- 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();
25278
25505
  }
25279
25506
  async flush(path) {
25280
- 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());
25281
25508
  }
25282
25509
  async unlinking(path) {
25283
25510
  for (const [held, node] of [...this.byPath]) {
25284
- if (!within8(held, path)) continue;
25511
+ if (!within9(held, path)) continue;
25285
25512
  await node.serial(async () => {
25286
25513
  await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
25287
25514
  });
@@ -25289,14 +25516,14 @@ var VfsNodes = class {
25289
25516
  }
25290
25517
  revoke(prefix) {
25291
25518
  for (const [path, node] of [...this.byPath]) {
25292
- if (!within8(path, prefix)) continue;
25519
+ if (!within9(path, prefix)) continue;
25293
25520
  node.revoke();
25294
25521
  this.forget(node);
25295
25522
  }
25296
25523
  }
25297
25524
  unlinked(path) {
25298
25525
  for (const [held, node] of [...this.byPath]) {
25299
- if (!within8(held, path) || this.byPath.get(held) !== node) continue;
25526
+ if (!within9(held, path) || this.byPath.get(held) !== node) continue;
25300
25527
  if (!this.dropName(held, node)) this.orphan(node);
25301
25528
  }
25302
25529
  }
@@ -25304,8 +25531,8 @@ var VfsNodes = class {
25304
25531
  if (from === to) return;
25305
25532
  const moved2 = [];
25306
25533
  for (const [p, node] of [...this.byPath]) {
25307
- if (within8(p, from)) moved2.push([p, node]);
25308
- 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)) {
25309
25536
  this.orphan(node);
25310
25537
  }
25311
25538
  }
@@ -25331,7 +25558,7 @@ function errCode(err) {
25331
25558
  return err?.code;
25332
25559
  }
25333
25560
  async function targetDir(fs, path) {
25334
- return parentOf4(await realTarget(fs, path).catch(() => path));
25561
+ return parentOf5(await realTarget(fs, path).catch(() => path));
25335
25562
  }
25336
25563
  async function refuseReadonly(fs, path, flags2, opts = {}) {
25337
25564
  const writes = (flags2 & O_ACCMODE) !== 0 || opts.truncate === true;
@@ -25594,7 +25821,7 @@ function isWatchSyscall(req) {
25594
25821
  return WATCHING.has(req.op);
25595
25822
  }
25596
25823
  var WATCHES_MAX = 8192;
25597
- var parentOf9 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
25824
+ var parentOf10 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
25598
25825
  function visibleTo2(watch, root, visible) {
25599
25826
  return {
25600
25827
  close: () => watch.close(),
@@ -25605,7 +25832,7 @@ function visibleTo2(watch, root, visible) {
25605
25832
  notes.map(async (note) => {
25606
25833
  if (!("filename" in note) || !note.filename.includes("/")) return true;
25607
25834
  const at = watch.root?.() ?? root;
25608
- const dir = parentOf9(`${at === "/" ? "" : at}/${note.filename}`);
25835
+ const dir = parentOf10(`${at === "/" ? "" : at}/${note.filename}`);
25609
25836
  if (!seen.has(dir)) seen.set(dir, visible(dir));
25610
25837
  return seen.get(dir);
25611
25838
  })
@@ -29550,7 +29777,7 @@ function sealedFetcher(hook, stop, idleMs = BODY_IDLE_MS) {
29550
29777
  var SWAP_GAP_MS = 2e3;
29551
29778
  var STAGES = ["_tmp_", "_pacquet-stage_"];
29552
29779
  function staged(source, target) {
29553
- if (parentOf4(source) !== parentOf4(target)) return false;
29780
+ if (parentOf5(source) !== parentOf5(target)) return false;
29554
29781
  const name2 = target.slice(target.lastIndexOf("/") + 1);
29555
29782
  const from = source.slice(source.lastIndexOf("/") + 1);
29556
29783
  return from.startsWith(`.${name2}-`) || STAGES.some((stage) => from.startsWith(`${name2}${stage}`));
@@ -29647,7 +29874,7 @@ var PackageSwaps = class {
29647
29874
 
29648
29875
  // src/kernel/rename-log.ts
29649
29876
  var RENAME_LOG_SIZE = 4096;
29650
- function within9(path, root) {
29877
+ function within10(path, root) {
29651
29878
  return path === root || path.startsWith(root === "/" ? "/" : `${root}/`);
29652
29879
  }
29653
29880
  var RenameLog = class {
@@ -29674,9 +29901,9 @@ var RenameLog = class {
29674
29901
  const renames = [];
29675
29902
  let paths = [...held];
29676
29903
  for (const { seq: seq2, from, to } of this.entries) {
29677
- if (seq2 <= seen || !paths.some((path) => within9(path, from))) continue;
29904
+ if (seq2 <= seen || !paths.some((path) => within10(path, from))) continue;
29678
29905
  renames.push([from, to]);
29679
- 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);
29680
29907
  }
29681
29908
  return { now, renames };
29682
29909
  }
@@ -31028,18 +31255,28 @@ function settle2(request3, value2) {
31028
31255
  request3.onerror = () => reject(request3.error);
31029
31256
  });
31030
31257
  }
31031
- function openMedia(name2, factory) {
31258
+ var privateTop2 = () => navigator.storage.getDirectory();
31259
+ async function stored(kept, top) {
31260
+ const path = kept?.path;
31261
+ if (!Array.isArray(path)) return kept;
31262
+ return directoryAt(await top(), path).catch(() => void 0);
31263
+ }
31264
+ function openMedia(name2, factory, top = privateTop2) {
31032
31265
  const request3 = factory.open(name2, 1);
31033
31266
  request3.onupgradeneeded = () => request3.result.createObjectStore(MEDIA_STORE);
31034
31267
  return settle2(request3, () => {
31035
31268
  const db = request3.result;
31036
31269
  const store = (mode) => db.transaction(MEDIA_STORE, mode).objectStore(MEDIA_STORE);
31037
31270
  return {
31038
- get: (id2) => {
31271
+ get: async (id2) => {
31039
31272
  const got = store("readonly").get(id2);
31040
- return settle2(got, () => got.result);
31273
+ return stored(await settle2(got, () => got.result), top);
31041
31274
  },
31042
- put: (id2, handle) => settle2(store("readwrite").put(handle, id2), () => void 0)
31275
+ put: async (id2, handle) => {
31276
+ const at = await portable(handle, top);
31277
+ const kept = "path" in at ? at : handle;
31278
+ await settle2(store("readwrite").put(kept, id2), () => void 0);
31279
+ }
31043
31280
  };
31044
31281
  });
31045
31282
  }
@@ -32647,6 +32884,7 @@ var Launcher = class {
32647
32884
  this.rewritten(change.paths, change.trees);
32648
32885
  this.watchers.heard(change.content ? "change" : "rename", change.paths);
32649
32886
  this.watchers.heard("rename", change.trees ?? []);
32887
+ this.watchers.peerChanged([...change.paths, ...change.trees ?? []]);
32650
32888
  if (change.outside && this.catalogChanged([...change.paths, ...change.trees ?? []])) {
32651
32889
  this.resetCatalogs();
32652
32890
  }
@@ -33400,9 +33638,9 @@ var Launcher = class {
33400
33638
  }
33401
33639
  packageRootIs(path) {
33402
33640
  const at = normalizePath(path);
33403
- const within10 = `${at}/`;
33641
+ const within11 = `${at}/`;
33404
33642
  const roots = [this.modulesDir, ...this.appended];
33405
- const root = PACKAGE_ROOT.exec(within10)?.[1] ?? packageRootOf(within10, roots);
33643
+ const root = PACKAGE_ROOT.exec(within11)?.[1] ?? packageRootOf(within11, roots);
33406
33644
  return root === at;
33407
33645
  }
33408
33646
  packageRoots(planned) {
@@ -33601,6 +33839,7 @@ var Launcher = class {
33601
33839
  fsCalls: servedFsCalls(),
33602
33840
  opfsCalls: this.rootFs.opfsCalls?.() ?? 0,
33603
33841
  opfsOps: this.rootFs.opfsOps?.() ?? 0,
33842
+ meta: this.rootFs.metaTransactions?.(),
33604
33843
  cwdChecks: this.cwds.checks,
33605
33844
  renameAcks: this.rootFs.renameWaits?.(),
33606
33845
  swaps: this.swaps.counts(),
@@ -33791,7 +34030,7 @@ var Launcher = class {
33791
34030
  }
33792
34031
  list(viewer = ROOT) {
33793
34032
  const members = new Map(this.jobs.list().map((member) => [member.pid, member]));
33794
- const rootOf = (member) => {
34033
+ const rootOf2 = (member) => {
33795
34034
  let root = member;
33796
34035
  for (let up = members.get(root.execParent ?? -1); up; up = members.get(up.execParent ?? -1)) {
33797
34036
  root = up;
@@ -33800,7 +34039,7 @@ var Launcher = class {
33800
34039
  };
33801
34040
  const shown = (pid) => {
33802
34041
  const member = pid === void 0 ? void 0 : members.get(pid);
33803
- const root = member && rootOf(member).pid;
34042
+ const root = member && rootOf2(member).pid;
33804
34043
  return root !== void 0 && this.running(root) ? root : INIT_PID;
33805
34044
  };
33806
34045
  const superseded = new Set([...members.values()].map((m) => m.execParent));
@@ -33810,7 +34049,7 @@ var Launcher = class {
33810
34049
  if (member.execed && superseded.has(member.pid) || !described2) continue;
33811
34050
  const cred = this.credOf(member.pid);
33812
34051
  if (!maySee(viewer, cred)) continue;
33813
- const root = rootOf(member);
34052
+ const root = rootOf2(member);
33814
34053
  const tags = this.tagsOf(member.pid) ?? this.tagsOf(root.pid);
33815
34054
  listed.push({
33816
34055
  pid: root.pid,
@@ -34209,9 +34448,9 @@ var Launcher = class {
34209
34448
  const served2 = serveFilesystem(port2, medium.handlers, FSA_CAPABILITIES);
34210
34449
  const id2 = source.slice("fsa:".length);
34211
34450
  this.removable.set(target, { id: id2, medium, served: served2 });
34212
- const stored = this.inserted.get(id2) ?? await this.media.get(id2);
34213
- if (stored && await granted(stored)) medium.insert(stored);
34214
- else ask(stored);
34451
+ const stored2 = this.inserted.get(id2) ?? await this.media.get(id2);
34452
+ if (stored2 && await granted(stored2)) medium.insert(stored2);
34453
+ else ask(stored2);
34215
34454
  return {
34216
34455
  port: port1,
34217
34456
  present: () => medium.present(),
@@ -34344,7 +34583,7 @@ var Launcher = class {
34344
34583
  const path = normalizePath(given);
34345
34584
  if (!await this.base.exists(path)) return void 0;
34346
34585
  if (path === "/") return path;
34347
- return joinName(await realDir(this.base, parentOf4(path)), baseName2(path));
34586
+ return joinName(await realDir(this.base, parentOf5(path)), baseName2(path));
34348
34587
  }
34349
34588
  pnpmReady() {
34350
34589
  if (this.pnpmMade !== void 0) return this.pnpmMade;
@@ -35124,11 +35363,24 @@ async function inOpfs(deps, root) {
35124
35363
  return await opfs?.resolve(root).catch(() => null) != null;
35125
35364
  }
35126
35365
  async function rootNamed(deps, req, root) {
35127
- if (req.metadata !== void 0 || !req.root || !deps.rootId) return req;
35366
+ if (req.metadata !== void 0 || !req.root && !req.rootPath || !deps.rootId) return req;
35128
35367
  const top = await deps.storage().catch(() => void 0);
35129
35368
  const name2 = await metadataName(top, root, deps.rootId);
35130
35369
  return name2 === META_DB ? req : { ...req, metadata: name2 };
35131
35370
  }
35371
+ async function rootOf(deps, req) {
35372
+ if (req.root) return req.root;
35373
+ const top = await deps.storage();
35374
+ return req.rootPath ? directoryAt(top, req.rootPath) : top;
35375
+ }
35376
+ async function insertedHandle(deps, req) {
35377
+ return "path" in req ? directoryAt(await deps.storage(), req.path) : req.handle;
35378
+ }
35379
+ function crossable(medium) {
35380
+ if (!medium.handle || clonable(medium.handle)) return medium;
35381
+ const { handle: _, ...rest2 } = medium;
35382
+ return rest2;
35383
+ }
35132
35384
  function workers(deps) {
35133
35385
  return {
35134
35386
  createWorker: deps.createWorker,
@@ -35411,7 +35663,7 @@ function serveKernel(port, deps) {
35411
35663
  }
35412
35664
  }
35413
35665
  async function init(req) {
35414
- launcher = (req.root ? Promise.resolve(req.root) : deps.storage()).then(async (root) => {
35666
+ launcher = rootOf(deps, req).then(async (root) => {
35415
35667
  req = await rootNamed(deps, req, root);
35416
35668
  const name2 = req.metadata ?? META_DB;
35417
35669
  const { meta, media } = await stores(deps, name2, req.media);
@@ -35439,7 +35691,7 @@ function serveKernel(port, deps) {
35439
35691
  ...req.approve ? { approve: approvals.ask } : {},
35440
35692
  ...net,
35441
35693
  ...media ? { media } : {},
35442
- onMountPending: (medium) => port.postMessage({ medium }),
35694
+ onMountPending: (medium) => port.postMessage({ medium: crossable(medium) }),
35443
35695
  ...req.hostfs ? { hostfs: askGrant } : {},
35444
35696
  ...pageHooks(req, port),
35445
35697
  ...mountPolicy(req.processMounts),
@@ -35455,7 +35707,8 @@ function serveKernel(port, deps) {
35455
35707
  const l = await ready2();
35456
35708
  if (req.op === "mount") return l.mount(req.spec);
35457
35709
  if (req.op === "umount") return l.umount(req.target);
35458
- if (req.op === "insert") return l.insert(req.target, req.handle, req.source);
35710
+ if (req.op === "insert")
35711
+ return l.insert(req.target, await insertedHandle(deps, req), req.source);
35459
35712
  return l.mounts.listed();
35460
35713
  }
35461
35714
  const attestOp = attestOps(port);