@ai-ecoverse/slicc-kernel 1.110.0 → 1.110.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.
package/README.md CHANGED
@@ -880,7 +880,7 @@ Not covered: hard links (OPFS cannot share contents between names; they work onl
880
880
 
881
881
  Reads retry briefly when a concurrent write has invalidated the OPFS file snapshot (`NotReadableError`).
882
882
 
883
- A kernel caches the directory handles it has walked and the sidecar entries it has read. Its own changes keep both caches current, and the other kernels on the origin hear of them through a `BroadcastChannel` named after the database (one for the sidecar, one for directories). A cached directory is checked against its path (`resolve()`) at most once a second before a lookup goes through it, so a directory moved, removed or replaced by another writer, such as a page using the OPFS API, is found at its new place or reported missing within a second.
883
+ A kernel caches the directory handles it has walked and the sidecar entries it has read. Its own changes keep both caches current, and the other kernels on the origin hear of them through a `BroadcastChannel` named after the database (one for the sidecar, one for directories). A cached directory is checked against its path (`resolve()`) at most once a second before a lookup goes through it, so a directory moved, removed or replaced by another writer, such as a page using the OPFS API, is found at its new place or reported missing within a second. It also keeps the file handles it has looked up (up to 4,096, the least recently used dropped first) for a second after each lookup. Before it uses one, it reads the file through it, which is the read a `stat` needs anyway, so a file another writer removed is missing at once and a rewritten one shows its new contents. One replaced by a directory is seen as a directory within a second. Its own changes, and the other kernels' notices, drop the handles they touch at once.
884
884
 
885
885
  The filesystem is our own rather than [ZenFS](https://github.com/zen-fs/core) (which SLICC uses), because OPFS stays the single source of truth: other writers, such as the BIOS installing packages or a page writing files, need no index to stay consistent with, and nothing is preloaded into memory at mount. POSIX metadata lives in the IndexedDB sidecar described above, which has per-entry transactions instead of one JSON file rewritten on every change.
886
886
 
@@ -984,6 +984,8 @@ var S_ISGID2 = 1024;
984
984
  var READ_ATTEMPTS = 3;
985
985
  var MAX_LINKS2 = 40;
986
986
  var CHECK_MS = 1e3;
987
+ var FILE_HANDLES = 4096;
988
+ var STALE = /* @__PURE__ */ new Set(["NotFoundError", "TypeMismatchError"]);
987
989
  var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
988
990
  var ERRNO_BY_DOM_ERROR = {
989
991
  NotFoundError: "ENOENT",
@@ -1109,6 +1111,9 @@ var OpfsFs = class {
1109
1111
  root;
1110
1112
  meta;
1111
1113
  dirs = new PathMap();
1114
+ files = new PathMap();
1115
+ snapshots = /* @__PURE__ */ new WeakMap();
1116
+ found = /* @__PURE__ */ new WeakMap();
1112
1117
  checked = /* @__PURE__ */ new WeakMap();
1113
1118
  channel;
1114
1119
  remote;
@@ -1346,6 +1351,19 @@ var OpfsFs = class {
1346
1351
  }
1347
1352
  forget(path) {
1348
1353
  this.dirs.deleteTree(path);
1354
+ this.files.deleteTree(path);
1355
+ }
1356
+ remember(path, handle, looked = false) {
1357
+ if (handle.kind !== "file") return handle;
1358
+ if (looked) {
1359
+ this.found.set(handle, performance.now());
1360
+ this.snapshots.delete(handle);
1361
+ }
1362
+ this.files.delete(path);
1363
+ if (this.files.size >= FILE_HANDLES)
1364
+ this.files.delete(this.files.keys().next().value);
1365
+ this.files.set(path, handle);
1366
+ return handle;
1349
1367
  }
1350
1368
  async inDir(path, op) {
1351
1369
  const dir = await this.dir(path);
@@ -1369,16 +1387,30 @@ var OpfsFs = class {
1369
1387
  }
1370
1388
  async handle(path) {
1371
1389
  if (split(path).length === 0) return this.root;
1390
+ const cached = this.files.get(path);
1391
+ const fresh2 = cached && performance.now() - (this.found.get(cached) ?? -CHECK_MS) < CHECK_MS;
1392
+ if (cached && !fresh2) this.files.delete(path);
1393
+ if (cached && fresh2) {
1394
+ try {
1395
+ this.snapshots.set(cached, await cached.getFile());
1396
+ return this.remember(path, cached);
1397
+ } catch (err) {
1398
+ if (!STALE.has(err?.name))
1399
+ throw translate(err, path, "read");
1400
+ this.files.delete(path);
1401
+ this.snapshots.delete(cached);
1402
+ }
1403
+ }
1372
1404
  const [dir, name2] = await this.parent(path);
1373
1405
  const known = this.dirs.has(path);
1374
1406
  return this.inDir(dir, async (parent) => {
1375
1407
  const [first, second] = known ? ["directory", "file"] : ["file", "directory"];
1376
1408
  try {
1377
- return await this.child(parent, name2, first);
1409
+ return this.remember(path, await this.child(parent, name2, first), true);
1378
1410
  } catch (err) {
1379
1411
  if (err?.name !== "TypeMismatchError") throw err;
1380
1412
  }
1381
- return this.child(parent, name2, second);
1413
+ return this.remember(path, await this.child(parent, name2, second), true);
1382
1414
  }).catch((err) => {
1383
1415
  throw translate(err, path);
1384
1416
  });
@@ -1521,9 +1553,15 @@ var OpfsFs = class {
1521
1553
  if (this.dirTimes.get(path) === ms) this.dirTimes.delete(path);
1522
1554
  }
1523
1555
  }
1556
+ async snapshot(handle) {
1557
+ const taken = this.snapshots.get(handle);
1558
+ if (!taken) return handle.getFile();
1559
+ this.snapshots.delete(handle);
1560
+ return taken;
1561
+ }
1524
1562
  async statOf(path, handle, meta, touched) {
1525
1563
  const directory = handle.kind === "directory";
1526
- const file = directory ? void 0 : await handle.getFile();
1564
+ const file = directory ? void 0 : await this.snapshot(handle);
1527
1565
  const modified = file?.lastModified ?? 0;
1528
1566
  const kept = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
1529
1567
  const mtime = touched ?? kept;
@@ -6108,7 +6146,7 @@ function satisfies(version, range2) {
6108
6146
  }
6109
6147
 
6110
6148
  // src/version.ts
6111
- var KERNEL_VERSION = true ? "1.110.0" : "0.0.0-dev";
6149
+ var KERNEL_VERSION = true ? "1.110.1" : "0.0.0-dev";
6112
6150
  var OS_RELEASE = "6.0.0-slicc";
6113
6151
  function procVersion() {
6114
6152
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC