@aact/view 3.0.0-beta.19
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 +118 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +123 -0
- package/dist/index.js.map +1 -0
- package/dist/load-model.d.ts +19 -0
- package/dist/load-model.js +15 -0
- package/dist/load-model.js.map +1 -0
- package/dist/server.d.ts +84 -0
- package/dist/server.js +210 -0
- package/dist/server.js.map +1 -0
- package/dist/ui/assets/elk-B440gP8v.js +25 -0
- package/dist/ui/assets/elk-B440gP8v.js.map +1 -0
- package/dist/ui/assets/index-D2ZA0Nkz.js +4 -0
- package/dist/ui/assets/index-D2ZA0Nkz.js.map +1 -0
- package/dist/ui/assets/index-DFjT4EUS.css +1 -0
- package/dist/ui/assets/rolldown-runtime-CkqCuyE9.js +1 -0
- package/dist/ui/assets/svelte-DegjF43v.js +3 -0
- package/dist/ui/assets/svelte-DegjF43v.js.map +1 -0
- package/dist/ui/assets/xyflow-BjlDntcZ.js +3 -0
- package/dist/ui/assets/xyflow-BjlDntcZ.js.map +1 -0
- package/dist/ui/assets/xyflow-na7JlWeF.css +1 -0
- package/dist/ui/index.html +18 -0
- package/dist/watcher.d.ts +19 -0
- package/dist/watcher.js +48 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# `@aact/view`
|
|
2
|
+
|
|
3
|
+
Browser-based live viewer for the C4 architecture model that
|
|
4
|
+
[aact](https://github.com/Byndyusoft/aact) parses. The Structurizr
|
|
5
|
+
DSL / C4-PUML / model-json source you point `aact check` at is the
|
|
6
|
+
source of truth; `aact view` renders it as an interactive graph
|
|
7
|
+
that re-layouts every time you save the file.
|
|
8
|
+
|
|
9
|
+
> **Status: experimental.** Ships on the `proposal/aact-view`
|
|
10
|
+
> branch; not yet on a beta release. Expect rough edges around
|
|
11
|
+
> non-default models and watch the CHANGELOG entry when the feature
|
|
12
|
+
> graduates.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
`@aact/view` is an optional companion package. Install alongside
|
|
17
|
+
aact in the project you want to inspect:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -D @aact/view
|
|
21
|
+
# or
|
|
22
|
+
pnpm add -D @aact/view
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The core `aact` package detects `@aact/view` via dynamic import; no
|
|
26
|
+
configuration plumbing.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx aact view # uses aact.config.ts in cwd
|
|
32
|
+
npx aact view --port 4321 # pin port (defaults to 3000 with auto-fallback)
|
|
33
|
+
npx aact view --no-open # skip auto-opening the browser
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The console prints a URL with a per-session auth token:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
▸ aact view ready at http://localhost:3000/?token=AbCd…
|
|
40
|
+
watching ./architecture.dsl for changes (Ctrl-C to stop)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Open that URL. Saving the source file re-parses through aact-core
|
|
44
|
+
and pushes the new model over WebSocket — the graph re-layouts in
|
|
45
|
+
place.
|
|
46
|
+
|
|
47
|
+
## What you see
|
|
48
|
+
|
|
49
|
+
The canvas follows the Simon Brown C4 reference palette (Person
|
|
50
|
+
deep blue, System mid blue, Container lighter, Component lightest,
|
|
51
|
+
externals neutral slate). Three view modes in the top bar:
|
|
52
|
+
|
|
53
|
+
- **Drill** — classic C4 levels. Double-click a boundary to descend;
|
|
54
|
+
breadcrumb walks back up.
|
|
55
|
+
- **Expand** — toggle boundaries open inline; parents stay visible
|
|
56
|
+
so cross-context interactions read in one frame.
|
|
57
|
+
- **Flat** — every boundary expanded at once; read-only big-picture
|
|
58
|
+
view.
|
|
59
|
+
|
|
60
|
+
Two more topbar toggles cover edge presentation:
|
|
61
|
+
|
|
62
|
+
- **Edge style** — Curve / Smooth / Step. Personal preference,
|
|
63
|
+
persisted in localStorage.
|
|
64
|
+
- **Edge filter** — All / Cross-BC. In Cross-BC mode intra-boundary
|
|
65
|
+
relations fade to background and inter-context interactions light
|
|
66
|
+
up; useful when an API gateway has many fan-outs and you want to
|
|
67
|
+
see which Bounded Contexts actually talk.
|
|
68
|
+
|
|
69
|
+
Hover a node to highlight only the edges incident to it; everything
|
|
70
|
+
else dims. The right-side details panel shows the selected
|
|
71
|
+
element / boundary's tags, technology, source location (clickable —
|
|
72
|
+
opens your IDE), properties, and outgoing relations.
|
|
73
|
+
|
|
74
|
+
The full visual + interaction spec lives in [`DESIGN.md`](./DESIGN.md).
|
|
75
|
+
|
|
76
|
+
## Live reload
|
|
77
|
+
|
|
78
|
+
A chokidar watcher debounces source changes by 80ms and coalesces
|
|
79
|
+
in-flight reloads. When the parser fails (broken DSL syntax) the
|
|
80
|
+
last good model stays on screen and the status pill flips to
|
|
81
|
+
"error" with the parser message — restoring the file recovers via
|
|
82
|
+
the next successful broadcast.
|
|
83
|
+
|
|
84
|
+
## Security
|
|
85
|
+
|
|
86
|
+
`aact view` listens on `localhost` only. Each session generates a
|
|
87
|
+
24-byte random auth token; `/api/model` and the `/api/ws` upgrade
|
|
88
|
+
require it as either a query string parameter (first navigation) or
|
|
89
|
+
a `HttpOnly` cookie (set on first HTML response). This stops random
|
|
90
|
+
browser tabs / extensions on the same machine from reading your
|
|
91
|
+
architecture graph or following `vscode://file/...` source links.
|
|
92
|
+
|
|
93
|
+
## What it doesn't do
|
|
94
|
+
|
|
95
|
+
- **Editing.** The viewer is read-only. Source DSL/PUML stays the
|
|
96
|
+
authority; your IDE is the editor.
|
|
97
|
+
- **Per-user layout persistence.** Positions are deterministic from
|
|
98
|
+
the model — every re-parse re-runs ELK so the layout is
|
|
99
|
+
reproducible across machines.
|
|
100
|
+
- **ArchiMate / Deployment view / UML / BPMN.** C4 paradigm only,
|
|
101
|
+
matching aact-core scope.
|
|
102
|
+
|
|
103
|
+
## Known follow-ups
|
|
104
|
+
|
|
105
|
+
These are flagged but not built:
|
|
106
|
+
|
|
107
|
+
- **ELK in a worker** — layout currently runs on the main thread.
|
|
108
|
+
Sub-100ms on typical C4 models (V < 100), can take 300-500ms at
|
|
109
|
+
V > 500. Canonical fix: ship `elkjs/lib/elk-worker.min.js` as a
|
|
110
|
+
static asset via Vite `?url` + `new ELK({ workerUrl })` so ELK
|
|
111
|
+
spawns its own sub-worker out of the main thread.
|
|
112
|
+
- **Search / filter by name.**
|
|
113
|
+
- **Focus mode** — pick a node, dim all non-1-hop-neighbours.
|
|
114
|
+
- **Export to SVG / PNG.**
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
GPL-3.0, matching aact-core.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { AactConfig } from "aact";
|
|
2
|
+
/**
|
|
3
|
+
* Inputs the core `aact view` subcommand hands over after loading
|
|
4
|
+
* and validating the user's config. Everything is already resolved
|
|
5
|
+
* — paths are absolute, the customRules array is parsed — so the
|
|
6
|
+
* companion has no business touching the loader pipeline a second
|
|
7
|
+
* time. It just hosts the UI on top of what's already in memory.
|
|
8
|
+
*/
|
|
9
|
+
export interface RunWorkbenchOptions {
|
|
10
|
+
/** Loaded + validated `aact.config.ts` payload. */
|
|
11
|
+
readonly config: AactConfig;
|
|
12
|
+
/** Absolute path to the resolved config file, or `null` when the
|
|
13
|
+
* caller (rarely) skipped config discovery. */
|
|
14
|
+
readonly configPath: string | null;
|
|
15
|
+
/** Override port. When omitted, listhen picks the next free
|
|
16
|
+
* port — the workbench prints the URL it actually bound to. */
|
|
17
|
+
readonly port?: number;
|
|
18
|
+
/** Suppress the automatic browser-open. The URL still prints to
|
|
19
|
+
* stdout so CI / headless flows can pick it up. */
|
|
20
|
+
readonly noOpen?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface RunWorkbenchResult {
|
|
23
|
+
/** `0` when the user quit cleanly; `2` when the server failed to
|
|
24
|
+
* boot (port collision the picker couldn't escape, file
|
|
25
|
+
* watcher errored, etc.). */
|
|
26
|
+
readonly exitCode: 0 | 2;
|
|
27
|
+
/** URL the workbench bound to, or `null` if the boot failed
|
|
28
|
+
* before binding. Surfaced on the core CLI's `ViewData`
|
|
29
|
+
* envelope so CI / agents can pick up where the session went. */
|
|
30
|
+
readonly url: string | null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Lifecycle entry point invoked by the core `aact view` subcommand
|
|
34
|
+
* via dynamic import.
|
|
35
|
+
*
|
|
36
|
+
* Boots the local HTTP server (listhen-picked port, browser opens
|
|
37
|
+
* unless `--no-open`), serves the inline workbench page, exposes
|
|
38
|
+
* `GET /api/model` + the `/api/ws` push channel, and wires a
|
|
39
|
+
* chokidar watcher on `config.source.path` so every save triggers
|
|
40
|
+
* a re-load + broadcast.
|
|
41
|
+
*
|
|
42
|
+
* Resolves only when the user terminates the session (Ctrl-C /
|
|
43
|
+
* SIGTERM); the core CLI then propagates the exit code into the
|
|
44
|
+
* envelope. Defensive cleanup: signal handlers tear the watcher
|
|
45
|
+
* + listener down so a hard-killed Node process doesn't leak the
|
|
46
|
+
* port or a stuck file watcher into the editor's `.fseventsd`.
|
|
47
|
+
*/
|
|
48
|
+
export declare const runWorkbench: (options: RunWorkbenchOptions) => Promise<RunWorkbenchResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { loadModelFromConfig } from "./load-model.js";
|
|
3
|
+
import { startServer } from "./server.js";
|
|
4
|
+
import { startWatcher } from "./watcher.js";
|
|
5
|
+
const sourceOf = (config) => typeof config.source === "string" ? config.source : config.source.path;
|
|
6
|
+
const createAuthToken = () => randomBytes(24).toString("base64url");
|
|
7
|
+
/** Build a `ModelEnvelope` from a single in-process loadModel call.
|
|
8
|
+
* Mirrors the wider `aact model --json` envelope so the SPA can
|
|
9
|
+
* reuse aact's contract — schemaVersion stays at `1`. */
|
|
10
|
+
const buildEnvelope = async (options, aactVersion) => {
|
|
11
|
+
const startedAt = performance.now();
|
|
12
|
+
const { model, issues } = await loadModelFromConfig(options.config);
|
|
13
|
+
return {
|
|
14
|
+
schemaVersion: 1,
|
|
15
|
+
command: "view",
|
|
16
|
+
ok: true,
|
|
17
|
+
exitCode: 0,
|
|
18
|
+
data: { model, issues: [...issues] },
|
|
19
|
+
diagnostics: [],
|
|
20
|
+
meta: {
|
|
21
|
+
aactVersion,
|
|
22
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
23
|
+
configPath: options.configPath,
|
|
24
|
+
source: sourceOf(options.config),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
const buildReloadError = (options, error, startedAt) => ({
|
|
29
|
+
message: error instanceof Error ? error.message : String(error),
|
|
30
|
+
source: sourceOf(options.config),
|
|
31
|
+
configPath: options.configPath,
|
|
32
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
33
|
+
at: new Date().toISOString(),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Lifecycle entry point invoked by the core `aact view` subcommand
|
|
37
|
+
* via dynamic import.
|
|
38
|
+
*
|
|
39
|
+
* Boots the local HTTP server (listhen-picked port, browser opens
|
|
40
|
+
* unless `--no-open`), serves the inline workbench page, exposes
|
|
41
|
+
* `GET /api/model` + the `/api/ws` push channel, and wires a
|
|
42
|
+
* chokidar watcher on `config.source.path` so every save triggers
|
|
43
|
+
* a re-load + broadcast.
|
|
44
|
+
*
|
|
45
|
+
* Resolves only when the user terminates the session (Ctrl-C /
|
|
46
|
+
* SIGTERM); the core CLI then propagates the exit code into the
|
|
47
|
+
* envelope. Defensive cleanup: signal handlers tear the watcher
|
|
48
|
+
* + listener down so a hard-killed Node process doesn't leak the
|
|
49
|
+
* port or a stuck file watcher into the editor's `.fseventsd`.
|
|
50
|
+
*/
|
|
51
|
+
export const runWorkbench = async (options) => {
|
|
52
|
+
// Resolve aact version lazily — it's only surfaced on the
|
|
53
|
+
// envelope `meta`, no need to crash the boot if the workspace
|
|
54
|
+
// is mid-rebuild. Falls back to "unknown".
|
|
55
|
+
let aactVersion = "unknown";
|
|
56
|
+
try {
|
|
57
|
+
const mod = (await import("aact"));
|
|
58
|
+
if (typeof mod.aactVersion === "string")
|
|
59
|
+
aactVersion = mod.aactVersion;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// ignore — version is informational only
|
|
63
|
+
}
|
|
64
|
+
let server;
|
|
65
|
+
try {
|
|
66
|
+
const authToken = createAuthToken();
|
|
67
|
+
const envelope = await buildEnvelope(options, aactVersion);
|
|
68
|
+
server = await startServer({
|
|
69
|
+
...(options.port === undefined ? {} : { port: options.port }),
|
|
70
|
+
...(options.noOpen === undefined ? {} : { noOpen: options.noOpen }),
|
|
71
|
+
initialEnvelope: envelope,
|
|
72
|
+
authToken,
|
|
73
|
+
});
|
|
74
|
+
// eslint-disable-next-line no-console -- intentional user-facing banner
|
|
75
|
+
console.log(`▸ aact view ready at ${server.url}`);
|
|
76
|
+
if (options.noOpen) {
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.log(` open it manually — --no-open suppressed auto-launch`);
|
|
79
|
+
}
|
|
80
|
+
// eslint-disable-next-line no-console
|
|
81
|
+
console.log(` watching ${sourceOf(options.config)} for changes (Ctrl-C to stop)`);
|
|
82
|
+
const watcher = startWatcher({
|
|
83
|
+
paths: [sourceOf(options.config)],
|
|
84
|
+
onChange: async () => {
|
|
85
|
+
const startedAt = performance.now();
|
|
86
|
+
try {
|
|
87
|
+
const next = await buildEnvelope(options, aactVersion);
|
|
88
|
+
server?.broadcast(next);
|
|
89
|
+
// eslint-disable-next-line no-console
|
|
90
|
+
console.log(` ↻ reloaded (${Object.keys(next.data.model.elements).length} elements, ${next.data.issues.length} issues)`);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const viewError = buildReloadError(options, error, startedAt);
|
|
94
|
+
server?.broadcastError(viewError);
|
|
95
|
+
// eslint-disable-next-line no-console
|
|
96
|
+
console.error(` ✗ reload failed: ${viewError.message}`);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
await new Promise((resolve) => {
|
|
101
|
+
const cleanup = (signal) => {
|
|
102
|
+
// eslint-disable-next-line no-console
|
|
103
|
+
console.log(`\n• stopping (${signal})…`);
|
|
104
|
+
process.off("SIGINT", onSigint);
|
|
105
|
+
process.off("SIGTERM", onSigterm);
|
|
106
|
+
void Promise.allSettled([watcher.close(), server?.close()]).then(() => resolve());
|
|
107
|
+
};
|
|
108
|
+
const onSigint = () => cleanup("SIGINT");
|
|
109
|
+
const onSigterm = () => cleanup("SIGTERM");
|
|
110
|
+
process.on("SIGINT", onSigint);
|
|
111
|
+
process.on("SIGTERM", onSigterm);
|
|
112
|
+
});
|
|
113
|
+
return { exitCode: 0, url: server.url };
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (server)
|
|
117
|
+
await server.close().catch(() => { });
|
|
118
|
+
// eslint-disable-next-line no-console
|
|
119
|
+
console.error(`aact view boot failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
120
|
+
return { exitCode: 2, url: server?.url ?? null };
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AactConfig, Model, ModelIssue } from "aact";
|
|
2
|
+
/**
|
|
3
|
+
* Load the normalised Model from `aact.config.ts`'s `source`.
|
|
4
|
+
*
|
|
5
|
+
* The companion deliberately routes through aact's public format
|
|
6
|
+
* registry instead of forking a loader — every format aact already
|
|
7
|
+
* supports (PUML, Structurizr DSL/JSON, model-json) flows through
|
|
8
|
+
* here automatically, and a third-party format added to the registry
|
|
9
|
+
* works in the workbench the moment it's installed.
|
|
10
|
+
*
|
|
11
|
+
* `config.source.path` is already absolute after
|
|
12
|
+
* `loadAndValidateConfig` ran upstream in the core CLI; the
|
|
13
|
+
* companion treats it as read-only data.
|
|
14
|
+
*/
|
|
15
|
+
export interface ModelLoadResult {
|
|
16
|
+
readonly model: Model;
|
|
17
|
+
readonly issues: readonly ModelIssue[];
|
|
18
|
+
}
|
|
19
|
+
export declare const loadModelFromConfig: (config: AactConfig) => Promise<ModelLoadResult>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { canLoad, loadFormat } from "aact";
|
|
2
|
+
export const loadModelFromConfig = async (config) => {
|
|
3
|
+
const source = typeof config.source === "string"
|
|
4
|
+
? { type: undefined, path: config.source }
|
|
5
|
+
: config.source;
|
|
6
|
+
if (!source.type) {
|
|
7
|
+
throw new Error(`aact config source.type is required for view (got "${source.path}" without type)`);
|
|
8
|
+
}
|
|
9
|
+
const format = await loadFormat(source.type);
|
|
10
|
+
if (!canLoad(format)) {
|
|
11
|
+
throw new Error(`Format "${source.type}" cannot load — load capability missing`);
|
|
12
|
+
}
|
|
13
|
+
return format.load(source.path);
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=load-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-model.js","sourceRoot":"","sources":["../src/load-model.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAoB3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,MAAkB,EACQ,EAAE;IAC5B,MAAM,MAAM,GACV,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ;QAC/B,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE;QAC1C,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;IAEpB,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,sDAAsD,MAAM,CAAC,IAAI,iBAAiB,CACnF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,WAAW,MAAM,CAAC,IAAI,yCAAyC,CAChE,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type Listener } from "listhen";
|
|
2
|
+
import type { ModelLoadResult } from "./load-model.js";
|
|
3
|
+
/**
|
|
4
|
+
* The envelope shape the server pushes over `/api/ws` and returns
|
|
5
|
+
* from `/api/model`. Mirrors aact's `CliEnvelope<ModelData>` enough
|
|
6
|
+
* that the client can treat them interchangeably — the workbench
|
|
7
|
+
* adds no second contract.
|
|
8
|
+
*/
|
|
9
|
+
export interface ModelEnvelope {
|
|
10
|
+
readonly schemaVersion: 1;
|
|
11
|
+
readonly command: "view";
|
|
12
|
+
readonly ok: boolean;
|
|
13
|
+
readonly exitCode: 0 | 1 | 2;
|
|
14
|
+
readonly data: {
|
|
15
|
+
readonly model: ModelLoadResult["model"];
|
|
16
|
+
readonly issues: readonly ModelLoadResult["issues"][number][];
|
|
17
|
+
};
|
|
18
|
+
readonly diagnostics: readonly never[];
|
|
19
|
+
readonly meta: {
|
|
20
|
+
readonly aactVersion: string;
|
|
21
|
+
readonly durationMs: number;
|
|
22
|
+
readonly configPath: string | null;
|
|
23
|
+
readonly source: string | null;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface ViewError {
|
|
27
|
+
readonly message: string;
|
|
28
|
+
readonly source: string | null;
|
|
29
|
+
readonly configPath: string | null;
|
|
30
|
+
readonly durationMs: number;
|
|
31
|
+
readonly at: string;
|
|
32
|
+
}
|
|
33
|
+
export type ServerMessage = {
|
|
34
|
+
readonly type: "model-update";
|
|
35
|
+
readonly envelope: ModelEnvelope;
|
|
36
|
+
} | {
|
|
37
|
+
readonly type: "model-error";
|
|
38
|
+
readonly error: ViewError;
|
|
39
|
+
};
|
|
40
|
+
/** Subscriber callback type — the workbench pushes a fresh envelope
|
|
41
|
+
* to every subscriber whenever chokidar reports a source change. */
|
|
42
|
+
export type Subscriber = (message: ServerMessage) => void;
|
|
43
|
+
export interface ServerHandle {
|
|
44
|
+
readonly listener: Listener;
|
|
45
|
+
readonly url: string;
|
|
46
|
+
/** Register a callback that fires on every model update. Returns
|
|
47
|
+
* an unsubscribe function. */
|
|
48
|
+
subscribe(fn: Subscriber): () => void;
|
|
49
|
+
/** Push a fresh envelope to every connected client. */
|
|
50
|
+
broadcast(envelope: ModelEnvelope): void;
|
|
51
|
+
/** Push a reload failure while keeping the latest valid model cached. */
|
|
52
|
+
broadcastError(error: ViewError): void;
|
|
53
|
+
/** Update the cached envelope so new HTTP `/api/model` hits return
|
|
54
|
+
* the latest known model without re-loading from disk. */
|
|
55
|
+
setCurrent(envelope: ModelEnvelope): void;
|
|
56
|
+
close(): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
export interface ServerOptions {
|
|
59
|
+
readonly port?: number;
|
|
60
|
+
/** Skip `listhen`'s automatic browser open — useful when the
|
|
61
|
+
* workbench is driven from CI or an editor extension. */
|
|
62
|
+
readonly noOpen?: boolean;
|
|
63
|
+
/** Initial envelope to serve immediately. The watcher will
|
|
64
|
+
* replace it via `setCurrent` whenever the source changes. */
|
|
65
|
+
readonly initialEnvelope: ModelEnvelope;
|
|
66
|
+
/** Per-session token protecting local JSON / WS endpoints from
|
|
67
|
+
* unrelated browser pages hitting localhost. */
|
|
68
|
+
readonly authToken: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Bring up the local workbench HTTP server.
|
|
72
|
+
*
|
|
73
|
+
* Routes:
|
|
74
|
+
* - `GET /` — inline HTML page (Phase 1 placeholder; the
|
|
75
|
+
* Svelte SPA replaces it in Phase 3).
|
|
76
|
+
* - `GET /api/model` — current `ModelEnvelope`, JSON.
|
|
77
|
+
* - `GET /api/ws` — WebSocket channel pushing `{type:"model-update", envelope}`
|
|
78
|
+
* payloads on every chokidar event.
|
|
79
|
+
*
|
|
80
|
+
* The h3 app keeps a per-client subscriber list keyed by peer; the
|
|
81
|
+
* watcher (in `runWorkbench`) calls `broadcast()` to notify the SPA
|
|
82
|
+
* after each successful `loadModelFromConfig`.
|
|
83
|
+
*/
|
|
84
|
+
export declare const startServer: (options: ServerOptions) => Promise<ServerHandle>;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { H3, defineWebSocketHandler, toNodeHandler } from "h3";
|
|
5
|
+
import { listen } from "listhen";
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the bundled SPA dist relative to this module so the path
|
|
8
|
+
* survives both the source tree (`packages/view/dist/ui/`) and the
|
|
9
|
+
* post-publish layout (`<install>/node_modules/@aact/view/dist/ui/`).
|
|
10
|
+
* Computed from `import.meta.url` because aact uses pure ESM —
|
|
11
|
+
* `__dirname` is unavailable.
|
|
12
|
+
*/
|
|
13
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const UI_ROOT = path.resolve(HERE, "ui");
|
|
15
|
+
const MIME = {
|
|
16
|
+
".html": "text/html; charset=utf-8",
|
|
17
|
+
".js": "text/javascript; charset=utf-8",
|
|
18
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
19
|
+
".css": "text/css; charset=utf-8",
|
|
20
|
+
".svg": "image/svg+xml",
|
|
21
|
+
".png": "image/png",
|
|
22
|
+
".ico": "image/x-icon",
|
|
23
|
+
".map": "application/json; charset=utf-8",
|
|
24
|
+
".json": "application/json; charset=utf-8",
|
|
25
|
+
};
|
|
26
|
+
const mimeFor = (file) => MIME[path.extname(file).toLowerCase()] ?? "application/octet-stream";
|
|
27
|
+
const isInside = (root, candidate) => {
|
|
28
|
+
const relative = path.relative(root, candidate);
|
|
29
|
+
return (relative === "" ||
|
|
30
|
+
(!relative.startsWith("..") && !path.isAbsolute(relative)));
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Bring up the local workbench HTTP server.
|
|
34
|
+
*
|
|
35
|
+
* Routes:
|
|
36
|
+
* - `GET /` — inline HTML page (Phase 1 placeholder; the
|
|
37
|
+
* Svelte SPA replaces it in Phase 3).
|
|
38
|
+
* - `GET /api/model` — current `ModelEnvelope`, JSON.
|
|
39
|
+
* - `GET /api/ws` — WebSocket channel pushing `{type:"model-update", envelope}`
|
|
40
|
+
* payloads on every chokidar event.
|
|
41
|
+
*
|
|
42
|
+
* The h3 app keeps a per-client subscriber list keyed by peer; the
|
|
43
|
+
* watcher (in `runWorkbench`) calls `broadcast()` to notify the SPA
|
|
44
|
+
* after each successful `loadModelFromConfig`.
|
|
45
|
+
*/
|
|
46
|
+
export const startServer = async (options) => {
|
|
47
|
+
let current = options.initialEnvelope;
|
|
48
|
+
const subscribers = new Set();
|
|
49
|
+
const peers = new Set();
|
|
50
|
+
const authCookie = `aact_view_token=${options.authToken}; Path=/; SameSite=Strict; HttpOnly`;
|
|
51
|
+
const hasAuthCookie = (headers) => {
|
|
52
|
+
const raw = headers.get("cookie") ?? "";
|
|
53
|
+
return raw
|
|
54
|
+
.split(";")
|
|
55
|
+
.map((part) => part.trim())
|
|
56
|
+
.some((part) => part === `aact_view_token=${options.authToken}`);
|
|
57
|
+
};
|
|
58
|
+
const isAuthorized = (url, headers) => url.searchParams.get("token") === options.authToken ||
|
|
59
|
+
hasAuthCookie(headers);
|
|
60
|
+
const parseRequestUrl = (input) => new URL(input, "http://localhost");
|
|
61
|
+
const authUrl = (url) => `${url.replace(/\/$/, "")}/?token=${encodeURIComponent(options.authToken)}`;
|
|
62
|
+
const unauthorized = () => new Response("Unauthorized", {
|
|
63
|
+
status: 401,
|
|
64
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
65
|
+
});
|
|
66
|
+
const htmlHeaders = (contentType) => ({
|
|
67
|
+
"content-type": contentType,
|
|
68
|
+
"set-cookie": authCookie,
|
|
69
|
+
});
|
|
70
|
+
const sendToSubscribers = (message) => {
|
|
71
|
+
for (const fn of subscribers) {
|
|
72
|
+
try {
|
|
73
|
+
fn(message);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Subscriber threw — don't let one bad listener stop the
|
|
77
|
+
// others from receiving the update.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const sendToPeers = (message) => {
|
|
82
|
+
const payload = JSON.stringify(message);
|
|
83
|
+
for (const peer of peers) {
|
|
84
|
+
try {
|
|
85
|
+
peer.send(payload);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Peer dropped mid-broadcast; CrossWS will report `close`
|
|
89
|
+
// / `error` next tick so just skip it here.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const app = new H3();
|
|
94
|
+
const resolveWebSocketHooks = async (request) => {
|
|
95
|
+
const response = (await app.fetch(request));
|
|
96
|
+
return response.crossws ?? {};
|
|
97
|
+
};
|
|
98
|
+
app.get("/api/model", (event) => {
|
|
99
|
+
const url = parseRequestUrl(event.req.url);
|
|
100
|
+
if (!isAuthorized(url, event.req.headers))
|
|
101
|
+
return unauthorized();
|
|
102
|
+
return current;
|
|
103
|
+
});
|
|
104
|
+
app.get("/api/ws", defineWebSocketHandler({
|
|
105
|
+
upgrade(request) {
|
|
106
|
+
const url = parseRequestUrl(request.url);
|
|
107
|
+
if (!isAuthorized(url, request.headers))
|
|
108
|
+
return unauthorized();
|
|
109
|
+
},
|
|
110
|
+
open(peer) {
|
|
111
|
+
peers.add(peer);
|
|
112
|
+
// Send the latest known envelope right away so a freshly
|
|
113
|
+
// opened tab doesn't have to wait for the next watcher event
|
|
114
|
+
// to populate.
|
|
115
|
+
peer.send(JSON.stringify({ type: "model-update", envelope: current }));
|
|
116
|
+
},
|
|
117
|
+
close(peer) {
|
|
118
|
+
peers.delete(peer);
|
|
119
|
+
},
|
|
120
|
+
error(peer) {
|
|
121
|
+
peers.delete(peer);
|
|
122
|
+
},
|
|
123
|
+
}));
|
|
124
|
+
// Serve the pre-built Svelte SPA from `dist/ui/`. Resolve each
|
|
125
|
+
// request path against the bundle dir, fall back to `index.html`
|
|
126
|
+
// for client-side routes (Svelte Flow drill-down is in-app, but
|
|
127
|
+
// a deep link / refresh hits `/something` and we want to bring
|
|
128
|
+
// the SPA up regardless). Aggressive directory traversal blocked
|
|
129
|
+
// by re-resolving and asserting the path stays under UI_ROOT.
|
|
130
|
+
const serveAsset = async (requestPath) => {
|
|
131
|
+
const clean = requestPath.replace(/^\/+/, "").split("?")[0] ?? "";
|
|
132
|
+
const relative = clean === "" ? "index.html" : clean;
|
|
133
|
+
const candidate = path.resolve(UI_ROOT, relative);
|
|
134
|
+
if (!isInside(UI_ROOT, candidate))
|
|
135
|
+
return null;
|
|
136
|
+
try {
|
|
137
|
+
const body = await readFile(candidate);
|
|
138
|
+
return { body, contentType: mimeFor(candidate) };
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
app.get("/", async () => {
|
|
145
|
+
const asset = await serveAsset("index.html");
|
|
146
|
+
if (!asset) {
|
|
147
|
+
return new Response("aact view UI bundle is missing — run `pnpm --filter @aact/view build:ui`.", {
|
|
148
|
+
status: 500,
|
|
149
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return new Response(asset.body, {
|
|
153
|
+
headers: htmlHeaders(asset.contentType),
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
app.get("/**:path", async (event) => {
|
|
157
|
+
const url = new URL(event.req.url);
|
|
158
|
+
if (url.pathname.startsWith("/api/")) {
|
|
159
|
+
return new Response("Not Found", { status: 404 });
|
|
160
|
+
}
|
|
161
|
+
const asset = await serveAsset(url.pathname);
|
|
162
|
+
if (asset) {
|
|
163
|
+
return new Response(asset.body, {
|
|
164
|
+
headers: htmlHeaders(asset.contentType),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
// SPA fallback — let the client route in-app.
|
|
168
|
+
const indexAsset = await serveAsset("index.html");
|
|
169
|
+
if (!indexAsset)
|
|
170
|
+
return new Response("Not Found", { status: 404 });
|
|
171
|
+
return new Response(indexAsset.body, {
|
|
172
|
+
headers: htmlHeaders(indexAsset.contentType),
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
const listener = await listen(toNodeHandler(app), {
|
|
176
|
+
port: options.port,
|
|
177
|
+
open: options.noOpen ? false : true,
|
|
178
|
+
showURL: false,
|
|
179
|
+
qr: false,
|
|
180
|
+
public: false,
|
|
181
|
+
ws: { resolve: resolveWebSocketHooks },
|
|
182
|
+
});
|
|
183
|
+
const url = authUrl(listener.url);
|
|
184
|
+
return {
|
|
185
|
+
listener,
|
|
186
|
+
url,
|
|
187
|
+
subscribe(fn) {
|
|
188
|
+
subscribers.add(fn);
|
|
189
|
+
return () => subscribers.delete(fn);
|
|
190
|
+
},
|
|
191
|
+
broadcast(envelope) {
|
|
192
|
+
current = envelope;
|
|
193
|
+
const message = { type: "model-update", envelope };
|
|
194
|
+
sendToPeers(message);
|
|
195
|
+
sendToSubscribers(message);
|
|
196
|
+
},
|
|
197
|
+
broadcastError(error) {
|
|
198
|
+
const message = { type: "model-error", error };
|
|
199
|
+
sendToPeers(message);
|
|
200
|
+
sendToSubscribers(message);
|
|
201
|
+
},
|
|
202
|
+
setCurrent(envelope) {
|
|
203
|
+
current = envelope;
|
|
204
|
+
},
|
|
205
|
+
close() {
|
|
206
|
+
return listener.close();
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|