@ai-ecoverse/slicc-kernel 1.75.0 → 1.75.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
@@ -664,7 +664,7 @@ The kernel writes `/etc/passwd` and `/etc/group` when it starts (see [Users](#us
664
664
  OPFS stores names, bytes, sizes and modification times, nothing else. Everything POSIX needs on top of that lives in an IndexedDB sidecar, so it survives reloads and new kernels:
665
665
 
666
666
  - mode bits (`chmod`);
667
- - access and change times, and a modification time set explicitly with `utime` (it holds until the file is written again); a directory's times move whenever an entry is created, removed or renamed in it, as POSIX asks, and a time set explicitly after such a change keeps winning, also when the change was made in another kernel and its write lands later; an Emscripten program reads a directory's times again after any call that can wait on another process (a pipe or socket read, `wait`, `poll`, a sleep), so a program that waits for a child or a peer to fill a directory sees the new times (rsync's final directory times rely on it), while a walk without waits reads each directory once; `chown`/`fchown` leave the times alone (Emscripten stamps the setattr of a chown with the current time, which isn't taken as an mtime);
667
+ - access and change times, and a modification time set explicitly with `utime` (it holds until the file is written again: any write or truncate moves it to the time of the write, also in Chrome, whose OPFS doesn't move a file's `lastModified` for writes through a sync access handle, so the kernel records the write's time itself, and it reaches the metadata store up to 100 ms after the write, so a tab closed inside that window keeps the old mtime on disk; the time follows a rename and is dropped with the file); a directory's times move whenever an entry is created, removed or renamed in it, as POSIX asks, and a time set explicitly after such a change keeps winning, also when the change was made in another kernel and its write lands later; an Emscripten program reads a directory's times again after any call that can wait on another process (a pipe or socket read, `wait`, `poll`, a sleep), so a program that waits for a child or a peer to fill a directory sees the new times (rsync's final directory times rely on it), while a walk without waits reads each directory once; `chown`/`fchown` leave the times alone (Emscripten stamps the setattr of a chown with the current time, which isn't taken as an mtime);
668
668
  - inode numbers that stay stable across renames;
669
669
  - symbolic links (`symlink`, `readlink`, `lstat`, and following them in paths), which exist only in the sidecar, with their own times: `touch -h`, `lutimes` and `utimensat` with `AT_SYMLINK_NOFOLLOW` set the link's, never the target's. A link's own mode can't be changed, as on Linux: `chmod -h`, `lchmod` and `fchmodat` with `AT_SYMLINK_NOFOLLOW` fail with `EOPNOTSUPP` (which `chmod -h` and `tar` ignore for links) and never follow it, and `open` with `O_PATH | O_NOFOLLOW` gives a descriptor for the link itself.
670
670
 
@@ -783,6 +783,7 @@ function syncAccessHandles(scope = globalThis) {
783
783
  function isHandle(child) {
784
784
  return "kind" in child;
785
785
  }
786
+ var FILE_TIMES_MS = 100;
786
787
  var OpfsFs = class {
787
788
  root;
788
789
  meta;
@@ -1053,8 +1054,50 @@ var OpfsFs = class {
1053
1054
  const fallback = directory || EXECUTABLE.test(path) ? 493 : 420;
1054
1055
  return (directory ? S_IFDIR : S_IFREG) | (meta?.mode ?? fallback);
1055
1056
  }
1057
+ id = crypto.randomUUID();
1056
1058
  dirTimes = /* @__PURE__ */ new Map();
1057
1059
  dirDirty = /* @__PURE__ */ new Set();
1060
+ fileTimes = /* @__PURE__ */ new Map();
1061
+ written(target) {
1062
+ this.committed(target);
1063
+ if (!this.ranged) return;
1064
+ this.fileTimes.set(target, Date.now());
1065
+ this.scheduleFileTimes();
1066
+ }
1067
+ scheduleFileTimes() {
1068
+ if (this.flushing.file || this.fileTimes.size === 0) return;
1069
+ this.flushing.file = setTimeout(() => {
1070
+ void this.flushFileTimes().catch(() => void 0).finally(() => {
1071
+ this.flushing.file = void 0;
1072
+ this.scheduleFileTimes();
1073
+ });
1074
+ }, FILE_TIMES_MS);
1075
+ this.flushing.file.unref?.();
1076
+ }
1077
+ async flushFileTimes() {
1078
+ for (const [path, ms] of [...this.fileTimes]) {
1079
+ const mtimeFor = await this.modified(path).catch(() => 0);
1080
+ await this.meta.update(
1081
+ path,
1082
+ (entry) => this.fileTimes.get(path) !== ms || this.explicitWins(entry, ms) ? entry : { ...entry, path, mtimeMs: ms, mtimeFor, ctimeMs: ms }
1083
+ );
1084
+ if (this.fileTimes.get(path) === ms) this.fileTimes.delete(path);
1085
+ }
1086
+ }
1087
+ timesOf(path) {
1088
+ return this.dirTimes.get(path) ?? this.fileTimes.get(path);
1089
+ }
1090
+ explicitWins(entry, ms) {
1091
+ if (entry?.timesAt === void 0 || entry.timesBy === this.id) return false;
1092
+ return entry.timesAt >= ms;
1093
+ }
1094
+ movePending(from, to) {
1095
+ for (const [path, ms] of [...this.fileTimes]) {
1096
+ if (!within2(path, from)) continue;
1097
+ this.fileTimes.delete(path);
1098
+ if (to !== void 0) this.fileTimes.set(to + path.slice(from.length), ms);
1099
+ }
1100
+ }
1058
1101
  flushing = {};
1059
1102
  touch(...dirs) {
1060
1103
  const now = Date.now();
@@ -1078,11 +1121,10 @@ var OpfsFs = class {
1078
1121
  if (this.dirTimes.get(path) === ms) this.dirTimes.delete(path);
1079
1122
  }
1080
1123
  }
1081
- async statOf(path, handle, meta) {
1124
+ async statOf(path, handle, meta, touched) {
1082
1125
  const directory = handle.kind === "directory";
1083
1126
  const file = directory ? void 0 : await handle.getFile();
1084
1127
  const modified = file?.lastModified ?? 0;
1085
- const touched = directory ? this.dirTimes.get(path) : void 0;
1086
1128
  const kept = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
1087
1129
  const mtime = touched ?? kept;
1088
1130
  return {
@@ -1114,13 +1156,14 @@ var OpfsFs = class {
1114
1156
  }
1115
1157
  async describe(found) {
1116
1158
  if (found.link) return this.owned(this.linkStat(found.link), found.path, found.link);
1159
+ const touched = this.timesOf(found.path);
1117
1160
  if (!this.owners) {
1118
1161
  const [meta2] = await this.meta.get([found.path]);
1119
- return this.statOf(found.path, found.handle, meta2);
1162
+ return this.statOf(found.path, found.handle, meta2, touched);
1120
1163
  }
1121
1164
  const entries = await this.meta.get(chain(found.path));
1122
1165
  const meta = entries.pop();
1123
- const stat = await this.statOf(found.path, found.handle, meta);
1166
+ const stat = await this.statOf(found.path, found.handle, meta, touched);
1124
1167
  return { ...stat, ...ownOf(meta) ?? inherit(entries) };
1125
1168
  }
1126
1169
  async stat(path) {
@@ -1177,13 +1220,14 @@ var OpfsFs = class {
1177
1220
  async pwrite(path, offset, bytes) {
1178
1221
  const [handle, , target] = await this.fileFor(path);
1179
1222
  await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
1180
- this.committed(target);
1223
+ this.written(target);
1181
1224
  }
1182
1225
  async truncate(path, size) {
1183
1226
  const handle = await this.existing(path, true);
1184
- const target = this.remote?.watched() ? (await this.locate(path, true)).path : path;
1227
+ const resolve = this.ranged || this.remote?.watched();
1228
+ const target = resolve ? (await this.locate(path, true)).path : path;
1185
1229
  await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
1186
- this.committed(target);
1230
+ this.written(target);
1187
1231
  }
1188
1232
  async writeSession(path, truncate) {
1189
1233
  const [handle, , target] = await this.fileFor(path);
@@ -1273,13 +1317,16 @@ var OpfsFs = class {
1273
1317
  async readdirStat(path) {
1274
1318
  const [dir, children2] = await this.listing(path);
1275
1319
  const base = dir === "/" ? "" : dir;
1320
+ const touched = children2.map(([name2]) => this.timesOf(`${base}/${name2}`));
1276
1321
  const metas = await this.meta.get(children2.map(([name2]) => `${base}/${name2}`));
1277
1322
  const handed2 = this.owners ? await this.handedTo(dir) : void 0;
1278
1323
  const owned4 = (stat, entry) => handed2 ? { ...stat, ...ownOf(entry) ?? handed2 } : stat;
1279
1324
  return Promise.all(
1280
1325
  children2.map(async ([name2, child], i) => {
1281
1326
  if (!isHandle(child)) return [name2, owned4(this.linkStat(child), child)];
1282
- const stat = await this.statOf(`${base}/${name2}`, child, metas[i]).catch(() => null);
1327
+ const stat = await this.statOf(`${base}/${name2}`, child, metas[i], touched[i]).catch(
1328
+ () => null
1329
+ );
1283
1330
  return [name2, stat && owned4(stat, metas[i])];
1284
1331
  })
1285
1332
  );
@@ -1337,6 +1384,7 @@ var OpfsFs = class {
1337
1384
  throw translate(err, path);
1338
1385
  }
1339
1386
  this.changed(found.path);
1387
+ this.movePending(found.path);
1340
1388
  }
1341
1389
  await this.meta.remove([found.path]);
1342
1390
  this.touch(parentOf3(found.path));
@@ -1400,6 +1448,7 @@ var OpfsFs = class {
1400
1448
  const moved2 = await this.place(source.handle, dir, staging);
1401
1449
  if (swap) await this.rm(destination, { recursive: true });
1402
1450
  await this.meta.move(source.path, destination, carry);
1451
+ this.movePending(source.path, destination);
1403
1452
  if (!moved2) await this.rm(source.path, { recursive: true });
1404
1453
  let copied = !moved2;
1405
1454
  if (swap) {
@@ -1521,6 +1570,7 @@ var OpfsFs = class {
1521
1570
  const handle = found.handle;
1522
1571
  const file = handle.kind === "file" ? await handle.getFile() : void 0;
1523
1572
  this.dirTimes.delete(found.path);
1573
+ this.fileTimes.delete(found.path);
1524
1574
  const ctimeMs = Date.now();
1525
1575
  await this.meta.update(found.path, (entry) => ({
1526
1576
  ...entry,
@@ -1529,7 +1579,8 @@ var OpfsFs = class {
1529
1579
  mtimeMs: mtime.getTime(),
1530
1580
  mtimeFor: file?.lastModified ?? 0,
1531
1581
  ctimeMs,
1532
- timesAt: ctimeMs
1582
+ timesAt: ctimeMs,
1583
+ timesBy: this.id
1533
1584
  }));
1534
1585
  }
1535
1586
  async reconcile() {