@ai-ecoverse/slicc-kernel 1.85.1 → 1.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.js CHANGED
@@ -107,6 +107,12 @@ async function lutimesOf(fs, path, atime, mtime) {
107
107
  function mayLink(fs, path) {
108
108
  return fs.links?.(path) ?? true;
109
109
  }
110
+ function modeOption(mode) {
111
+ if (!Number.isInteger(mode) || mode < 0 || mode > 4095) {
112
+ throw fsError("EINVAL", `not a mode: ${String(mode)}`);
113
+ }
114
+ return mode;
115
+ }
110
116
  function fsError(code, path) {
111
117
  return Object.assign(new Error(`${code}: ${path}`), { code });
112
118
  }
@@ -570,7 +576,9 @@ function id(value) {
570
576
  return value;
571
577
  }
572
578
  function changeCred(cur, change) {
573
- if (typeof change !== "object" || change === null) throw fsError("EINVAL", "not a change");
579
+ if (typeof change !== "object" || change === null || Array.isArray(change)) {
580
+ throw fsError("EINVAL", "not a change");
581
+ }
574
582
  const privileged = cur.euid === 0;
575
583
  const next2 = copyCred(cur);
576
584
  for (const keys of [UIDS, GIDS]) {
@@ -3002,6 +3010,9 @@ var MemoryMeta = class {
3002
3010
  async get(paths) {
3003
3011
  return paths.map((path) => this.entries.get(path));
3004
3012
  }
3013
+ async fresh(path) {
3014
+ return this.entries.get(path);
3015
+ }
3005
3016
  async update(path, change) {
3006
3017
  this.set(path, change(this.entries.get(path)));
3007
3018
  }
@@ -3473,7 +3484,8 @@ function sidecar(meta) {
3473
3484
  const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
3474
3485
  throw translate(err, pathOf(args), "sidecar", false);
3475
3486
  });
3476
- const ops = Object.fromEntries(META_OPS.map((op) => [op, guarded3(op)]));
3487
+ const names = meta.fresh ? [...META_OPS, "fresh"] : META_OPS;
3488
+ const ops = Object.fromEntries(names.map((op) => [op, guarded3(op)]));
3477
3489
  if (meta.absent) ops.absent = meta.absent.bind(meta);
3478
3490
  const { removeFile } = meta;
3479
3491
  if (removeFile)
@@ -3597,6 +3609,24 @@ var OpfsFs = class {
3597
3609
  committed(target) {
3598
3610
  if (this.remote?.watched()) this.remote.wrote(target);
3599
3611
  }
3612
+ counted = [];
3613
+ async stamp(path) {
3614
+ const entry = this.meta.fresh ? await this.meta.fresh(path) : (await this.meta.get([path]))[0];
3615
+ return String(entry?.writes ?? 0);
3616
+ }
3617
+ async counting(target) {
3618
+ if (!isProtected(this.counted, target)) return;
3619
+ await this.meta.update(target, (entry) => ({
3620
+ ...entry,
3621
+ path: target,
3622
+ writes: (entry?.writes ?? 0) + 1
3623
+ }));
3624
+ }
3625
+ async recounting(handle, target) {
3626
+ if (this.counted.length === 0) return;
3627
+ const where = await this.root.resolve(handle).catch(() => null);
3628
+ await this.counting(where ? `/${where.join("/")}` : target);
3629
+ }
3600
3630
  resolvePath(base, path) {
3601
3631
  return resolvePath(base, path);
3602
3632
  }
@@ -3655,6 +3685,7 @@ var OpfsFs = class {
3655
3685
  guarded = [];
3656
3686
  protect(prefixes2) {
3657
3687
  this.guarded = prefixes2;
3688
+ this.counted = prefixes2;
3658
3689
  }
3659
3690
  unguarded() {
3660
3691
  return Object.create(this, { guarded: { value: [] } });
@@ -3991,9 +4022,10 @@ var OpfsFs = class {
3991
4022
  return this.retrying(
3992
4023
  path,
3993
4024
  async () => {
4025
+ const stamp = await this.stamp(path);
3994
4026
  const file = await handle.getFile();
3995
4027
  const bytes = new Uint8Array(await file.arrayBuffer());
3996
- return { bytes, version: `${file.lastModified}:${file.size}` };
4028
+ return { bytes, version: `${file.lastModified}:${file.size}:${stamp}` };
3997
4029
  },
3998
4030
  false
3999
4031
  );
@@ -4003,8 +4035,9 @@ var OpfsFs = class {
4003
4035
  return this.retrying(
4004
4036
  path,
4005
4037
  async () => {
4038
+ const stamp = await this.stamp(path);
4006
4039
  const file = await handle.getFile();
4007
- return `${file.lastModified}:${file.size}`;
4040
+ return `${file.lastModified}:${file.size}:${stamp}`;
4008
4041
  },
4009
4042
  false
4010
4043
  );
@@ -4019,14 +4052,24 @@ var OpfsFs = class {
4019
4052
  }
4020
4053
  async pwrite(path, offset, bytes) {
4021
4054
  const [handle, , target] = await this.fileFor(path);
4022
- await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
4055
+ await this.counting(target);
4056
+ try {
4057
+ await this.retrying(path, () => positioned(handle, (io) => io.write(offset, bytes)), true);
4058
+ } finally {
4059
+ await this.recounting(handle, target);
4060
+ }
4023
4061
  this.written(target);
4024
4062
  }
4025
4063
  async truncate(path, size) {
4026
4064
  const handle = await this.existing(path, true);
4027
4065
  const resolve2 = this.ranged || this.remote?.watched();
4028
- const target = resolve2 ? (await this.locate(path, true)).path : path;
4029
- await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
4066
+ const target = resolve2 || this.counted.length > 0 ? (await this.locate(path, true)).path : path;
4067
+ await this.counting(target);
4068
+ try {
4069
+ await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
4070
+ } finally {
4071
+ await this.recounting(handle, target);
4072
+ }
4030
4073
  this.written(target);
4031
4074
  }
4032
4075
  async writeSession(path, truncate) {
@@ -18381,17 +18424,21 @@ var Launcher = class {
18381
18424
  const real = await this.realLayoutPath("/tmp");
18382
18425
  const st = real === void 0 ? void 0 : await this.base.lstat(real).catch(() => void 0);
18383
18426
  if (real !== void 0 && st?.isDirectory && this.base.hasOwner && !await this.base.hasOwner(real)) {
18427
+ const names = await this.base.readdir(real).catch(() => void 0);
18384
18428
  let removed = 0;
18385
- for (const name2 of await this.base.readdir(real).catch(() => [])) {
18429
+ let left = names === void 0 ? 1 : 0;
18430
+ for (const name2 of names ?? []) {
18386
18431
  const path = joinName(real, name2);
18387
- if (await this.base.hasOwner(path) || this.inUse(path)) continue;
18388
- const failed3 = await this.base.rm(path, { recursive: true, force: true }).catch(String);
18432
+ if (await this.base.hasOwner(path)) continue;
18433
+ const failed3 = this.inUse(path) ? "busy" : await this.base.rm(path, { recursive: true, force: true }).catch(String);
18389
18434
  if (failed3 === void 0) removed++;
18435
+ else left++;
18390
18436
  }
18391
18437
  if (removed > 0) {
18392
18438
  console.warn(`slicc-kernel users: removed ${removed} entries from /tmp that had no owner`);
18393
18439
  }
18394
- await this.base.chown?.(real, { uid: 0, gid: 0, mode: 1023, follow: false });
18440
+ if (left === 0)
18441
+ await this.base.chown?.(real, { uid: 0, gid: 0, mode: 1023, follow: false });
18395
18442
  }
18396
18443
  if (st?.isDirectory) await this.base.chmod(real, 1023);
18397
18444
  }
@@ -20060,11 +20107,17 @@ async function createNodeKernel(options = {}) {
20060
20107
  close: () => session.close()
20061
20108
  };
20062
20109
  },
20063
- async writeFile(path, data) {
20110
+ async writeFile(path, data, options2 = {}) {
20111
+ const mode = options2.mode === void 0 ? void 0 : modeOption(options2.mode);
20064
20112
  const parent = path.slice(0, path.lastIndexOf("/")) || "/";
20065
20113
  await launcher.fs.mkdir(parent, { recursive: true });
20114
+ if (mode !== void 0) {
20115
+ await launcher.fs.writeFile(path, "");
20116
+ await launcher.fs.chmod(path, mode);
20117
+ }
20066
20118
  await launcher.fs.writeFile(path, data);
20067
20119
  },
20120
+ chmod: async (path, mode) => launcher.fs.chmod(path, modeOption(mode)),
20068
20121
  readFile: async (path) => launcher.fs.readFileBuffer(path),
20069
20122
  mount: (spec) => guard2(() => launcher.mount(spec)),
20070
20123
  umount: (target) => guard2(async () => launcher.umount(target)),