@ai-ecoverse/slicc-kernel 1.110.1 → 1.110.3
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 +29 -15
- package/dist/kernel-worker.js +46 -16
- package/dist/kernel-worker.js.map +2 -2
- package/dist/meta.js +10 -1
- package/dist/meta.js.map +2 -2
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +36 -15
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +1 -1
- package/dist/types/fs/meta.d.ts +6 -4
- package/dist/types/fs/opfs.d.ts +1 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -865,7 +865,7 @@ The kernel writes `/etc/passwd` and `/etc/group` when it starts (see [Users](#us
|
|
|
865
865
|
|
|
866
866
|
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:
|
|
867
867
|
|
|
868
|
-
- mode bits (`chmod`);
|
|
868
|
+
- mode bits (`chmod`; a `chmod` to the mode a file or directory already has writes nothing and leaves its ctime as it was, which saves a sidecar write for every installer that sets the mode a file was made with);
|
|
869
869
|
- 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);
|
|
870
870
|
- inode numbers that stay stable across renames;
|
|
871
871
|
- 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.
|
|
@@ -927,17 +927,22 @@ The Biome, TypeScript, lefthook, Renovate and CI configuration comes from [slicc
|
|
|
927
927
|
|
|
928
928
|
`tools/big-file.mjs [bytes]` is a manual check, not part of any suite. It writes a file of that size (64 MiB by default) through a tmpfs mount with the unit-test launcher, reads it back, and prints the peak RSS and its ratio to the file size. A 256 MiB file peaks at about 1.8 times its size. Before ranged I/O (#127), a 1.1 GiB file peaked at 18 GB of RSS. **Do not run it past 1 GiB on a workstation.** It refuses anything over 256 MiB unless `SLICC_BIG_FILE_I_HAVE_THE_RAM=1` is set. Measure a small file and extrapolate instead. The unit suite checks a 32 MiB file the same way, and allows a fixed 192 MiB for garbage V8 has yet to collect, which dominates at that size.
|
|
929
929
|
|
|
930
|
-
Releases are cut by semantic-release on every push to `main`, in two jobs, and only on `main`. The first job runs on the
|
|
930
|
+
Releases are cut by semantic-release on every push to `main`, in two jobs, and only on `main`. The first job is light work and runs on the light runner (`CERT_LIGHT_RUNNER`), so it starts within seconds of the push, before `main` has moved on. It computes the version, tags the commit and creates the GitHub release, with `npmPublish: false`. A run that starts after `main` has moved skips; the run for the newer commit releases it. The second job runs on a GitHub-hosted runner, because npm's trusted publishing and provenance accept no self-hosted runner. It publishes the tagged commit with `tools/release-recover.mjs`. However long it waits for a runner, it publishes exactly the commit the run started on, so the provenance, the tag and the package's `gitHead` agree. For any version tagged at HEAD, `tools/release-recover.mjs` pushes the tag if origin lacks it, publishes the version if npm lacks it (with retries) and it is newer than npm's `latest` (an older one stays unpublished, so `latest` never moves back to it, and an `npm view` error fails the job instead of guessing; #837), and creates its GitHub release if that is missing too. So a failure after the version is tagged (a rejected push, a failed `npm publish`) doesn't strand it, and re-running the Release workflow completes it the same way.
|
|
931
931
|
|
|
932
932
|
### Certification (`cert-review`)
|
|
933
933
|
|
|
934
|
-
|
|
934
|
+
Every change is certified in its pull request, before it merges (`cert-review-pr.yml`, next section), and `cert-review` is a required check of the merge queue, so every commit on `main`, and every release built from it, was certified first. A release is then **verified on npm** (`release / verify on npm` in `release.yml`, `tools/cert-review/verify.mjs`), in code, for every tag the run made:
|
|
935
|
+
|
|
936
|
+
- It waits up to an hour for npm to serve the version (an accepted publish can take a while to appear), then downloads the tarball and checks `dist.integrity` (sha512), the SLSA v1 provenance (the tarball's sha512 is its subject; `release.yml` on `refs/heads/main`, a GitHub-hosted builder; `gitCommit` = `gitHead` = the tag, on `main`), and `npm audit signatures`. A version that `release-recover.mjs` skipped because npm's `latest` is newer (#837) has nothing to verify.
|
|
937
|
+
- It then writes `certified @ai-ecoverse/slicc-kernel@<v> sha256=<64 hex> (covers <first>–<v>; ci: <run>; pr-certified: <each merged pull request's cert-review>; known: <open known-issue issues>)` into the GitHub release's notes, between `<!-- cert-verify -->` markers. homescoop's ci reads exactly that prefix and re-hashes the npm tarball against it. `covers` starts after the last version npm serves, so a skipped version is covered by the next. Notes that already carry the identical line stay unchanged.
|
|
938
|
+
- A mismatch writes no line. The job is red with a `supply-chain alert`, and so is a line already in the notes with another sha256.
|
|
939
|
+
|
|
940
|
+
The tools in `tools/cert-review/`:
|
|
935
941
|
|
|
936
|
-
- **plan:** works out the version, the previous tag, the commit and the base (`base.mjs`). The base is the newest earlier release whose `cert-review` commit status is `success`, or, until there is one, the release `base.json` names (the cert lane's last line). CI never reads the npm `certified` dist-tag.
|
|
937
942
|
- **delta** (`delta.mjs`): lists the pull requests merged between the two tags (their merge commits, plus `pr` for a planted branch). Each gets its conventional-commit kind (`fix`, `feat`, `perf` and `revert` are behaviour changes) and the integration test files and titles it adds or changes. The titles are read from the pull request's patch: the added `test(…)` lines and the hunk headers git puts above changed lines. Escapes are decoded as JavaScript does. A title git cut short matches as a prefix, and each `${…}` in a template-literal title matches anything there, so `` `a forked child of a ${kind} WASIX program…` `` matches the rows it ran as. A title made only of placeholders is skipped.
|
|
938
943
|
- **suite** (`suite.mjs`, `reporter.mjs`) runs on the bench pool, without secrets. It takes the tag's tree with its own dependencies and Chromium, and replaces `dist/` with the **published npm tarball** (for a planted branch, its own `npm pack`). The test command runs in a session of its own (`detached`), and `suite.mjs` notes a `SIGINT` (`ignored` in its summary) instead of dying of it. That way a stray signal to the step's process group can't cut the run short, while a `SIGTERM` (a real cancel) still ends it. The log is written as the run goes, and a `SIGTERM` still writes the summary with the results so far (`exit: 'signal SIGTERM'`). A run that left only its `.jsonl` (the runner was lost) is read from that, as `exit: 'no summary'`. The NEGATIVE step runs even when the release suite's step failed. Before it reports, `suite.mjs` re-runs once just the test files that left no result (no rows, a cancelled row, or a file-level failure, such as a lost process), with a second `node --test` over those files, and merges their results (`retried`, `firstExit`, `unfinished` in the summary). That holds for the release suite and for each NEGATIVE run. A fix whose test passes on its baseline stays `no-negative`.
|
|
939
|
-
- The
|
|
940
|
-
- The base job runs the
|
|
944
|
+
- The candidate job runs the pull request's whole `test/integration/` suite on its packed build.
|
|
945
|
+
- The base job runs `main`'s own suite on `main`'s packed build, then the NEGATIVE (#861): `main`'s tree, with all of the pull request's `test/**` changes applied (test files, helpers and fixtures), on `main`'s build. So a fix in `tools/` fails there just as a fix in `src/` does. A test file that fails to load on `main` (a module or export that only the pull request adds) leaves no result for its titles, which is `prev-not-run`, never a NEGATIVE.
|
|
941
946
|
- Each run uses the tree's own `npm test` command, with a reporter that records every test and subtest with its status and error.
|
|
942
947
|
- **kernel-compat** (`compat.mjs`) supplies the base-set and catalog evidence: homescoop's kernel-compat run for the release.
|
|
943
948
|
- Its reference kernel is the certified base, not slicc-bios's pin. cert-review passes `--reference <base>`, and `kernel-compat.yml` takes a `reference` input that it hands to homescoop's workflow (homescoop#504), recorded in compat.json. So it uses a dispatched run named `Kernel compat v<version> ref <base>` (kernel-compat has no run of its own after Release). Its `candidate` job checks that npm serves the version, or takes a packed tarball from another run (`run`, `artifact`, `sha256`, for PR builds `x.y.z-pr.N.M`), and prints `@ai-ecoverse/slicc-kernel@<version>`, which compat.mjs matches. It dispatches one when none has finished and waits up to 150 minutes; package reruns carry the same reference.
|
|
@@ -948,24 +953,23 @@ Releases are cut by semantic-release on every push to `main`, in two jobs, and o
|
|
|
948
953
|
- `negative` (a fix's test fails on the previous dist and passes on the release) or `passes` (a feature's test passes);
|
|
949
954
|
- otherwise a finding: `no-test`, `no-negative`, `fails-on-release`, `not-run`, `prev-not-run` or `no-titles`.
|
|
950
955
|
|
|
951
|
-
It also diffs the two suites test by test (`regressed`, `newFailing`, `fixed`, `stillFailing`, `removed`), flags a suite run that didn't finish (`suite-incomplete`), and adds the kernel-compat findings. A release suite that didn't finish is a `missing-evidence` finding that always blocks `certified`, with the remedy `re-run the failed jobs`. That covers a signal, a timeout, a cut-off run, no results, or any test file without a result. Its base tests that didn't run are `unrun`, never `removed`. INCOMPLETE is not FAIL: record writes `cert-ci: INCOMPLETE <name>@<version> (<n> files without results: …; re-run the failed jobs)`,
|
|
956
|
+
It also diffs the two suites test by test (`regressed`, `newFailing`, `fixed`, `stillFailing`, `removed`), flags a suite run that didn't finish (`suite-incomplete`), and adds the kernel-compat findings. A release suite that didn't finish is a `missing-evidence` finding that always blocks `certified`, with the remedy `re-run the failed jobs`. That covers a signal, a timeout, a cut-off run, no results, or any test file without a result. Its base tests that didn't run are `unrun`, never `removed`. INCOMPLETE is not FAIL: record writes `cert-ci: INCOMPLETE <name>@<version> (<n> files without results: …; re-run the failed jobs) (ci: …; known: …)`, with the known issues as on a certified line, and the `cert-review` status is `error`, which means re-run, where `failure` means a real defect. A defect found in code (a compat crash) still FAILs. A base suite that is missing or didn't finish is a `missing-evidence` finding. It blocks `certified` in code (record FAILs it) unless the release suite is complete and all-pass and the delta removes or renames no test file and drops no test title (`dropped` per row). Either way the line ends with `base suite incomplete (<n> results)`. Claude, on Bedrock with the key only in that step, then reads those artifacts and `instructions.txt` (which has the roll-forward FAIL list verbatim) and writes `verdict.json` and `comment.md`. Its tools are reading files, `gh pr view`, and `gh issue list`/`view`. `gh issue create` is allowed only for a release on `main`; otherwise, and for an issue in another repository, it lists the issue under `elsewhere` for the cert lane to file.
|
|
952
957
|
- **record** (`record.mjs`), in code, with read-only permissions:
|
|
953
958
|
- It re-downloads the tarball and checks `dist.integrity` (sha512) and the sha256, the SLSA v1 provenance (the tarball is its subject; `release.yml`, `refs/heads/main`, a GitHub-hosted builder; commit = `gitHead` = the tag), `npm audit signatures`, and that the commit is on `main`. A problem there, a `compat-crash`, or a planted build is a FAIL whatever the review says.
|
|
954
|
-
- It adds the **known issues as of the release**: every issue labelled `known-issue` (open or closed) and every one in `known.json`, unless it was fixed before the release. That holds when `known.json`'s `fixedIn` names an earlier or equal version, or the merged pull request that closed it is an ancestor of the tag, or it was closed (with no closing pull request) before the release commit. An issue closed as not planned or as a duplicate is dropped.
|
|
959
|
+
- It adds the **known issues as of the release**: every issue labelled `known-issue` (open or closed) and every one in `known.json`, unless it was fixed before the release. An issue in another repository, whether listed or named by the reviewer, counts only while it is open. That holds when `known.json`'s `fixedIn` names an earlier or equal version, or the merged pull request that closed it is an ancestor of the tag, or it was closed (with no closing pull request) before the release commit. An issue closed as not planned or as a duplicate is dropped.
|
|
955
960
|
- The line is `certified @ai-ecoverse/slicc-kernel@<v> sha256=<…> (ci: <run>; known: …)`. Any test gaps from the findings (`no-test`, `no-negative`, `prev-not-run`, `no-titles`, `not-run`) are appended in code as `test gaps: #<pr> <kind>`, so a fix without a NEGATIVE is never certified silently.
|
|
956
|
-
- **
|
|
957
|
-
|
|
958
|
-
Moving the `certified` dist-tag stays manual. Automating it needs an npm token that may change dist-tags, in a step that re-hashes the tarball and tags only on a `certified` line.
|
|
961
|
+
- **verify** (`verify.mjs`) is the `release / verify on npm` job above.
|
|
959
962
|
|
|
960
|
-
### Certification in the pull request (`cert-review
|
|
963
|
+
### Certification in the pull request (`cert-review`)
|
|
961
964
|
|
|
962
|
-
`.github/workflows/cert-review-pr.yml` certifies a pull request before it merges, so `main`, and with it npm's `latest`, only gets certified changes.
|
|
965
|
+
`.github/workflows/cert-review-pr.yml` certifies a pull request before it merges, so `main`, and with it npm's `latest`, only gets certified changes. It reports as the `cert-review` commit status, a required check of `main`'s merge queue. Every same-repo pull request gets one (`not behaviour`, `waits for merge-ok`, the review's verdict, or the self-guard's hold). **A pull request from a fork gets none**, because fork code never reaches the pools or the key, so it can't enter the queue: only Lars merges one, after reading it.
|
|
963
966
|
|
|
964
|
-
- **Which pull requests:** those that change shipped behaviour, by a conservative path filter (`behaviourFiles` in `lib.mjs`): `src/`, `build.mjs`, `package.json` and its lock, `tsconfig*.json`, `.releaserc*`, `tools/release-recover.mjs`, the workflows that build or publish (`release.yml`, `ci.yml`), and the reviewer's own code (`tools/cert-review/`, `cert-review*.yml`, `.github/runner.mjs`). **Main's code decides:** every job checks out the pull request's base and fetches only the trees under test, so the path filter, pack, suite, findings, the instructions and record all run from `main`. A pull request that changes the reviewer is reviewed by `main`'s reviewer. The workflow file of a `pull_request` run still comes from the pull request, so **pull
|
|
967
|
+
- **Which pull requests:** those that change shipped behaviour, by a conservative path filter (`behaviourFiles` in `lib.mjs`): `src/`, `build.mjs`, `package.json` and its lock, `tsconfig*.json`, `.releaserc*`, `tools/release-recover.mjs`, the workflows that build or publish (`release.yml`, `ci.yml`), and the reviewer's own code (`tools/cert-review/`, `cert-review*.yml`, `.github/runner.mjs`). **Main's code decides:** every job checks out the pull request's base and fetches only the trees under test, so the path filter, pack, suite, findings, the instructions and record all run from `main`. A pull request that changes the reviewer is reviewed by `main`'s reviewer. The workflow file of a `pull_request` run still comes from the pull request, so a change to the reviewer is held by the **certification self-guard**, the shared composite action `slicc-shared-web/.github/actions/cert-self-guard` at a pinned sha. Its light `self-guard` job runs on every pull request event and every merge group. When the diff touches the reviewer (`.github/workflows/cert*.yml`, `tools/cert-review/**`, `.github/runner.mjs` and the guard's own paths), `cert-review` is red with `reviewer change: needs coordinator review (cert-review-approved on this head)` and no review runs. The label is the Kernel Coordinator's alone: they add `cert-review-approved` after reading the change, and the review then starts with `merge-ok`. The approval is bound to the head's arrival on GitHub, and a push removes the label. A merge group that holds an unapproved reviewer change is red too. If the guard doesn't finish, the status is an error that asks to re-run the failed jobs. **The residual:** the pull request's YAML still decides what runs, so the guard catches an accidental edit, not a deliberate rewrite of this workflow. The pull request that adds this workflow can't pass its own check, because its base has no tools yet. Any other pull request gets a green status that says `not behaviour: <files>`, so the skip is recorded.
|
|
968
|
+
- **Decide by what ships:** when a pull request's behaviour files are only `package.json` and/or `package-lock.json` (a Renovate devDependency bump, say), both sides are packed as usual, and then the `same dist` job (`same.mjs`, in code) compares the two tarballs before anything else runs. It checks the same paths, entry types (a symlink isn't a file) and modes. `package.json` must be equal once `devDependencies`, `version` and `gitHead` are removed, so `scripts` (they ship, a consumer's install runs `postinstall`, and `prepack`/`prepublishOnly` can make the published build differ from the packed one), runtime dependencies, `exports`, `bin`, `files` and `engines` must all match. Every other file must be byte-identical once each side's **exact** version string (build.mjs bakes it into `dist`) is replaced by one placeholder. A version string in one more place, or any looser match, is a difference. Source maps are compared as JSON without their `mappings`, whose columns the longer or shorter version string shifts; the code itself is compared byte for byte. If all of that matches, `cert-review` is green with `not behaviour (identical dist): <candidate sha256> = <base sha256> after normalising <v1>/<v2>`, and the suites, kernel-compat, the review and the record are skipped. `same.json` (uploaded as an artifact) names the rule and both tarballs' full sha256. Any difference, or a `same dist` job that doesn't finish, means the full review. The kernel has no runtime `dependencies`, since esbuild bundles everything into `dist`, so a devDependency that changes the product shows up as a byte difference. A test-only devDependency is checked by `ci / test`, on the pull request and on the merge group.
|
|
965
969
|
- **When:** only for a same-repo pull request with `merge-ok`, when the label is added and on every push while it is set. Each pull request has its own concurrency group, and a newer push cancels the older review. A behaviour change without `merge-ok` gets a red status, `waits for merge-ok to be reviewed`. Other labels don't start or cancel anything. Fork code never reaches the pools or the key.
|
|
966
970
|
- **What:** the checkout is the event's default `refs/pull/N/merge`, the pull request merged onto `main` at review time, so a review is of what would land, without pushing to the branch. Both sides are packed in the same run, on a GitHub-hosted runner, with no secrets: the candidate (the merge ref, `x.y.z-pr.N.<run>`) and the base (its first parent, `main`). Then:
|
|
967
971
|
- the candidate's whole suite, and the base's;
|
|
968
|
-
- the pull request's new or changed test files on the
|
|
972
|
+
- the pull request's new or changed test files on `main`'s tree, with the pull request's `test/**` applied, and `main`'s build (the NEGATIVE: a fix's test must fail on `main` and pass on the candidate);
|
|
969
973
|
- kernel-compat on the candidate's artifact (#812), against npm's `latest`;
|
|
970
974
|
- the review (Claude through `claude-code-action/base-action`, with the Bedrock key in that step only, no issue filing);
|
|
971
975
|
- and record, which re-hashes the candidate's artifact against the pack job's sha256.
|
|
@@ -1100,6 +1104,16 @@ A threaded WASIX C program for `test/integration/thread-signal.test.mjs` (#520).
|
|
|
1100
1104
|
|
|
1101
1105
|
**Build:** `WASIX_SYSROOT_28=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 (`-pthread`) and binaryen's `wasm-opt` on `PATH`. `bin/sigthread.wasm` is 44,788 bytes, sha256 `5781fa39d0a0032cd9b214b71de1f15f589b40e4cecd6862362048dd27f0db56`.
|
|
1102
1106
|
|
|
1107
|
+
### `spintest`
|
|
1108
|
+
|
|
1109
|
+
A WASIX C program and a JS command for `test/integration/transport-spin.test.mjs`, which guards the sync transport's paths from #856 (#864).
|
|
1110
|
+
- `spintest rounds N` does N 1-byte write+read rounds on a pipe, a different byte each round, and prints `N rounds in order` if every byte came back as sent. These are fast replies, which take the transport's spin.
|
|
1111
|
+
- `spintest wait` prints `waiting` and blocks in a 1-byte `read` of its terminal, a reply that comes only when the test types. The test measures its own process's CPU (all threads) while the read blocks.
|
|
1112
|
+
- `spintest big M` writes an M MiB file and reads it back in 1 MiB `read` calls, each of which must be whole and match. A 1 MiB read fills the transport window exactly.
|
|
1113
|
+
- `spinread PATH` (`bin/spinread.mjs`, a JS command) reads a whole file through the synchronous `ctx.sync.fs.readFile` and prints its length and a 31-hash. A file over 1 MiB is a reply larger than the window, so it comes in several chunks.
|
|
1114
|
+
|
|
1115
|
+
**Build:** `WASIX_SYSROOT_28=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 and binaryen's `wasm-opt` on `PATH`. `bin/spintest.wasm` is 50,095 bytes, sha256 `be7659422f187a97fac412ac1dd2ec9d412e8dcd067da88157960352ded6178b`.
|
|
1116
|
+
|
|
1103
1117
|
### `longwait`
|
|
1104
1118
|
|
|
1105
1119
|
A WASIX C program for `test/integration/long-timers.test.mjs` (#767).
|
package/dist/kernel-worker.js
CHANGED
|
@@ -219,13 +219,22 @@ var IndexedDbMeta = class {
|
|
|
219
219
|
this.cache.set(path, next3 ?? null);
|
|
220
220
|
}
|
|
221
221
|
async updateMany(changes) {
|
|
222
|
+
if (changes.length === 0) return;
|
|
223
|
+
this.settleRemoves();
|
|
222
224
|
const [transaction, store] = this.transaction("readwrite");
|
|
225
|
+
const next3 = /* @__PURE__ */ new Map();
|
|
223
226
|
for (const { path, change } of changes) {
|
|
224
227
|
const request2 = store.get(path);
|
|
225
|
-
request2.onsuccess = () =>
|
|
228
|
+
request2.onsuccess = () => {
|
|
229
|
+
const entry = change(request2.result);
|
|
230
|
+
next3.set(path, entry ?? null);
|
|
231
|
+
if (entry) store.put(entry);
|
|
232
|
+
else store.delete(path);
|
|
233
|
+
};
|
|
226
234
|
}
|
|
227
235
|
await settle(transaction, () => void 0);
|
|
228
236
|
this.changed({ paths: changes.map(({ path }) => path) });
|
|
237
|
+
for (const [path, entry] of next3) this.cache.set(path, entry);
|
|
229
238
|
}
|
|
230
239
|
close() {
|
|
231
240
|
this.channel.close();
|
|
@@ -1021,6 +1030,12 @@ function sidecar(meta) {
|
|
|
1021
1030
|
});
|
|
1022
1031
|
const names = meta.fresh ? [...META_OPS, "fresh"] : META_OPS;
|
|
1023
1032
|
const ops = Object.fromEntries(names.map((op) => [op, guarded2(op)]));
|
|
1033
|
+
const { updateMany } = meta;
|
|
1034
|
+
ops.updateMany = updateMany ? (changes) => updateMany.call(meta, changes).catch((err) => {
|
|
1035
|
+
throw translate(err, changes[0]?.path ?? "/", "sidecar", false);
|
|
1036
|
+
}) : async (changes) => {
|
|
1037
|
+
for (const { path, change } of changes) await ops.update(path, change);
|
|
1038
|
+
};
|
|
1024
1039
|
if (meta.absent) ops.absent = meta.absent.bind(meta);
|
|
1025
1040
|
const { removeFile } = meta;
|
|
1026
1041
|
if (removeFile)
|
|
@@ -1503,14 +1518,20 @@ var OpfsFs = class {
|
|
|
1503
1518
|
this.flushing.file.unref?.();
|
|
1504
1519
|
}
|
|
1505
1520
|
async flushFileTimes() {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1521
|
+
const pending = [...this.fileTimes];
|
|
1522
|
+
const stamps = [];
|
|
1523
|
+
for (const [path] of pending) stamps.push(await this.modified(path).catch(() => 0));
|
|
1524
|
+
await this.batched(
|
|
1525
|
+
pending.map(([path, ms], i) => ({
|
|
1509
1526
|
path,
|
|
1510
|
-
(entry) => this.fileTimes.get(path) !== ms || this.explicitWins(entry, ms) ? entry : { ...entry, path, mtimeMs: ms, mtimeFor, ctimeMs: ms }
|
|
1511
|
-
)
|
|
1527
|
+
change: (entry) => this.fileTimes.get(path) !== ms || this.explicitWins(entry, ms) ? entry : { ...entry, path, mtimeMs: ms, mtimeFor: stamps[i], ctimeMs: ms }
|
|
1528
|
+
}))
|
|
1529
|
+
);
|
|
1530
|
+
for (const [path, ms] of pending)
|
|
1512
1531
|
if (this.fileTimes.get(path) === ms) this.fileTimes.delete(path);
|
|
1513
|
-
|
|
1532
|
+
}
|
|
1533
|
+
batched(changes) {
|
|
1534
|
+
return this.meta.updateMany(changes);
|
|
1514
1535
|
}
|
|
1515
1536
|
timesOf(path) {
|
|
1516
1537
|
return this.dirTimes.get(path) ?? this.fileTimes.get(path);
|
|
@@ -1542,15 +1563,21 @@ var OpfsFs = class {
|
|
|
1542
1563
|
});
|
|
1543
1564
|
}
|
|
1544
1565
|
async flushDirTimes() {
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1566
|
+
while (this.dirDirty.size > 0) {
|
|
1567
|
+
const pending = [];
|
|
1568
|
+
for (const path of this.dirDirty) {
|
|
1569
|
+
const ms = this.dirTimes.get(path);
|
|
1570
|
+
if (ms !== void 0) pending.push([path, ms]);
|
|
1571
|
+
}
|
|
1572
|
+
this.dirDirty.clear();
|
|
1573
|
+
await this.batched(
|
|
1574
|
+
pending.map(([path, ms]) => ({
|
|
1575
|
+
path,
|
|
1576
|
+
change: (entry) => (entry?.timesAt ?? -1) >= ms ? entry : { ...entry, path, mtimeMs: ms, mtimeFor: 0, ctimeMs: ms }
|
|
1577
|
+
}))
|
|
1552
1578
|
);
|
|
1553
|
-
|
|
1579
|
+
for (const [path, ms] of pending)
|
|
1580
|
+
if (this.dirTimes.get(path) === ms) this.dirTimes.delete(path);
|
|
1554
1581
|
}
|
|
1555
1582
|
}
|
|
1556
1583
|
async snapshot(handle) {
|
|
@@ -2089,6 +2116,9 @@ var OpfsFs = class {
|
|
|
2089
2116
|
async chmod(path, mode) {
|
|
2090
2117
|
const found = await this.locate(path, true);
|
|
2091
2118
|
this.refuse(found.path);
|
|
2119
|
+
const [known] = await this.meta.get([found.path]);
|
|
2120
|
+
const directory = found.handle?.kind === "directory";
|
|
2121
|
+
if ((this.mode(found.path, directory, known) & PERM_MASK) === (mode & PERM_MASK)) return;
|
|
2092
2122
|
const ctimeMs = Date.now();
|
|
2093
2123
|
await this.meta.update(found.path, (entry) => ({
|
|
2094
2124
|
...entry,
|
|
@@ -6146,7 +6176,7 @@ function satisfies(version, range2) {
|
|
|
6146
6176
|
}
|
|
6147
6177
|
|
|
6148
6178
|
// src/version.ts
|
|
6149
|
-
var KERNEL_VERSION = true ? "1.110.
|
|
6179
|
+
var KERNEL_VERSION = true ? "1.110.3" : "0.0.0-dev";
|
|
6150
6180
|
var OS_RELEASE = "6.0.0-slicc";
|
|
6151
6181
|
function procVersion() {
|
|
6152
6182
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|