@zerotal/core 1.6.2 → 1.6.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/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/src/command/builtin/ServeCommand.ts +6 -1
- package/src/dev/DevOrchestrator.ts +29 -0
- package/src/support/version.ts +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,31 @@ follows the Zerotal monorepo's unified versioning.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [1.6.3] — 2026-08-15
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`serve --dev` says when the framework on disk is no longer the framework running.** A
|
|
16
|
+
running dev server holds the code it imported at boot: `bun add zerotal@latest` in another
|
|
17
|
+
terminal rewrites `node_modules` and nothing else, and a save restarts only the _worker_,
|
|
18
|
+
which re-executes your app against that same in-memory framework. So an upgrade taken
|
|
19
|
+
mid-session appears to do nothing — the fix is installed, the symptom persists, and the
|
|
20
|
+
reasonable conclusion is that the fix does not work.
|
|
21
|
+
|
|
22
|
+
The supervisor now compares the version it booted with against the one installed, on each
|
|
23
|
+
restart, and says which is which:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
[zerotal:dev] ⚠ framework upgraded on disk: running v1.6.2, installed v1.6.3
|
|
27
|
+
[zerotal:dev] restart the dev server to pick it up (a save will not).
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Once per version, so a long session is not nagged, and silent where there is nothing to read
|
|
31
|
+
— a workspace checkout or a hoisted layout is not a finding.
|
|
32
|
+
|
|
33
|
+
- **The dev banner carries the version** — `Zerotal v1.6.3 › dev`. There was previously
|
|
34
|
+
nothing on screen naming the framework a running server was actually executing.
|
|
35
|
+
|
|
11
36
|
## [1.6.2] — 2026-08-15
|
|
12
37
|
|
|
13
38
|
### Fixed
|
package/package.json
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
type PortOwner,
|
|
25
25
|
} from "../../support/port.ts";
|
|
26
26
|
import { localNetworkAddress } from "../../support/network.ts";
|
|
27
|
+
import { ZEROTAL_VERSION } from "../../support/version.ts";
|
|
27
28
|
import { createInterface } from "node:readline";
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -319,7 +320,11 @@ export class ServeCommand extends Command {
|
|
|
319
320
|
const network = localNetworkAddress();
|
|
320
321
|
|
|
321
322
|
this.newLine();
|
|
322
|
-
|
|
323
|
+
// The version is here because a running server holds the framework it booted
|
|
324
|
+
// with: an upgrade installed in another terminal is invisible until a full
|
|
325
|
+
// restart, and without this line there is nothing on screen to tell you which
|
|
326
|
+
// one you are looking at.
|
|
327
|
+
this.line(` Zerotal v${ZEROTAL_VERSION} › ${mode}`);
|
|
323
328
|
this.newLine();
|
|
324
329
|
this._route("Local", `http://localhost:${port}`);
|
|
325
330
|
this._route(
|
|
@@ -6,6 +6,7 @@ import { watch } from "node:fs";
|
|
|
6
6
|
import type { BuildHookFn } from "./DevBuildHook.ts";
|
|
7
7
|
import { DEV_WORKER_ENV_VAR } from "../support/env.ts";
|
|
8
8
|
import { requestGracefulStop } from "./devShutdown.ts";
|
|
9
|
+
import { ZEROTAL_VERSION, installedCoreVersion } from "../support/version.ts";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* How the orchestrator reports to whatever is presenting dev mode.
|
|
@@ -81,6 +82,8 @@ export class DevOrchestrator {
|
|
|
81
82
|
* queues a single follow-up instead of running in parallel.
|
|
82
83
|
*/
|
|
83
84
|
private _restartInFlight: Promise<void> | null = null;
|
|
85
|
+
/** The installed version already warned about, so one upgrade warns once. */
|
|
86
|
+
private _warnedVersion: string | null = null;
|
|
84
87
|
private _restartQueued = false;
|
|
85
88
|
|
|
86
89
|
/**
|
|
@@ -282,11 +285,37 @@ export class DevOrchestrator {
|
|
|
282
285
|
// browser refetches the updated CSS.
|
|
283
286
|
await this._runBuild();
|
|
284
287
|
|
|
288
|
+
this._warnIfFrameworkUpgradedUnderneath();
|
|
289
|
+
|
|
285
290
|
// Stop before spawning, always: the old server owns the port until it exits.
|
|
286
291
|
await this._stopChild();
|
|
287
292
|
await this._spawnServerWithRetry();
|
|
288
293
|
}
|
|
289
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Say so when the framework on disk is no longer the framework running.
|
|
297
|
+
*
|
|
298
|
+
* A `bun add zerotal@latest` in another terminal changes `node_modules` and
|
|
299
|
+
* nothing else: this process holds the code it imported at boot, and a restart
|
|
300
|
+
* only re-executes the *app* against that same in-memory framework. So the
|
|
301
|
+
* upgrade appears to have no effect — the fix is installed, the symptom
|
|
302
|
+
* persists, and the obvious conclusion is that the fix does not work.
|
|
303
|
+
*
|
|
304
|
+
* Checked on restart rather than at boot, because that is when it can have
|
|
305
|
+
* become true, and warned once per version so a long session is not nagged.
|
|
306
|
+
*/
|
|
307
|
+
private _warnIfFrameworkUpgradedUnderneath(): void {
|
|
308
|
+
const onDisk = installedCoreVersion(this._cwd);
|
|
309
|
+
if (onDisk === null || onDisk === ZEROTAL_VERSION || onDisk === this._warnedVersion) return;
|
|
310
|
+
|
|
311
|
+
this._warnedVersion = onDisk;
|
|
312
|
+
this._say(
|
|
313
|
+
` [zerotal:dev] ⚠ framework upgraded on disk: running v${ZEROTAL_VERSION}, installed v${onDisk}`,
|
|
314
|
+
"warn",
|
|
315
|
+
);
|
|
316
|
+
this._say(" [zerotal:dev] restart the dev server to pick it up (a save will not).", "warn");
|
|
317
|
+
}
|
|
318
|
+
|
|
290
319
|
/** Stop the running server and wait for it to actually exit, releasing the port. */
|
|
291
320
|
private async _stopChild(): Promise<void> {
|
|
292
321
|
const child = this._child;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The framework version this build of `@zerotal/core` is.
|
|
3
|
+
*
|
|
4
|
+
* Read from the manifest rather than written down, for the reason `@zerotal/monitor`
|
|
5
|
+
* already learned: a hardcoded version is correct until the next release, and three
|
|
6
|
+
* separate literals across the monorepo once claimed a version that had never been
|
|
7
|
+
* published. The monorepo releases in lockstep, so this package's manifest carries
|
|
8
|
+
* the framework version, and it ships inside the tarball.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
|
|
14
|
+
const pkg: { version: string } = JSON.parse(
|
|
15
|
+
readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
|
|
16
|
+
) as { version: string };
|
|
17
|
+
|
|
18
|
+
/** Full version of the running framework, e.g. `1.6.2`. */
|
|
19
|
+
export const ZEROTAL_VERSION: string = pkg.version;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The version of `@zerotal/core` currently installed in a project, which is not
|
|
23
|
+
* necessarily {@link ZEROTAL_VERSION} — a long-running process holds the code it
|
|
24
|
+
* booted with, so an upgrade lands on disk without reaching it.
|
|
25
|
+
*
|
|
26
|
+
* @param cwd - Project root to look under.
|
|
27
|
+
* @returns The installed version, or `null` when there is nothing to read — a
|
|
28
|
+
* workspace checkout, a hoisted layout, a partial install. Absence is not a
|
|
29
|
+
* finding, so callers stay quiet on `null` rather than guessing.
|
|
30
|
+
*/
|
|
31
|
+
export function installedCoreVersion(cwd: string): string | null {
|
|
32
|
+
try {
|
|
33
|
+
const raw = readFileSync(`${cwd}/node_modules/@zerotal/core/package.json`, "utf8");
|
|
34
|
+
const { version } = JSON.parse(raw) as { version?: string };
|
|
35
|
+
return version ?? null;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|