@unispechq/unispec-platform 0.2.0 → 0.2.2

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.
Files changed (44) hide show
  1. package/README.md +20 -204
  2. package/dist/cli.js +7 -5
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/index.d.ts +1 -2
  5. package/dist/commands/index.d.ts.map +1 -1
  6. package/dist/commands/index.js +1 -2
  7. package/dist/commands/index.js.map +1 -1
  8. package/dist/commands/portal.d.ts +4 -0
  9. package/dist/commands/portal.d.ts.map +1 -0
  10. package/dist/commands/portal.js +94 -0
  11. package/dist/commands/portal.js.map +1 -0
  12. package/package.json +2 -2
  13. package/dist/commands/dev.d.ts +0 -3
  14. package/dist/commands/dev.d.ts.map +0 -1
  15. package/dist/commands/dev.js +0 -211
  16. package/dist/commands/dev.js.map +0 -1
  17. package/dist/commands/start.d.ts +0 -3
  18. package/dist/commands/start.d.ts.map +0 -1
  19. package/dist/commands/start.js +0 -125
  20. package/dist/commands/start.js.map +0 -1
  21. package/dist/dev/aggregate.d.ts +0 -15
  22. package/dist/dev/aggregate.d.ts.map +0 -1
  23. package/dist/dev/aggregate.js +0 -56
  24. package/dist/dev/aggregate.js.map +0 -1
  25. package/dist/dev/index.d.ts +0 -3
  26. package/dist/dev/index.d.ts.map +0 -1
  27. package/dist/dev/index.js +0 -3
  28. package/dist/dev/index.js.map +0 -1
  29. package/dist/dev/server.d.ts +0 -17
  30. package/dist/dev/server.d.ts.map +0 -1
  31. package/dist/dev/server.js +0 -503
  32. package/dist/dev/server.js.map +0 -1
  33. package/dist/dev/ui.d.ts +0 -3
  34. package/dist/dev/ui.d.ts.map +0 -1
  35. package/dist/dev/ui.js +0 -93
  36. package/dist/dev/ui.js.map +0 -1
  37. package/dist/workspace/storage.d.ts +0 -7
  38. package/dist/workspace/storage.d.ts.map +0 -1
  39. package/dist/workspace/storage.js +0 -60
  40. package/dist/workspace/storage.js.map +0 -1
  41. package/dist/workspace/types.d.ts +0 -64
  42. package/dist/workspace/types.d.ts.map +0 -1
  43. package/dist/workspace/types.js +0 -2
  44. package/dist/workspace/types.js.map +0 -1
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # UniSpec Platform
2
2
 
3
- **CLI and local orchestrator for the UniSpec ecosystem**
3
+ **UniSpec CLI utilities and portal launcher**
4
4
 
5
- This repository contains the official UniSpec CLI and the foundation for a local orchestrator.
5
+ This repository contains the UniSpec CLI utilities and a launcher for `@unispechq/unispec-portal`.
6
6
  It builds on top of:
7
7
 
8
8
  - `unispec-spec` — the UniSpec format definition (schemas, examples, reference docs)
@@ -23,8 +23,7 @@ This repository is in early development.
23
23
  Current focus:
24
24
 
25
25
  - Provide a stable CLI surface for `validate`, `normalize`, `diff`, `convert`
26
- - Lay groundwork for `dev` (local orchestrator)
27
- - Prepare integration points for the future `unispec-registry` (publish/read)
26
+ - Provide a stable launcher for `unispec portal <dev|start>`
28
27
 
29
28
  ---
30
29
 
@@ -34,9 +33,9 @@ Current focus:
34
33
  unispec-platform/
35
34
  ├─ src/
36
35
  │ ├─ cli.ts # CLI entrypoint
37
- │ ├─ commands/ # Command registration (validate/normalize/diff/convert/dev)
36
+ │ ├─ commands/ # Command registration (validate/normalize/diff/convert/portal)
38
37
  │ ├─ io/ # File IO + output helpers
39
- │ └─ config/ # Config loading (future dev/orchestrator mode)
38
+ │ └─ config/ # Config loading
40
39
  ├─ package.json
41
40
  ├─ tsconfig.json
42
41
  └─ README.md
@@ -89,218 +88,35 @@ Options:
89
88
  - `--format <format>` — `json|yaml` (for JSON-like targets)
90
89
  - `--output <file>` — write output to file (use `-` for stdout)
91
90
 
92
- ### `unispec dev`
93
- Local orchestrator mode.
91
+ ### `unispec portal <dev|start>`
92
+ Launch UniSpec Portal (Next.js orchestrator).
94
93
 
95
- Serves:
94
+ In the current architecture, `unispec-platform` provides CLI utilities (validate/normalize/diff/convert) and a launcher for `@unispechq/unispec-portal`.
96
95
 
97
- - `GET /` — embedded UI (reads `/unispec.json` in the browser)
98
- - `GET /unispec.json` — aggregated snapshot
99
- - `GET /health` — healthcheck
100
- - `GET /workspace.json` — workspace info (enabled when config path is known)
101
- - `GET /workspace/state` — workspace state (stored in `.unispec/workspace.json`)
102
- - `PUT /workspace/state` — replace workspace state
103
- - `POST /tests/run` — run workspace tests (suite/flow)
104
-
105
- Options:
106
-
107
- - `--config <path>` — path to config file (default: `unispec.config.json|yaml|yml`)
108
- - `--host <host>` — bind host (default: `127.0.0.1`)
109
- - `--port <port>` — bind port (default: `4141`)
110
- - `--watch` — watch config/spec files and refresh aggregated snapshot
111
- - `--ui` / `--no-ui` — enable/disable embedded UI at `/` (default: enabled)
112
- - `--ui-dev <url>` — proxy UI requests to an external dev server (e.g. Next.js) for HMR
113
- - `--portal` — spawn portal server from an installed dependency and proxy UI to it
114
- - `--portal-port <port>` — port for portal server (default: `3000`)
115
- - `--portal-package <name>` — portal package name (default: `@unispechq/unispec-portal`)
116
-
117
- Planned responsibilities:
118
-
119
- - Watch local specs and/or connected services
120
- - Aggregate multiple UniSpec documents (microservices mode)
121
- - Serve a local dashboard and endpoints (e.g. `/unispec.json` for aggregated view)
122
- - Provide a single workflow to validate/normalize/diff before publishing to Registry
123
-
124
- ---
125
-
126
- ## Dev server (local orchestrator)
127
-
128
- `unispec dev` starts a local HTTP API that aggregates multiple UniSpec documents.
129
-
130
- Default bind:
131
-
132
- - Host: `127.0.0.1`
133
- - Port: `4141`
134
-
135
- Endpoints:
136
-
137
- - `GET /health` -> `OK`
138
- - `GET /unispec.json` -> aggregated JSON
139
- - `GET /workspace.json` -> workspace info (paths + API URLs)
140
- - `GET /workspace/state` -> workspace state
141
- - `PUT /workspace/state` -> save workspace state
142
- - `POST /tests/run` -> run tests (suite flow)
143
-
144
- Aggregation format (A):
145
-
146
- ```json
147
- {
148
- "version": 1,
149
- "generatedAt": "2025-12-28T12:00:00.000Z",
150
- "services": [
151
- {
152
- "name": "users",
153
- "specPath": "./services/users/unispec.yaml",
154
- "valid": true,
155
- "spec": {}
156
- }
157
- ]
158
- }
159
- ```
160
-
161
- If at least one service is invalid, `/unispec.json` responds with status `422` and includes per-service errors.
162
-
163
- ### Example
164
-
165
- Start the server:
166
-
167
- ```bash
168
- npm run dev -- dev --verbose
169
- ```
170
-
171
- Run using the bundled examples:
96
+ Examples:
172
97
 
173
98
  ```bash
174
- npm run dev -- dev -- --config example/unispec.config.json --verbose
99
+ unispec portal dev --config ./unispec.config.json
100
+ unispec portal start --config ./unispec.config.json
175
101
  ```
176
102
 
177
- Then open:
178
-
179
- - `http://127.0.0.1:4141/health`
180
- - `http://127.0.0.1:4141/unispec.json`
181
- - `http://127.0.0.1:4141/`
182
-
183
- ### `unispec start`
184
- Production-like server mode.
185
-
186
- `unispec start` runs the same HTTP surface as `unispec dev`, but is intended for long-running use.
187
-
188
103
  Options:
189
104
 
190
105
  - `--config <path>` — path to config file
191
- - `--host <host>` — bind host (default: `127.0.0.1`)
192
- - `--port <port>` — bind port (default: `4141`)
193
- - `--ui` / `--no-ui` enable/disable embedded UI
194
- - `--ui-url <url>` — proxy UI requests to an external UI server
195
- - `--portal` — spawn portal server (SSR) from an installed dependency and proxy UI to it
196
- - `--portal-port <port>` port for portal server (default: `3000`)
106
+ - `--host <host>` — bind host (forwarded to portal)
107
+ - `--port <port>` — bind port (forwarded to portal)
108
+ - `--storage <type>` `json|sqlite` (forwarded to portal)
109
+ - `--data-dir <path>` — data directory (forwarded to portal)
110
+ - `--open` — open browser (forwarded to portal)
111
+ - `--verbose`verbose output (forwarded to portal)
112
+ - `--json-errors` — output errors as JSON (forwarded to portal)
197
113
  - `--portal-package <name>` — portal package name (default: `@unispechq/unispec-portal`)
198
114
 
199
- ### Portal package contract
200
-
201
- When using `--portal`, the portal package is expected to provide a single runnable `bin` entry which supports:
202
-
203
- - `--port <port>`
204
- - `--mode dev|start`
205
-
206
- Example:
207
-
208
- ```bash
209
- unispec-portal --mode dev --port 3000
210
- unispec-portal --mode start --port 3000
211
- ```
212
-
213
115
  ---
214
116
 
215
- ## Workspace layer
216
-
217
- Workspace data is stored in `.unispec/workspace.json` next to your config file. This file is not part of UniSpec.
218
-
219
- ### Workspace endpoints
220
-
221
- - `GET /workspace.json` — info for clients (mode, configPath, storage paths, and API URLs)
222
- - `GET /workspace/state` — workspace state JSON
223
- - `PUT /workspace/state` — replace workspace state JSON (atomic write)
224
-
225
- ### Endpoint IDs
226
-
227
- Workspace entities reference endpoints using:
228
-
229
- ```
230
- {serviceName}:{METHOD}:{path}
231
- ```
232
-
233
- Example:
117
+ ## Notes
234
118
 
235
- ```
236
- users:GET:/users
237
- ```
238
-
239
- ---
240
-
241
- ## Workspace tests
242
-
243
- Tests are stored in workspace state as YAML or JSON strings and can be run through:
244
-
245
- - `POST /tests/run` with `{ "suiteId": "..." }` or `{ "testIds": ["..."] }`
246
-
247
- ### YAML/JSON test format (single request)
248
-
249
- ```yaml
250
- request:
251
- method: GET
252
- url: "http://127.0.0.1:4141/health"
253
- expect:
254
- status: 200
255
- bodyContains: "OK"
256
- ```
257
-
258
- ### Multi-step tests (flow)
259
-
260
- ```yaml
261
- steps:
262
- - name: health
263
- request:
264
- method: GET
265
- url: "http://127.0.0.1:4141/health"
266
- expect:
267
- status: 200
268
- bodyContains: "OK"
269
- extract:
270
- st:
271
- from: status
272
-
273
- - name: unispec
274
- request:
275
- method: GET
276
- url: "http://127.0.0.1:4141/unispec.json"
277
- expect:
278
- status: 200
279
- jsonContains:
280
- version: 1
281
- ```
282
-
283
- ### Variables and interpolation
284
-
285
- You can extract variables and use them with `{{var}}` in URL/headers/body:
286
-
287
- ```yaml
288
- steps:
289
- - request:
290
- method: GET
291
- url: "https://example.com/api/session"
292
- extract:
293
- token:
294
- from: json
295
- path: "$.token"
296
- - request:
297
- method: GET
298
- url: "https://example.com/api/me"
299
- headers:
300
- authorization: "Bearer {{token}}"
301
- expect:
302
- status: 200
303
- ```
119
+ All orchestration responsibilities (HTTP API, watch mode, workspace storage, and test runner) live in the Portal package.
304
120
 
305
121
  ---
306
122
 
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
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 { createRequire } from "node:module";
4
+ import { registerConvertCommand, registerDiffCommand, registerNormalizeCommand, registerPortalCommand, registerValidateCommand, } from "./commands/index.js";
4
5
  function errorToJson(err, includeStack) {
5
6
  const message = err instanceof Error ? err.message : String(err);
6
7
  const code = err instanceof Error && "code" in err ? String(err.code) : undefined;
@@ -30,10 +31,12 @@ function normalizeGlobalOptionsArgv(argv) {
30
31
  return [...head, ...extracted, ...remaining];
31
32
  }
32
33
  const program = new Command();
34
+ const require = createRequire(import.meta.url);
35
+ const pkg = require("../package.json");
33
36
  program
34
37
  .name("unispec")
35
- .description("UniSpec CLI and local orchestrator")
36
- .version("0.1.0")
38
+ .description("UniSpec CLI and portal launcher")
39
+ .version(String(pkg.version ?? "0.0.0"))
37
40
  .option("-q, --quiet", "Suppress non-essential output")
38
41
  .option("--verbose", "Enable verbose diagnostic output (stderr)")
39
42
  .option("--json-errors", "Output errors as JSON to stderr");
@@ -41,8 +44,7 @@ registerValidateCommand(program);
41
44
  registerNormalizeCommand(program);
42
45
  registerDiffCommand(program);
43
46
  registerConvertCommand(program);
44
- registerDevCommand(program);
45
- registerStartCommand(program);
47
+ registerPortalCommand(program);
46
48
  program.parseAsync(normalizeGlobalOptionsArgv(process.argv)).catch((err) => {
47
49
  const opts = program.opts();
48
50
  const verbose = opts.verbose ?? false;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
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,4 @@
1
+ import type { Command } from "commander";
2
+ export declare function buildPortalNodeArgs(binPath: string, mode: "dev" | "start", options: Record<string, unknown>): string[];
3
+ export declare function registerPortalCommand(program: Command): void;
4
+ //# 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,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAoBtH;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoD5D"}
@@ -0,0 +1,94 @@
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 buildPortalNodeArgs(binPath, mode, options) {
31
+ const args = [binPath, mode, "--config", String(options.config)];
32
+ const host = pickArg(options, "host", "host");
33
+ if (host)
34
+ args.push("--host", host);
35
+ const port = pickArg(options, "port", "port");
36
+ if (port)
37
+ args.push("--port", port);
38
+ const storage = pickArg(options, "storage", "storage");
39
+ if (storage)
40
+ args.push("--storage", storage);
41
+ const dataDir = pickArg(options, "dataDir", "data-dir");
42
+ if (dataDir)
43
+ args.push("--data-dir", dataDir);
44
+ if (options.open)
45
+ args.push("--open");
46
+ if (options.verbose)
47
+ args.push("--verbose");
48
+ if (options["jsonErrors"] ?? options["json-errors"])
49
+ args.push("--json-errors");
50
+ return args;
51
+ }
52
+ export function registerPortalCommand(program) {
53
+ const cmd = program
54
+ .command("portal")
55
+ .description("Launch UniSpec Portal (Next.js orchestrator)")
56
+ .option("--portal-package <name>", "Portal package name", "@unispechq/unispec-portal");
57
+ const addSharedOptions = (c) => c
58
+ .requiredOption("--config <path>", "Path to unispec config file")
59
+ .option("--host <host>", "Bind host")
60
+ .option("--port <port>", "Bind port")
61
+ .option("--storage <type>", "Storage backend (json|sqlite)")
62
+ .option("--data-dir <path>", "Data directory (default: <configDir>/.unispec)")
63
+ .option("--open", "Open browser")
64
+ .option("--verbose", "Verbose portal output")
65
+ .option("--json-errors", "Output portal errors as JSON");
66
+ for (const mode of ["dev", "start"]) {
67
+ addSharedOptions(cmd
68
+ .command(mode)
69
+ .description(`Run portal in ${mode} mode`)
70
+ .action(async function (options) {
71
+ const parentOpts = this.parent?.opts?.();
72
+ const portalPackage = String(parentOpts?.portalPackage ?? "@unispechq/unispec-portal");
73
+ let resolved = null;
74
+ try {
75
+ resolved = await resolvePortalBin(portalPackage);
76
+ }
77
+ catch (err) {
78
+ const message = err instanceof Error ? err.message : String(err);
79
+ throw new Error(`Failed to resolve portal package '${portalPackage}': ${message}`);
80
+ }
81
+ if (!resolved) {
82
+ throw new Error(`Portal package '${portalPackage}' does not expose a runnable bin. Expected a package.json "bin" entry.`);
83
+ }
84
+ const args = buildPortalNodeArgs(resolved.binPath, mode, options);
85
+ const child = spawn(process.execPath, args, {
86
+ stdio: "inherit",
87
+ env: { ...process.env },
88
+ });
89
+ const code = await new Promise((resolve) => child.once("exit", (c) => resolve(c ?? 0)));
90
+ process.exitCode = code;
91
+ }));
92
+ }
93
+ }
94
+ //# 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,7 +1,7 @@
1
1
  {
2
2
  "name": "@unispechq/unispec-platform",
3
- "version": "0.2.0",
4
- "description": "CLI and local orchestrator for UniSpec (validate/normalize/diff/convert/dev).",
3
+ "version": "0.2.2",
4
+ "description": "UniSpec CLI utilities (validate/normalize/diff/convert) and portal launcher.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -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,CAgLzD"}