@openparachute/app 0.2.0-rc.4 → 0.2.0-rc.5
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 +15 -0
- package/package.json +1 -1
- package/src/self-register.ts +32 -3
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,21 @@ side-by-side:
|
|
|
9
9
|
The admin SPA at `web/admin/` ships inside the host package as
|
|
10
10
|
`dist/admin/`; its version mirrors the host's version.
|
|
11
11
|
|
|
12
|
+
## [app 0.2.0-rc.5] - 2026-05-22
|
|
13
|
+
|
|
14
|
+
fix(app): self-register uses `manifestName` as services.json row key
|
|
15
|
+
(matches hub install path; closes duplicate-port bug).
|
|
16
|
+
|
|
17
|
+
Hub installs modules under `manifest.manifestName` (`"parachute-app"`),
|
|
18
|
+
but the boot-time self-registration was writing under the short name
|
|
19
|
+
`"app"`. The two writes left services.json with two rows on the same
|
|
20
|
+
port, which trips hub's duplicate-port detector on re-read
|
|
21
|
+
(`duplicate port 1946 — claimed by both "parachute-app" and "app"`).
|
|
22
|
+
|
|
23
|
+
The row key is now sourced from `.parachute/module.json#manifestName`,
|
|
24
|
+
so the install path and the runtime path converge to one row. Mirrors
|
|
25
|
+
the fix landed in parachute-runner.
|
|
26
|
+
|
|
12
27
|
## [app 0.2.0-rc.1] + [app-client 0.1.0-rc.1] - 2026-05-21
|
|
13
28
|
|
|
14
29
|
feat(app): Phase 2.0 — extract `@openparachute/app-client` shared
|
package/package.json
CHANGED
package/src/self-register.ts
CHANGED
|
@@ -31,11 +31,40 @@
|
|
|
31
31
|
* When per-UI `uis` map lands (design doc section 12), the caller assembles
|
|
32
32
|
* the `uis` field and passes it through via `extraFields`.
|
|
33
33
|
*/
|
|
34
|
+
import { readFileSync } from "node:fs";
|
|
34
35
|
import * as path from "node:path";
|
|
35
36
|
|
|
36
37
|
import pkg from "../package.json" with { type: "json" };
|
|
37
38
|
import { type ServiceEntry, readServiceEntry, upsertService } from "./services-manifest.ts";
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The canonical services.json row key for the app module — sourced from
|
|
42
|
+
* `.parachute/module.json#manifestName`. Hub looks up modules in
|
|
43
|
+
* services.json by `manifestName` (vault + scribe use this convention), so
|
|
44
|
+
* self-registering under the short name "app" would create a duplicate row
|
|
45
|
+
* alongside the hub-installed `parachute-app` row and trip hub's
|
|
46
|
+
* duplicate-port detector on re-read. See parachute-patterns or the
|
|
47
|
+
* `manifestName` field on .parachute/module.json files. */
|
|
48
|
+
const ROW_NAME = resolveManifestName();
|
|
49
|
+
|
|
50
|
+
function resolveManifestName(): string {
|
|
51
|
+
// Read once at module load — `.parachute/module.json` ships in the
|
|
52
|
+
// package and doesn't change at runtime.
|
|
53
|
+
try {
|
|
54
|
+
const manifestPath = path.resolve(import.meta.dir, "..", ".parachute", "module.json");
|
|
55
|
+
const raw = JSON.parse(readFileSync(manifestPath, "utf8")) as { manifestName?: unknown };
|
|
56
|
+
if (typeof raw.manifestName === "string" && raw.manifestName.length > 0) {
|
|
57
|
+
return raw.manifestName;
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
// Fall through to the conservative fallback. The module.json is part
|
|
61
|
+
// of the published package, so this branch is effectively unreachable
|
|
62
|
+
// in production — it exists so tests that mount a stub package layout
|
|
63
|
+
// don't hard-crash on import.
|
|
64
|
+
}
|
|
65
|
+
return "parachute-app";
|
|
66
|
+
}
|
|
67
|
+
|
|
39
68
|
export type SelfRegisterOpts = {
|
|
40
69
|
/**
|
|
41
70
|
* The port app just bound. Used only as the first-run fallback — if
|
|
@@ -68,7 +97,7 @@ export type SelfRegisterResult = {
|
|
|
68
97
|
ok: boolean;
|
|
69
98
|
/** The path we wrote to (or attempted to write to). */
|
|
70
99
|
manifestPath: string;
|
|
71
|
-
/** True when services.json already had a row for `app` before we wrote. */
|
|
100
|
+
/** True when services.json already had a row for `parachute-app` before we wrote. */
|
|
72
101
|
hadExistingEntry: boolean;
|
|
73
102
|
/** The port we ended up stamping (existing-entry port or boundPort). */
|
|
74
103
|
portWritten: number;
|
|
@@ -92,7 +121,7 @@ export function selfRegister(opts: SelfRegisterOpts): SelfRegisterResult {
|
|
|
92
121
|
|
|
93
122
|
let existing: ServiceEntry | undefined;
|
|
94
123
|
try {
|
|
95
|
-
existing = readServiceEntry(
|
|
124
|
+
existing = readServiceEntry(ROW_NAME, manifestPath);
|
|
96
125
|
} catch (e) {
|
|
97
126
|
const err = e as Error;
|
|
98
127
|
logger.warn(`[app] skipped self-register: ${err.message}`);
|
|
@@ -107,7 +136,7 @@ export function selfRegister(opts: SelfRegisterOpts): SelfRegisterResult {
|
|
|
107
136
|
|
|
108
137
|
const portToWrite = existing?.port ?? opts.boundPort;
|
|
109
138
|
const entry: ServiceEntry = {
|
|
110
|
-
name:
|
|
139
|
+
name: ROW_NAME,
|
|
111
140
|
port: portToWrite,
|
|
112
141
|
paths: ["/app", "/.parachute"],
|
|
113
142
|
health: "/app/healthz",
|