@unispechq/unispec-platform 0.1.5 → 0.2.1

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 CHANGED
@@ -89,48 +89,39 @@ Options:
89
89
  - `--format <format>` — `json|yaml` (for JSON-like targets)
90
90
  - `--output <file>` — write output to file (use `-` for stdout)
91
91
 
92
- ### `unispec dev`
93
- Local orchestrator mode.
92
+ ### `unispec portal <dev|start>`
93
+ Launch UniSpec Portal (Next.js orchestrator).
94
94
 
95
- Serves:
95
+ In the current architecture, `unispec-platform` provides CLI utilities (validate/normalize/diff/convert) and a launcher for `@unispechq/unispec-portal`.
96
96
 
97
- - `GET /` — embedded UI (reads `/unispec.json` in the browser)
98
- - `GET /unispec.json` — aggregated snapshot
99
- - `GET /health` — healthcheck
97
+ Examples:
100
98
 
101
- Options:
102
-
103
- - `--config <path>` — path to config file (default: `unispec.config.json|yaml|yml`)
104
- - `--host <host>` — bind host (default: `127.0.0.1`)
105
- - `--port <port>` — bind port (default: `4141`)
106
- - `--watch` — watch config/spec files and refresh aggregated snapshot
107
- - `--ui` / `--no-ui` — enable/disable embedded UI at `/` (default: enabled)
108
- - `--ui-dev <url>` — proxy UI requests to an external dev server (e.g. Next.js) for HMR
99
+ ```bash
100
+ unispec portal dev --config ./unispec.config.json
101
+ unispec portal start --config ./unispec.config.json
102
+ ```
109
103
 
110
- Planned responsibilities:
104
+ Options:
111
105
 
112
- - Watch local specs and/or connected services
113
- - Aggregate multiple UniSpec documents (microservices mode)
114
- - Serve a local dashboard and endpoints (e.g. `/unispec.json` for aggregated view)
115
- - Provide a single workflow to validate/normalize/diff before publishing to Registry
106
+ - `--config <path>` path to config file
107
+ - `--host <host>` bind host (forwarded to portal)
108
+ - `--port <port>` bind port (forwarded to portal)
109
+ - `--storage <type>` `json|sqlite` (forwarded to portal)
110
+ - `--data-dir <path>` — data directory (forwarded to portal)
111
+ - `--open` — open browser (forwarded to portal)
112
+ - `--verbose` — verbose output (forwarded to portal)
113
+ - `--json-errors` — output errors as JSON (forwarded to portal)
114
+ - `--portal-package <name>` — portal package name (default: `@unispechq/unispec-portal`)
116
115
 
117
116
  ---
118
117
 
119
- ## Dev server (local orchestrator)
118
+ ## Notes
120
119
 
121
- `unispec dev` starts a local HTTP API that aggregates multiple UniSpec documents.
120
+ All orchestration responsibilities (HTTP API, watch mode, workspace storage, and test runner) live in the Portal package.
122
121
 
123
- Default bind:
124
-
125
- - Host: `127.0.0.1`
126
- - Port: `4141`
127
-
128
- Endpoints:
129
-
130
- - `GET /health` -> `OK`
131
- - `GET /unispec.json` -> aggregated JSON
122
+ ---
132
123
 
133
- Aggregation format (A):
124
+ ## Aggregation format (A)
134
125
 
135
126
  ```json
136
127
  {
@@ -169,6 +160,128 @@ Then open:
169
160
  - `http://127.0.0.1:4141/unispec.json`
170
161
  - `http://127.0.0.1:4141/`
171
162
 
163
+ ### `unispec start`
164
+ Production-like server mode.
165
+
166
+ `unispec start` runs the same HTTP surface as `unispec dev`, but is intended for long-running use.
167
+
168
+ Options:
169
+
170
+ - `--config <path>` — path to config file
171
+ - `--host <host>` — bind host (default: `127.0.0.1`)
172
+ - `--port <port>` — bind port (default: `4141`)
173
+ - `--ui` / `--no-ui` — enable/disable embedded UI
174
+ - `--ui-url <url>` — proxy UI requests to an external UI server
175
+ - `--portal` — spawn portal server (SSR) from an installed dependency and proxy UI to it
176
+ - `--portal-port <port>` — port for portal server (default: `3000`)
177
+ - `--portal-package <name>` — portal package name (default: `@unispechq/unispec-portal`)
178
+
179
+ ### Portal package contract
180
+
181
+ When using `--portal`, the portal package is expected to provide a single runnable `bin` entry which supports:
182
+
183
+ - `--port <port>`
184
+ - `--mode dev|start`
185
+
186
+ Example:
187
+
188
+ ```bash
189
+ unispec-portal --mode dev --port 3000
190
+ unispec-portal --mode start --port 3000
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Workspace layer
196
+
197
+ Workspace data is stored in `.unispec/workspace.json` next to your config file. This file is not part of UniSpec.
198
+
199
+ ### Workspace endpoints
200
+
201
+ - `GET /workspace.json` — info for clients (mode, configPath, storage paths, and API URLs)
202
+ - `GET /workspace/state` — workspace state JSON
203
+ - `PUT /workspace/state` — replace workspace state JSON (atomic write)
204
+
205
+ ### Endpoint IDs
206
+
207
+ Workspace entities reference endpoints using:
208
+
209
+ ```
210
+ {serviceName}:{METHOD}:{path}
211
+ ```
212
+
213
+ Example:
214
+
215
+ ```
216
+ users:GET:/users
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Workspace tests
222
+
223
+ Tests are stored in workspace state as YAML or JSON strings and can be run through:
224
+
225
+ - `POST /tests/run` with `{ "suiteId": "..." }` or `{ "testIds": ["..."] }`
226
+
227
+ ### YAML/JSON test format (single request)
228
+
229
+ ```yaml
230
+ request:
231
+ method: GET
232
+ url: "http://127.0.0.1:4141/health"
233
+ expect:
234
+ status: 200
235
+ bodyContains: "OK"
236
+ ```
237
+
238
+ ### Multi-step tests (flow)
239
+
240
+ ```yaml
241
+ steps:
242
+ - name: health
243
+ request:
244
+ method: GET
245
+ url: "http://127.0.0.1:4141/health"
246
+ expect:
247
+ status: 200
248
+ bodyContains: "OK"
249
+ extract:
250
+ st:
251
+ from: status
252
+
253
+ - name: unispec
254
+ request:
255
+ method: GET
256
+ url: "http://127.0.0.1:4141/unispec.json"
257
+ expect:
258
+ status: 200
259
+ jsonContains:
260
+ version: 1
261
+ ```
262
+
263
+ ### Variables and interpolation
264
+
265
+ You can extract variables and use them with `{{var}}` in URL/headers/body:
266
+
267
+ ```yaml
268
+ steps:
269
+ - request:
270
+ method: GET
271
+ url: "https://example.com/api/session"
272
+ extract:
273
+ token:
274
+ from: json
275
+ path: "$.token"
276
+ - request:
277
+ method: GET
278
+ url: "https://example.com/api/me"
279
+ headers:
280
+ authorization: "Bearer {{token}}"
281
+ expect:
282
+ status: 200
283
+ ```
284
+
172
285
  ---
173
286
 
174
287
  ## Development
package/dist/cli.js CHANGED
@@ -1,12 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
- import { registerConvertCommand, registerDevCommand, registerDiffCommand, registerNormalizeCommand, registerStartCommand, registerValidateCommand, } from "./commands/index.js";
3
+ import { registerConvertCommand, registerDiffCommand, registerNormalizeCommand, registerPortalCommand, registerValidateCommand, } from "./commands/index.js";
4
4
  function errorToJson(err, includeStack) {
5
5
  const message = err instanceof Error ? err.message : String(err);
6
6
  const code = err instanceof Error && "code" in err ? String(err.code) : undefined;
7
7
  const stack = includeStack && err instanceof Error ? err.stack : undefined;
8
8
  return { error: { message, ...(code ? { code } : {}), ...(stack ? { stack } : {}) } };
9
9
  }
10
+ function normalizeGlobalOptionsArgv(argv) {
11
+ if (argv.length <= 2)
12
+ return argv;
13
+ const head = argv.slice(0, 2);
14
+ const rest = argv.slice(2);
15
+ const globalNoValue = new Set(["--verbose", "--json-errors", "--quiet", "-q"]);
16
+ const extracted = [];
17
+ const remaining = [];
18
+ for (let i = 0; i < rest.length; i++) {
19
+ const a = rest[i];
20
+ if (a === "--") {
21
+ remaining.push(...rest.slice(i));
22
+ break;
23
+ }
24
+ if (globalNoValue.has(a)) {
25
+ extracted.push(a);
26
+ continue;
27
+ }
28
+ remaining.push(a);
29
+ }
30
+ return [...head, ...extracted, ...remaining];
31
+ }
10
32
  const program = new Command();
11
33
  program
12
34
  .name("unispec")
@@ -19,9 +41,8 @@ registerValidateCommand(program);
19
41
  registerNormalizeCommand(program);
20
42
  registerDiffCommand(program);
21
43
  registerConvertCommand(program);
22
- registerDevCommand(program);
23
- registerStartCommand(program);
24
- program.parseAsync(process.argv).catch((err) => {
44
+ registerPortalCommand(program);
45
+ program.parseAsync(normalizeGlobalOptionsArgv(process.argv)).catch((err) => {
25
46
  const opts = program.opts();
26
47
  const verbose = opts.verbose ?? false;
27
48
  const jsonErrors = opts["jsonErrors"] ?? opts["json-errors"] ?? false;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAE7B,SAAS,WAAW,CAAC,GAAY,EAAE,YAAqB;IACtD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAE,GAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3F,MAAM,KAAK,GAAG,YAAY,IAAI,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,OAAO,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;AACxF,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,oCAAoC,CAAC;KACjD,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,aAAa,EAAE,+BAA+B,CAAC;KACtD,MAAM,CAAC,WAAW,EAAE,2CAA2C,CAAC;KAChE,MAAM,CAAC,eAAe,EAAE,iCAAiC,CAAC,CAAC;AAE9D,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAClC,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAE9B,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAiD,CAAC;IAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;IACtC,MAAM,UAAU,GAAI,IAAY,CAAC,YAAY,CAAC,IAAK,IAAY,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC;IAExF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAClF,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,5 @@ export * from "./validate.js";
2
2
  export * from "./normalize.js";
3
3
  export * from "./diff.js";
4
4
  export * from "./convert.js";
5
- export * from "./dev.js";
6
- export * from "./start.js";
5
+ export * from "./portal.js";
7
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
@@ -2,6 +2,5 @@ export * from "./validate.js";
2
2
  export * from "./normalize.js";
3
3
  export * from "./diff.js";
4
4
  export * from "./convert.js";
5
- export * from "./dev.js";
6
- export * from "./start.js";
5
+ export * from "./portal.js";
7
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerPortalCommand(program: Command): void;
3
+ //# sourceMappingURL=portal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../src/commands/portal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoCzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoE5D"}
@@ -0,0 +1,90 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join } from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
+ async function resolvePortalBin(portalPackage) {
6
+ const require = createRequire(import.meta.url);
7
+ const pkgJsonPath = require.resolve(`${portalPackage}/package.json`);
8
+ const pkgDir = dirname(pkgJsonPath);
9
+ const pkgRaw = await readFile(pkgJsonPath, "utf8");
10
+ const pkg = JSON.parse(pkgRaw);
11
+ if (!pkg.bin) {
12
+ return null;
13
+ }
14
+ if (typeof pkg.bin === "string") {
15
+ return { binPath: join(pkgDir, pkg.bin), binName: portalPackage };
16
+ }
17
+ const entries = Object.entries(pkg.bin);
18
+ if (entries.length === 0) {
19
+ return null;
20
+ }
21
+ const preferred = entries.find(([name]) => name === "unispec-portal") ?? entries[0];
22
+ return { binPath: join(pkgDir, preferred[1]), binName: preferred[0] };
23
+ }
24
+ function pickArg(options, camel, kebab) {
25
+ const v = options[camel] ?? options[kebab];
26
+ if (v === undefined || v === null)
27
+ return undefined;
28
+ return String(v);
29
+ }
30
+ export function registerPortalCommand(program) {
31
+ const cmd = program
32
+ .command("portal")
33
+ .description("Launch UniSpec Portal (Next.js orchestrator)")
34
+ .option("--portal-package <name>", "Portal package name", "@unispechq/unispec-portal");
35
+ const addSharedOptions = (c) => c
36
+ .requiredOption("--config <path>", "Path to unispec config file")
37
+ .option("--host <host>", "Bind host")
38
+ .option("--port <port>", "Bind port")
39
+ .option("--storage <type>", "Storage backend (json|sqlite)")
40
+ .option("--data-dir <path>", "Data directory (default: <configDir>/.unispec)")
41
+ .option("--open", "Open browser")
42
+ .option("--verbose", "Verbose portal output")
43
+ .option("--json-errors", "Output portal errors as JSON");
44
+ for (const mode of ["dev", "start"]) {
45
+ addSharedOptions(cmd
46
+ .command(mode)
47
+ .description(`Run portal in ${mode} mode`)
48
+ .action(async function (options) {
49
+ const parentOpts = this.parent?.opts?.();
50
+ const portalPackage = String(parentOpts?.portalPackage ?? "@unispechq/unispec-portal");
51
+ let resolved = null;
52
+ try {
53
+ resolved = await resolvePortalBin(portalPackage);
54
+ }
55
+ catch (err) {
56
+ const message = err instanceof Error ? err.message : String(err);
57
+ throw new Error(`Failed to resolve portal package '${portalPackage}': ${message}`);
58
+ }
59
+ if (!resolved) {
60
+ throw new Error(`Portal package '${portalPackage}' does not expose a runnable bin. Expected a package.json "bin" entry.`);
61
+ }
62
+ const args = [resolved.binPath, mode, "--config", String(options.config)];
63
+ const host = pickArg(options, "host", "host");
64
+ if (host)
65
+ args.push("--host", host);
66
+ const port = pickArg(options, "port", "port");
67
+ if (port)
68
+ args.push("--port", port);
69
+ const storage = pickArg(options, "storage", "storage");
70
+ if (storage)
71
+ args.push("--storage", storage);
72
+ const dataDir = pickArg(options, "dataDir", "data-dir");
73
+ if (dataDir)
74
+ args.push("--data-dir", dataDir);
75
+ if (options.open)
76
+ args.push("--open");
77
+ if (options.verbose)
78
+ args.push("--verbose");
79
+ if (options["jsonErrors"] ?? options["json-errors"])
80
+ args.push("--json-errors");
81
+ const child = spawn(process.execPath, args, {
82
+ stdio: "inherit",
83
+ env: { ...process.env },
84
+ });
85
+ const code = await new Promise((resolve) => child.once("exit", (c) => resolve(c ?? 0)));
86
+ process.exitCode = code;
87
+ }));
88
+ }
89
+ }
90
+ //# sourceMappingURL=portal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.js","sourceRoot":"","sources":["../../src/commands/portal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unispechq/unispec-platform",
3
- "version": "0.1.5",
3
+ "version": "0.2.1",
4
4
  "description": "CLI and local orchestrator for UniSpec (validate/normalize/diff/convert/dev).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,3 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerDevCommand(program: Command): void;
3
- //# sourceMappingURL=dev.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiFzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4KzD"}
@@ -1,207 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { watch } from "node:fs";
3
- import { dirname, isAbsolute, join, resolve } from "node:path";
4
- import { createRequire } from "node:module";
5
- import { readFile } from "node:fs/promises";
6
- import { loadConfig } from "../config/loadConfig.js";
7
- import { findConfigPath } from "../config/loadConfig.js";
8
- import { aggregateFromConfig } from "../dev/aggregate.js";
9
- import { startDevServer } from "../dev/server.js";
10
- function resolveConfigSpecPaths(config, baseDir) {
11
- if (!baseDir)
12
- return config;
13
- const services = Array.isArray(config.services) ? config.services : [];
14
- return {
15
- ...config,
16
- services: services.map((s) => {
17
- const spec = String(s?.spec ?? "");
18
- const resolvedSpec = spec && !isAbsolute(spec) ? resolve(baseDir, spec) : spec;
19
- return {
20
- ...s,
21
- spec: resolvedSpec,
22
- };
23
- }),
24
- };
25
- }
26
- async function resolvePortalBin(portalPackage) {
27
- const require = createRequire(import.meta.url);
28
- const pkgJsonPath = require.resolve(`${portalPackage}/package.json`);
29
- const pkgDir = dirname(pkgJsonPath);
30
- const pkgRaw = await readFile(pkgJsonPath, "utf8");
31
- const pkg = JSON.parse(pkgRaw);
32
- if (!pkg.bin) {
33
- return null;
34
- }
35
- if (typeof pkg.bin === "string") {
36
- return { binPath: join(pkgDir, pkg.bin), binName: portalPackage };
37
- }
38
- const entries = Object.entries(pkg.bin);
39
- if (entries.length === 0) {
40
- return null;
41
- }
42
- const preferred = entries.find(([name]) => name.includes("dev")) ?? entries[0];
43
- return { binPath: join(pkgDir, preferred[1]), binName: preferred[0] };
44
- }
45
- async function spawnPortalDevServer(options) {
46
- const resolved = await resolvePortalBin(options.portalPackage);
47
- if (!resolved) {
48
- throw new Error(`Portal package '${options.portalPackage}' does not expose a runnable bin. Expected a package.json "bin" entry.`);
49
- }
50
- const args = [resolved.binPath, "--port", String(options.port)];
51
- const child = spawn(process.execPath, args, {
52
- stdio: options.verbose ? "inherit" : "ignore",
53
- env: { ...process.env, PORT: String(options.port) },
54
- });
55
- child.once("exit", (code) => {
56
- if (options.verbose) {
57
- process.stderr.write(`Portal dev server exited with code ${code ?? "null"}\n`);
58
- }
59
- });
60
- return child;
61
- }
62
- export function registerDevCommand(program) {
63
- program
64
- .command("dev")
65
- .description("Local orchestrator mode: aggregate specs and serve a local API")
66
- .option("--config <path>", "Path to unispec config file")
67
- .option("--host <host>", "Host to bind", "127.0.0.1")
68
- .option("--port <port>", "Port to bind", "4141")
69
- .option("--watch", "Watch config/spec files and refresh aggregated snapshot")
70
- .option("--ui", "Serve embedded UI at /", true)
71
- .option("--no-ui", "Disable embedded UI")
72
- .option("--ui-dev <url>", "Proxy UI requests to an external dev server (e.g. Next.js)")
73
- .option("--portal", "Spawn portal server and proxy UI to it")
74
- .option("--portal-port <port>", "Port for portal server", "3000")
75
- .option("--portal-dev", "Spawn portal dev server from an installed dependency and proxy UI to it")
76
- .option("--portal-dev-port <port>", "Port for portal dev server")
77
- .option("--portal-package <name>", "Portal package name", "@unispechq/unispec-portal")
78
- .action(async function (options) {
79
- const parentOpts = this.parent?.opts?.();
80
- const verbose = parentOpts?.verbose ?? false;
81
- const host = String(options.host ?? "127.0.0.1");
82
- const port = Number(options.port ?? 4141);
83
- let snapshot;
84
- let snapshotError;
85
- const configPath = await findConfigPath({ explicitPath: options.config });
86
- const configDir = configPath ? dirname(configPath) : undefined;
87
- const loadAndAggregate = async () => {
88
- const config = await loadConfig({ explicitPath: options.config });
89
- const cfg = resolveConfigSpecPaths(config ?? { version: 1, services: [] }, configDir);
90
- return aggregateFromConfig(cfg);
91
- };
92
- const refreshSnapshot = async () => {
93
- try {
94
- snapshot = await loadAndAggregate();
95
- snapshotError = undefined;
96
- if (verbose) {
97
- process.stderr.write("Refreshed aggregated snapshot\n");
98
- }
99
- }
100
- catch (err) {
101
- const message = err instanceof Error ? err.message : String(err);
102
- snapshotError = { message };
103
- if (verbose) {
104
- process.stderr.write(`Failed to refresh snapshot: ${message}\n`);
105
- }
106
- }
107
- };
108
- await refreshSnapshot();
109
- const getConfig = async () => {
110
- const config = await loadConfig({ explicitPath: options.config });
111
- return resolveConfigSpecPaths(config ?? { version: 1, services: [] }, configDir);
112
- };
113
- const getAggregated = async () => {
114
- if (snapshot) {
115
- return snapshot;
116
- }
117
- if (snapshotError) {
118
- return {
119
- version: 1,
120
- generatedAt: new Date().toISOString(),
121
- services: [
122
- {
123
- name: "orchestrator",
124
- specPath: "config",
125
- valid: false,
126
- errors: [snapshotError],
127
- },
128
- ],
129
- };
130
- }
131
- return loadAndAggregate();
132
- };
133
- let portalChild;
134
- let uiDevUrl = options.uiDev;
135
- const portalEnabled = options.portalDev || options.portal;
136
- const portalPortRaw = options.portalDevPort ?? options.portalPort;
137
- if (portalEnabled) {
138
- if (uiDevUrl) {
139
- throw new Error("--portal cannot be used together with --ui-dev");
140
- }
141
- const portalPort = Number(portalPortRaw ?? 3000);
142
- const portalPackage = String(options.portalPackage ?? "@unispechq/unispec-portal");
143
- uiDevUrl = `http://127.0.0.1:${portalPort}`;
144
- try {
145
- portalChild = await spawnPortalDevServer({ portalPackage, port: portalPort, verbose });
146
- const stop = () => {
147
- try {
148
- portalChild?.kill("SIGINT");
149
- }
150
- catch {
151
- // ignore
152
- }
153
- };
154
- process.once("SIGINT", stop);
155
- process.once("SIGTERM", stop);
156
- process.once("exit", stop);
157
- if (verbose) {
158
- process.stderr.write(`Spawned portal server (${portalPackage}) at ${uiDevUrl}\n`);
159
- }
160
- }
161
- catch (err) {
162
- const message = err instanceof Error ? err.message : String(err);
163
- throw new Error(`Failed to start portal dev server: ${message}`);
164
- }
165
- }
166
- if (options.watch) {
167
- const cfg = await getConfig();
168
- const serviceSpecs = Array.isArray(cfg.services) ? cfg.services.map((s) => String(s.spec ?? "")) : [];
169
- const watchTargets = [
170
- ...(configPath ? [configPath] : []),
171
- ...serviceSpecs.filter(Boolean).map((p) => (isAbsolute(p) ? p : resolve(process.cwd(), p))),
172
- ];
173
- let timer;
174
- const scheduleRefresh = () => {
175
- if (timer)
176
- clearTimeout(timer);
177
- timer = setTimeout(() => {
178
- void refreshSnapshot();
179
- }, 200);
180
- };
181
- for (const target of watchTargets) {
182
- try {
183
- watch(target, { persistent: true }, () => scheduleRefresh());
184
- if (verbose) {
185
- process.stderr.write(`Watching ${target}\n`);
186
- }
187
- }
188
- catch (err) {
189
- if (verbose) {
190
- const message = err instanceof Error ? err.message : String(err);
191
- process.stderr.write(`Failed to watch ${target}: ${message}\n`);
192
- }
193
- }
194
- }
195
- }
196
- await startDevServer({
197
- host,
198
- port,
199
- getConfig,
200
- getAggregated,
201
- verbose,
202
- ui: options.ui,
203
- uiDevUrl,
204
- });
205
- });
206
- }
207
- //# sourceMappingURL=dev.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerStartCommand(program: Command): void;
3
- //# sourceMappingURL=start.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0EzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoF3D"}
@@ -1,121 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { createRequire } from "node:module";
3
- import { readFile } from "node:fs/promises";
4
- import { dirname, isAbsolute, join, resolve } from "node:path";
5
- import { loadConfig, findConfigPath } from "../config/loadConfig.js";
6
- import { startDevServer } from "../dev/server.js";
7
- function resolveConfigSpecPaths(config, baseDir) {
8
- if (!baseDir)
9
- return config;
10
- const services = Array.isArray(config.services) ? config.services : [];
11
- return {
12
- ...config,
13
- services: services.map((s) => {
14
- const spec = String(s?.spec ?? "");
15
- const resolvedSpec = spec && !isAbsolute(spec) ? resolve(baseDir, spec) : spec;
16
- return {
17
- ...s,
18
- spec: resolvedSpec,
19
- };
20
- }),
21
- };
22
- }
23
- async function resolvePortalBin(portalPackage, kind) {
24
- const require = createRequire(import.meta.url);
25
- const pkgJsonPath = require.resolve(`${portalPackage}/package.json`);
26
- const pkgDir = dirname(pkgJsonPath);
27
- const pkgRaw = await readFile(pkgJsonPath, "utf8");
28
- const pkg = JSON.parse(pkgRaw);
29
- if (!pkg.bin)
30
- return null;
31
- if (typeof pkg.bin === "string") {
32
- return join(pkgDir, pkg.bin);
33
- }
34
- const entries = Object.entries(pkg.bin);
35
- if (entries.length === 0)
36
- return null;
37
- const preferred = entries.find(([name]) => name.includes(kind)) ?? entries[0];
38
- return join(pkgDir, preferred[1]);
39
- }
40
- async function spawnPortalServer(options) {
41
- const binPath = await resolvePortalBin(options.portalPackage, options.mode);
42
- if (!binPath) {
43
- throw new Error(`Portal package '${options.portalPackage}' does not expose a runnable bin. Expected a package.json "bin" entry.`);
44
- }
45
- const args = [binPath, "--port", String(options.port), "--mode", options.mode];
46
- const child = spawn(process.execPath, args, {
47
- stdio: options.verbose ? "inherit" : "ignore",
48
- env: { ...process.env, PORT: String(options.port) },
49
- });
50
- child.once("exit", (code) => {
51
- if (options.verbose) {
52
- process.stderr.write(`Portal server exited with code ${code ?? "null"}\n`);
53
- }
54
- });
55
- return child;
56
- }
57
- export function registerStartCommand(program) {
58
- program
59
- .command("start")
60
- .description("Start a local server (UI + /unispec.json)")
61
- .option("--config <path>", "Path to unispec config file")
62
- .option("--host <host>", "Host to bind", "127.0.0.1")
63
- .option("--port <port>", "Port to bind", "4141")
64
- .option("--ui", "Serve embedded UI at /", true)
65
- .option("--no-ui", "Disable embedded UI")
66
- .option("--ui-url <url>", "Proxy UI requests to an external UI server")
67
- .option("--portal", "Spawn portal server (SSR) from an installed dependency and proxy UI to it")
68
- .option("--portal-port <port>", "Port for portal server", "3000")
69
- .option("--portal-package <name>", "Portal package name", "@unispechq/unispec-portal")
70
- .action(async function (options) {
71
- const parentOpts = this.parent?.opts?.();
72
- const verbose = parentOpts?.verbose ?? false;
73
- const host = String(options.host ?? "127.0.0.1");
74
- const port = Number(options.port ?? 4141);
75
- const configPath = await findConfigPath({ explicitPath: options.config });
76
- const configDir = configPath ? dirname(configPath) : undefined;
77
- const getConfig = async () => {
78
- const config = await loadConfig({ explicitPath: options.config });
79
- return resolveConfigSpecPaths(config ?? { version: 1, services: [] }, configDir);
80
- };
81
- let portalChild;
82
- let uiDevUrl = options.uiUrl;
83
- if (options.portal) {
84
- if (uiDevUrl) {
85
- throw new Error("--portal cannot be used together with --ui-url");
86
- }
87
- const portalPort = Number(options.portalPort ?? 3000);
88
- const portalPackage = String(options.portalPackage ?? "@unispechq/unispec-portal");
89
- uiDevUrl = `http://127.0.0.1:${portalPort}`;
90
- portalChild = await spawnPortalServer({
91
- portalPackage,
92
- port: portalPort,
93
- mode: "start",
94
- verbose,
95
- });
96
- const stop = () => {
97
- try {
98
- portalChild?.kill("SIGINT");
99
- }
100
- catch {
101
- // ignore
102
- }
103
- };
104
- process.once("SIGINT", stop);
105
- process.once("SIGTERM", stop);
106
- process.once("exit", stop);
107
- if (verbose) {
108
- process.stderr.write(`Spawned portal server (${portalPackage}) at ${uiDevUrl}\n`);
109
- }
110
- }
111
- await startDevServer({
112
- host,
113
- port,
114
- getConfig,
115
- verbose,
116
- ui: options.ui,
117
- uiDevUrl: uiDevUrl,
118
- });
119
- });
120
- }
121
- //# sourceMappingURL=start.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
@@ -1,15 +0,0 @@
1
- import type { UniSpecPlatformConfig } from "../config/loadConfig.js";
2
- export interface AggregatedService {
3
- name: string;
4
- specPath: string;
5
- valid: boolean;
6
- errors?: unknown;
7
- spec?: unknown;
8
- }
9
- export interface AggregateResult {
10
- version: number;
11
- generatedAt: string;
12
- services: AggregatedService[];
13
- }
14
- export declare function aggregateFromConfig(config: UniSpecPlatformConfig): Promise<AggregateResult>;
15
- //# sourceMappingURL=aggregate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../src/dev/aggregate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGrE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0DjG"}
@@ -1,56 +0,0 @@
1
- import { loadUniSpec, normalizeUniSpec, validateUniSpec } from "@unispechq/unispec-core";
2
- import { readSpecFile } from "../io/readSpecFile.js";
3
- export async function aggregateFromConfig(config) {
4
- const version = typeof config.version === "number" ? config.version : 1;
5
- const services = Array.isArray(config.services) ? config.services : [];
6
- const results = [];
7
- for (const service of services) {
8
- const name = String(service?.name ?? "");
9
- const specPath = String(service?.spec ?? "");
10
- if (!name || !specPath) {
11
- results.push({
12
- name: name || "unknown",
13
- specPath,
14
- valid: false,
15
- errors: [{ message: "Invalid config entry: service.name and service.spec are required" }],
16
- });
17
- continue;
18
- }
19
- try {
20
- const raw = await readSpecFile(specPath);
21
- const doc = await loadUniSpec(raw);
22
- const validation = await validateUniSpec(doc);
23
- if (!validation.valid) {
24
- results.push({
25
- name,
26
- specPath,
27
- valid: false,
28
- errors: validation.errors,
29
- });
30
- continue;
31
- }
32
- const normalized = normalizeUniSpec(doc);
33
- results.push({
34
- name,
35
- specPath,
36
- valid: true,
37
- spec: normalized,
38
- });
39
- }
40
- catch (err) {
41
- const message = err instanceof Error ? err.message : String(err);
42
- results.push({
43
- name,
44
- specPath,
45
- valid: false,
46
- errors: [{ message }],
47
- });
48
- }
49
- }
50
- return {
51
- version,
52
- generatedAt: new Date().toISOString(),
53
- services: results,
54
- };
55
- }
56
- //# sourceMappingURL=aggregate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/dev/aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEzF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAgBrD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAA6B;IACrE,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvE,MAAM,OAAO,GAAwB,EAAE,CAAC;IAExC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAE7C,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,IAAI,IAAI,SAAS;gBACvB,QAAQ;gBACR,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,kEAAkE,EAAE,CAAC;aAC1F,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;YAE9C,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI;oBACJ,QAAQ;oBACR,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI;gBACJ,QAAQ;gBACR,KAAK,EAAE,IAAI;gBACX,IAAI,EAAE,UAAU;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI;gBACJ,QAAQ;gBACR,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,QAAQ,EAAE,OAAO;KAClB,CAAC;AACJ,CAAC"}
@@ -1,3 +0,0 @@
1
- export * from "./aggregate.js";
2
- export * from "./server.js";
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
package/dist/dev/index.js DELETED
@@ -1,3 +0,0 @@
1
- export * from "./aggregate.js";
2
- export * from "./server.js";
3
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
@@ -1,14 +0,0 @@
1
- import http from "node:http";
2
- import type { AggregateResult } from "./aggregate.js";
3
- import type { UniSpecPlatformConfig } from "../config/loadConfig.js";
4
- export interface DevServerOptions {
5
- host: string;
6
- port: number;
7
- getConfig: () => Promise<UniSpecPlatformConfig>;
8
- getAggregated?: () => Promise<AggregateResult>;
9
- verbose?: boolean;
10
- ui?: boolean;
11
- uiDevUrl?: string;
12
- }
13
- export declare function startDevServer(options: DevServerOptions): Promise<http.Server>;
14
- //# sourceMappingURL=server.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dev/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGrE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAgID,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAoEpF"}
@@ -1,175 +0,0 @@
1
- import http from "node:http";
2
- import net from "node:net";
3
- import { aggregateFromConfig } from "./aggregate.js";
4
- import { getEmbeddedIndexHtml, getEmbeddedUiJs } from "./ui.js";
5
- function writeJson(res, status, body) {
6
- const json = JSON.stringify(body, null, 2);
7
- res.statusCode = status;
8
- res.setHeader("content-type", "application/json; charset=utf-8");
9
- res.end(`${json}\n`);
10
- }
11
- function writeHtml(res, status, html) {
12
- res.statusCode = status;
13
- res.setHeader("content-type", "text/html; charset=utf-8");
14
- res.end(html);
15
- }
16
- function writeJs(res, status, js) {
17
- res.statusCode = status;
18
- res.setHeader("content-type", "application/javascript; charset=utf-8");
19
- res.end(js);
20
- }
21
- function shouldProxyUi(options) {
22
- return Boolean(options.uiDevUrl);
23
- }
24
- function getForwardedPort(hostHeader) {
25
- if (!hostHeader)
26
- return undefined;
27
- const idx = hostHeader.lastIndexOf(":");
28
- if (idx === -1)
29
- return undefined;
30
- const port = hostHeader.slice(idx + 1);
31
- return port ? port : undefined;
32
- }
33
- function buildProxyHeaders(target, req) {
34
- const headers = { ...req.headers };
35
- const originalHost = req.headers.host;
36
- const forwardedFor = req.socket.remoteAddress;
37
- const forwardedPort = getForwardedPort(originalHost);
38
- delete headers.host;
39
- headers["x-forwarded-host"] = originalHost;
40
- headers["x-forwarded-proto"] = "http";
41
- if (forwardedPort) {
42
- headers["x-forwarded-port"] = forwardedPort;
43
- }
44
- if (forwardedFor) {
45
- headers["x-forwarded-for"] = forwardedFor;
46
- }
47
- headers.host = target.host;
48
- return headers;
49
- }
50
- function proxyHttpRequest(target, req, res) {
51
- const headers = buildProxyHeaders(target, req);
52
- const proxyReq = http.request({
53
- protocol: target.protocol,
54
- hostname: target.hostname,
55
- port: target.port || (target.protocol === "https:" ? 443 : 80),
56
- method: req.method,
57
- path: req.url,
58
- headers,
59
- }, (proxyRes) => {
60
- res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
61
- proxyRes.pipe(res);
62
- });
63
- proxyReq.on("error", (err) => {
64
- res.statusCode = 502;
65
- res.setHeader("content-type", "application/json; charset=utf-8");
66
- res.end(JSON.stringify({ error: { message: `UI proxy error: ${err.message}` } }, null, 2) + "\n");
67
- });
68
- req.pipe(proxyReq);
69
- }
70
- function proxyWebSocketUpgrade(target, req, socket, head) {
71
- const port = Number(target.port || (target.protocol === "https:" ? 443 : 80));
72
- const upstream = net.connect(port, target.hostname, () => {
73
- const lines = [];
74
- const path = req.url ?? "/";
75
- lines.push(`${req.method ?? "GET"} ${path} HTTP/1.1`);
76
- const originalHost = req.headers.host;
77
- const forwardedFor = req.socket.remoteAddress;
78
- const forwardedPort = getForwardedPort(originalHost);
79
- const targetHost = target.host || `${target.hostname}:${port}`;
80
- const headers = { ...req.headers };
81
- headers.host = targetHost;
82
- headers["x-forwarded-host"] = originalHost;
83
- headers["x-forwarded-proto"] = "http";
84
- if (forwardedPort)
85
- headers["x-forwarded-port"] = forwardedPort;
86
- if (forwardedFor)
87
- headers["x-forwarded-for"] = forwardedFor;
88
- for (const [k, v] of Object.entries(headers)) {
89
- if (v === undefined)
90
- continue;
91
- if (Array.isArray(v)) {
92
- for (const vv of v)
93
- lines.push(`${k}: ${vv}`);
94
- }
95
- else {
96
- lines.push(`${k}: ${v}`);
97
- }
98
- }
99
- lines.push("\r\n");
100
- upstream.write(lines.join("\r\n"));
101
- if (head && head.length) {
102
- upstream.write(head);
103
- }
104
- socket.pipe(upstream);
105
- upstream.pipe(socket);
106
- });
107
- upstream.on("error", () => {
108
- try {
109
- socket.end();
110
- }
111
- catch {
112
- // ignore
113
- }
114
- });
115
- }
116
- export async function startDevServer(options) {
117
- const uiEnabled = options.ui ?? true;
118
- const uiDevUrl = options.uiDevUrl ? new URL(options.uiDevUrl) : undefined;
119
- const server = http.createServer(async (req, res) => {
120
- const method = req.method ?? "GET";
121
- const url = req.url ?? "/";
122
- if (method === "GET" && url === "/health") {
123
- res.statusCode = 200;
124
- res.setHeader("content-type", "text/plain; charset=utf-8");
125
- res.end("OK\n");
126
- return;
127
- }
128
- if (method === "GET" && url === "/unispec.json") {
129
- try {
130
- const aggregated = options.getAggregated
131
- ? await options.getAggregated()
132
- : await aggregateFromConfig(await options.getConfig());
133
- const hasErrors = aggregated.services.some((s) => !s.valid);
134
- writeJson(res, hasErrors ? 422 : 200, aggregated);
135
- }
136
- catch (err) {
137
- const message = err instanceof Error ? err.message : String(err);
138
- writeJson(res, 500, { error: { message } });
139
- }
140
- return;
141
- }
142
- if (uiEnabled) {
143
- if (uiDevUrl && shouldProxyUi(options)) {
144
- proxyHttpRequest(uiDevUrl, req, res);
145
- return;
146
- }
147
- if (method === "GET" && (url === "/" || url === "/index.html")) {
148
- writeHtml(res, 200, getEmbeddedIndexHtml());
149
- return;
150
- }
151
- if (method === "GET" && url === "/ui.js") {
152
- writeJs(res, 200, getEmbeddedUiJs());
153
- return;
154
- }
155
- }
156
- writeJson(res, 404, { error: { message: "Not found" } });
157
- });
158
- server.on("upgrade", (req, socket, head) => {
159
- const uiDev = uiDevUrl;
160
- if (!uiDev || !uiEnabled) {
161
- socket.destroy();
162
- return;
163
- }
164
- proxyWebSocketUpgrade(uiDev, req, socket, head);
165
- });
166
- await new Promise((resolve, reject) => {
167
- server.once("error", reject);
168
- server.listen(options.port, options.host, () => resolve());
169
- });
170
- if (options.verbose) {
171
- process.stderr.write(`Dev server listening on http://${options.host}:${options.port}\n`);
172
- }
173
- return server;
174
- }
175
- //# sourceMappingURL=server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dev/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAYhE,SAAS,SAAS,CAAC,GAAwB,EAAE,MAAc,EAAE,IAAa;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3C,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC;IACxB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;IACjE,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,SAAS,CAAC,GAAwB,EAAE,MAAc,EAAE,IAAY;IACvE,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC;IACxB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC;IAC1D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,OAAO,CAAC,GAAwB,EAAE,MAAc,EAAE,EAAU;IACnE,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC;IACxB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,uCAAuC,CAAC,CAAC;IACvE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,OAAyB;IAC9C,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,gBAAgB,CAAC,UAA8B;IACtD,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACjC,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAW,EAAE,GAAyB;IAC/D,MAAM,OAAO,GAA6B,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;IAE7D,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;IACtC,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;IAC9C,MAAM,aAAa,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAErD,OAAQ,OAAe,CAAC,IAAI,CAAC;IAE7B,OAAO,CAAC,kBAAkB,CAAC,GAAG,YAAY,CAAC;IAC3C,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC;IACtC,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CAAC,kBAAkB,CAAC,GAAG,aAAa,CAAC;IAC9C,CAAC;IACD,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,CAAC,iBAAiB,CAAC,GAAG,YAAY,CAAC;IAC5C,CAAC;IAED,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAW,EAAE,GAAyB,EAAE,GAAwB;IACxF,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAC3B;QACE,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,GAAG,CAAC,GAAG;QACb,OAAO;KACR,EACD,CAAC,QAAQ,EAAE,EAAE;QACX,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,QAAQ,CAAC,OAAc,CAAC,CAAC;QACnE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC,CACF,CAAC;IAEF,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QAC3B,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAC;QACjE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,mBAAmB,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAW,EAAE,GAAyB,EAAE,MAAc,EAAE,IAAY;IACjG,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9E,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE;QACvD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,IAAI,IAAI,WAAW,CAAC,CAAC;QAEtD,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;QACtC,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;QAC9C,MAAM,aAAa,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;QACrD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,IAAI,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;QAE/D,MAAM,OAAO,GAAG,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,GAAG,UAAU,CAAC;QACzB,OAAe,CAAC,kBAAkB,CAAC,GAAG,YAAY,CAAC;QACnD,OAAe,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC;QAC/C,IAAI,aAAa;YAAG,OAAe,CAAC,kBAAkB,CAAC,GAAG,aAAa,CAAC;QACxE,IAAI,YAAY;YAAG,OAAe,CAAC,iBAAiB,CAAC,GAAG,YAAY,CAAC;QAErE,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,KAAK,SAAS;gBAAE,SAAS;YAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrB,KAAK,MAAM,EAAE,IAAI,CAAC;oBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAChD,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACnC,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACxB,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtB,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACxB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAyB;IAC5D,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC;IACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE1E,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAClD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC;QAE3B,IAAI,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC1C,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;YACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,2BAA2B,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAChB,OAAO;QACT,CAAC;QAED,IAAI,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,eAAe,EAAE,CAAC;YAChD,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa;oBACtC,CAAC,CAAC,MAAM,OAAO,CAAC,aAAa,EAAE;oBAC/B,CAAC,CAAC,MAAM,mBAAmB,CAAC,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;gBACzD,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC5D,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;YAC9C,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,QAAQ,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvC,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;gBACrC,OAAO;YACT,CAAC;YAED,IAAI,MAAM,KAAK,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,aAAa,CAAC,EAAE,CAAC;gBAC/D,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,oBAAoB,EAAE,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YAED,IAAI,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACzC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC,CAAC;gBACrC,OAAO;YACT,CAAC;QACH,CAAC;QAED,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;QACzC,MAAM,KAAK,GAAG,QAAQ,CAAC;QACvB,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YACzB,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,qBAAqB,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/dist/dev/ui.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export declare function getEmbeddedIndexHtml(): string;
2
- export declare function getEmbeddedUiJs(): string;
3
- //# sourceMappingURL=ui.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/dev/ui.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,IAAI,MAAM,CAqC7C;AAED,wBAAgB,eAAe,IAAI,MAAM,CAqDxC"}
package/dist/dev/ui.js DELETED
@@ -1,93 +0,0 @@
1
- export function getEmbeddedIndexHtml() {
2
- return `<!doctype html>
3
- <html lang="en">
4
- <head>
5
- <meta charset="utf-8" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <title>UniSpec Dev</title>
8
- <style>
9
- :root { color-scheme: light dark; }
10
- body { font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 0; }
11
- header { padding: 16px 20px; border-bottom: 1px solid rgba(127,127,127,0.25); }
12
- main { padding: 16px 20px; }
13
- .muted { opacity: 0.75; font-size: 12px; }
14
- .card { border: 1px solid rgba(127,127,127,0.25); border-radius: 10px; padding: 12px; margin: 12px 0; }
15
- .ok { color: #1f8a4c; }
16
- .bad { color: #b42318; }
17
- pre { overflow: auto; max-height: calc(100vh - 380px); }
18
- button { cursor: pointer; padding: 8px 12px; border-radius: 8px; border: 1px solid rgba(127,127,127,0.35); background: transparent; }
19
- </style>
20
- </head>
21
- <body>
22
- <header>
23
- <div><strong>UniSpec Dev</strong></div>
24
- <div class="muted">Data source: <code>/unispec.json</code></div>
25
- </header>
26
- <main>
27
- <button id="refresh">Refresh</button>
28
- <div id="status" class="muted" style="margin-top: 8px;"></div>
29
- <div id="root"></div>
30
- <details style="margin-top: 16px;">
31
- <summary>Raw <code>/unispec.json</code></summary>
32
- <pre id="raw"></pre>
33
- </details>
34
- </main>
35
- <script type="module" src="/ui.js"></script>
36
- </body>
37
- </html>`;
38
- }
39
- export function getEmbeddedUiJs() {
40
- return `async function load() {
41
- const status = document.getElementById('status');
42
- const root = document.getElementById('root');
43
- const raw = document.getElementById('raw');
44
-
45
- status.textContent = 'Loading /unispec.json ...';
46
- root.innerHTML = '';
47
- raw.textContent = '';
48
-
49
- const res = await fetch('/unispec.json', { cache: 'no-store' });
50
- const text = await res.text();
51
- let json;
52
- try { json = JSON.parse(text); } catch { json = undefined; }
53
-
54
- status.textContent = 'HTTP ' + res.status + ' • ' + new Date().toLocaleTimeString();
55
- raw.textContent = text;
56
-
57
- if (!json || !Array.isArray(json.services)) {
58
- const div = document.createElement('div');
59
- div.className = 'card';
60
- div.innerHTML = '<div class="bad"><strong>Invalid response</strong></div>';
61
- root.appendChild(div);
62
- return;
63
- }
64
-
65
- for (const svc of json.services) {
66
- const div = document.createElement('div');
67
- div.className = 'card';
68
- const ok = svc && svc.valid === true;
69
- const title = (svc && svc.name) ? String(svc.name) : '(unnamed)';
70
- const path = (svc && svc.specPath) ? String(svc.specPath) : '';
71
- div.innerHTML =
72
- '<div><strong>' + title + '</strong> ' +
73
- '<span class="' + (ok ? 'ok' : 'bad') + '">' + (ok ? 'valid' : 'invalid') + '</span></div>' +
74
- (path ? '<div class="muted">' + path + '</div>' : '');
75
-
76
- if (!ok && svc && Array.isArray(svc.errors) && svc.errors.length) {
77
- const pre = document.createElement('pre');
78
- pre.textContent = JSON.stringify(svc.errors, null, 2);
79
- div.appendChild(pre);
80
- }
81
-
82
- root.appendChild(div);
83
- }
84
- }
85
-
86
- document.getElementById('refresh')?.addEventListener('click', () => {
87
- void load();
88
- });
89
-
90
- void load();
91
- `;
92
- }
93
- //# sourceMappingURL=ui.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/dev/ui.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAmCD,CAAC;AACT,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmDR,CAAC;AACF,CAAC"}