@unotest/core 0.32.0 → 0.33.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/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.33.0] - 2026-09-06
4
+
5
+ ### Patch Changes
6
+
7
+ - 8846a06: Viewer: a run started the moment the viewer came up is shown, not lost
8
+ until the next change.
9
+
10
+ Watching a directory becomes live a moment after the watcher reports it
11
+ is ready, and a run directory created inside that gap was not merely
12
+ late — it was invisible until something else changed in the same day,
13
+ which in a quiet environment is the next run. Measured at 1-3% on macOS
14
+ with a busy machine, and the window is exactly "open the viewer, start a
15
+ run". The viewer now proves the watch delivers before it declares itself
16
+ up: it creates a directory of its own and waits to hear about it, so
17
+ boot completes on evidence rather than on a promise. A tree it cannot
18
+ write to, or a probe that never comes back, costs a warning in the log
19
+ and boots as before.
20
+
21
+ A directory whose name starts with a dot is never reported as a run — an
22
+ editor's leftovers, or the watcher's own probe, can no longer appear in
23
+ the run list as a phantom.
24
+
25
+ Run queue: a waiter whose process paused is no longer mistaken for dead
26
+ — the queue stamps its own files with its own clock.
27
+
28
+ A run waiting for a slot proves it is alive by touching its ticket, and
29
+ what it is judged against was the modification time the filesystem
30
+ wrote: on a network share that is the server's clock, and against a
31
+ machine busy enough to keep a process off the CPU, a ticket written a
32
+ moment ago could look like it belonged to a process that died. The queue
33
+ now stamps every file it creates with the same clock it judges them by.
34
+
35
+ - Updated dependencies [2abfd4d]
36
+ - Updated dependencies [b3e1220]
37
+ - Updated dependencies [b3e1220]
38
+ - Updated dependencies [5d1bde3]
39
+ - @unotest/protocol@0.33.0
40
+ - @unotest/dsl@0.33.0
41
+
3
42
  ## [0.32.0] - 2026-09-05
4
43
 
5
44
  ### Patch Changes
package/dist/index.js CHANGED
@@ -715,7 +715,7 @@ import {
715
715
  writeFile as writeFile6
716
716
  } from "fs/promises";
717
717
  import { dirname as dirname3, join as join8 } from "path";
718
- async function createExclusive(path, contents) {
718
+ async function createExclusive(path, contents, now) {
719
719
  let handle;
720
720
  try {
721
721
  handle = await open2(path, "wx");
@@ -725,27 +725,33 @@ async function createExclusive(path, contents) {
725
725
  }
726
726
  try {
727
727
  await handle.writeFile(contents, "utf8");
728
+ await handle.utimes(...stampOf(now));
728
729
  } finally {
729
730
  await handle.close();
730
731
  }
731
732
  return true;
732
733
  }
733
734
  __name(createExclusive, "createExclusive");
734
- async function writeFileAtomic(path, contents, tmpToken) {
735
+ async function writeFileAtomic(path, contents, tmpToken, now) {
735
736
  const tmp = join8(dirname3(path), `.tmp-${tmpToken}`);
736
737
  await writeFile6(tmp, contents, "utf8");
738
+ await utimes2(tmp, ...stampOf(now));
737
739
  await rename4(tmp, path);
738
740
  }
739
741
  __name(writeFileAtomic, "writeFileAtomic");
740
742
  async function touchFile(path, now) {
741
- const seconds = now / 1e3;
742
743
  try {
743
- await utimes2(path, seconds, seconds);
744
+ await utimes2(path, ...stampOf(now));
744
745
  } catch (e) {
745
746
  if (!isErrno(e, "ENOENT")) throw e;
746
747
  }
747
748
  }
748
749
  __name(touchFile, "touchFile");
750
+ function stampOf(now) {
751
+ const seconds = now / 1e3;
752
+ return [seconds, seconds];
753
+ }
754
+ __name(stampOf, "stampOf");
749
755
  async function fileAgeMs(path, now) {
750
756
  try {
751
757
  const s = await stat(path);
@@ -874,7 +880,8 @@ var SlotPool = class {
874
880
  for (let n = 0; n < this.opts.total && taken.length < count; n++) {
875
881
  await keepAlive?.();
876
882
  const name = `${SLOT_PREFIX}${n}`;
877
- if (await createExclusive(join9(this.opts.dir, name), body)) {
883
+ const path = join9(this.opts.dir, name);
884
+ if (await createExclusive(path, body, this.opts.now())) {
878
885
  taken.push(name);
879
886
  }
880
887
  }
@@ -915,7 +922,7 @@ var SlotPool = class {
915
922
  });
916
923
  const token = this.opts.token();
917
924
  for (let attempt = 0; attempt < mutex.attempts; attempt++) {
918
- if (await createExclusive(mutex.path, token)) {
925
+ if (await createExclusive(mutex.path, token, this.opts.now())) {
919
926
  try {
920
927
  return await body(() => touchFile(mutex.path, this.opts.now()));
921
928
  } finally {
@@ -1049,7 +1056,8 @@ var FsRunQueue = class {
1049
1056
  await writeFileAtomic(
1050
1057
  join10(this.ticketsDir, name),
1051
1058
  JSON.stringify(ticket),
1052
- `${name}-${this.random()}`
1059
+ `${name}-${this.random()}`,
1060
+ createdAt
1053
1061
  );
1054
1062
  } catch (e) {
1055
1063
  throw new QueueUnavailableError(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unotest/core",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Shared runner-side utilities for the @unotest ecosystem: JSONL run-artifact writer (steps.jsonl + heartbeat), atomic run-manifest + run-sources writers, atomic runtime-state writer with a protocol-normalizing runtime-inspection helper, layered .env reader/applier (target + environment axes), idempotent .gitignore updater. Used by @unotest/web and @unotest/mobile; depends on @unotest/protocol plus a type-only import of @unotest/dsl/executor event types (M-20). Unlike protocol (pure data), this package owns the thin filesystem layer both runners need.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -29,8 +29,8 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "chokidar": "^4.0.3",
32
- "@unotest/protocol": "^0.32.0",
33
- "@unotest/dsl": "^0.32.0"
32
+ "@unotest/dsl": "^0.33.0",
33
+ "@unotest/protocol": "^0.33.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^22.10.0",