@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 +1 -1
- package/dist/kernel-worker.js +60 -9
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node.js +60 -9
- package/dist/node.js.map +3 -3
- package/package.json +1 -1
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
|
|
package/dist/kernel-worker.js
CHANGED
|
@@ -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.
|
|
1223
|
+
this.written(target);
|
|
1181
1224
|
}
|
|
1182
1225
|
async truncate(path, size) {
|
|
1183
1226
|
const handle = await this.existing(path, true);
|
|
1184
|
-
const
|
|
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.
|
|
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(
|
|
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() {
|