@unispechq/unispec-platform 0.1.0

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 (74) hide show
  1. package/README.md +225 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +36 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/convert.d.ts +3 -0
  7. package/dist/commands/convert.d.ts.map +1 -0
  8. package/dist/commands/convert.js +55 -0
  9. package/dist/commands/convert.js.map +1 -0
  10. package/dist/commands/dev.d.ts +3 -0
  11. package/dist/commands/dev.d.ts.map +1 -0
  12. package/dist/commands/dev.js +108 -0
  13. package/dist/commands/dev.js.map +1 -0
  14. package/dist/commands/diff.d.ts +3 -0
  15. package/dist/commands/diff.d.ts.map +1 -0
  16. package/dist/commands/diff.js +32 -0
  17. package/dist/commands/diff.js.map +1 -0
  18. package/dist/commands/index.d.ts +6 -0
  19. package/dist/commands/index.d.ts.map +1 -0
  20. package/dist/commands/index.js +6 -0
  21. package/dist/commands/index.js.map +1 -0
  22. package/dist/commands/normalize.d.ts +3 -0
  23. package/dist/commands/normalize.d.ts.map +1 -0
  24. package/dist/commands/normalize.js +30 -0
  25. package/dist/commands/normalize.js.map +1 -0
  26. package/dist/commands/validate.d.ts +3 -0
  27. package/dist/commands/validate.d.ts.map +1 -0
  28. package/dist/commands/validate.js +31 -0
  29. package/dist/commands/validate.js.map +1 -0
  30. package/dist/config/loadConfig.d.ts +13 -0
  31. package/dist/config/loadConfig.d.ts.map +1 -0
  32. package/dist/config/loadConfig.js +42 -0
  33. package/dist/config/loadConfig.js.map +1 -0
  34. package/dist/dev/aggregate.d.ts +15 -0
  35. package/dist/dev/aggregate.d.ts.map +1 -0
  36. package/dist/dev/aggregate.js +56 -0
  37. package/dist/dev/aggregate.js.map +1 -0
  38. package/dist/dev/index.d.ts +3 -0
  39. package/dist/dev/index.d.ts.map +1 -0
  40. package/dist/dev/index.js +3 -0
  41. package/dist/dev/index.js.map +1 -0
  42. package/dist/dev/server.d.ts +12 -0
  43. package/dist/dev/server.d.ts.map +1 -0
  44. package/dist/dev/server.js +44 -0
  45. package/dist/dev/server.js.map +1 -0
  46. package/dist/io/index.d.ts +6 -0
  47. package/dist/io/index.d.ts.map +1 -0
  48. package/dist/io/index.js +6 -0
  49. package/dist/io/index.js.map +1 -0
  50. package/dist/io/readSpecFile.d.ts +2 -0
  51. package/dist/io/readSpecFile.d.ts.map +1 -0
  52. package/dist/io/readSpecFile.js +27 -0
  53. package/dist/io/readSpecFile.js.map +1 -0
  54. package/dist/io/readSpecInput.d.ts +6 -0
  55. package/dist/io/readSpecInput.d.ts.map +1 -0
  56. package/dist/io/readSpecInput.js +54 -0
  57. package/dist/io/readSpecInput.js.map +1 -0
  58. package/dist/io/readStdin.d.ts +2 -0
  59. package/dist/io/readStdin.d.ts.map +1 -0
  60. package/dist/io/readStdin.js +17 -0
  61. package/dist/io/readStdin.js.map +1 -0
  62. package/dist/io/readStdinText.d.ts +2 -0
  63. package/dist/io/readStdinText.d.ts.map +1 -0
  64. package/dist/io/readStdinText.js +15 -0
  65. package/dist/io/readStdinText.js.map +1 -0
  66. package/dist/io/writeFileOrStdout.d.ts +2 -0
  67. package/dist/io/writeFileOrStdout.d.ts.map +1 -0
  68. package/dist/io/writeFileOrStdout.js +11 -0
  69. package/dist/io/writeFileOrStdout.js.map +1 -0
  70. package/dist/io/writeOutput.d.ts +4 -0
  71. package/dist/io/writeOutput.d.ts.map +1 -0
  72. package/dist/io/writeOutput.js +11 -0
  73. package/dist/io/writeOutput.js.map +1 -0
  74. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # UniSpec Platform
2
+
3
+ **CLI and local orchestrator for the UniSpec ecosystem**
4
+
5
+ This repository contains the official UniSpec CLI and the foundation for a local orchestrator.
6
+ It builds on top of:
7
+
8
+ - `unispec-spec` — the UniSpec format definition (schemas, examples, reference docs)
9
+ - `@unispechq/unispec-core` — the Core Engine (loader, validator, normalizer, diff, converters)
10
+
11
+ UniSpec is a modern, multi-protocol API specification format designed to unify:
12
+
13
+ - REST
14
+ - GraphQL
15
+ - WebSocket
16
+ - (future) event-driven APIs
17
+
18
+ ---
19
+
20
+ ## Repository Status
21
+
22
+ This repository is in early development.
23
+ Current focus:
24
+
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)
28
+
29
+ ---
30
+
31
+ ## Repository Structure
32
+
33
+ ```text
34
+ unispec-platform/
35
+ ├─ src/
36
+ │ ├─ cli.ts # CLI entrypoint
37
+ │ ├─ commands/ # Command registration (validate/normalize/diff/convert/dev)
38
+ │ ├─ io/ # File IO + output helpers
39
+ │ └─ config/ # Config loading (future dev/orchestrator mode)
40
+ ├─ windsurfrules.yml # WindSurf rules (stored without dotfile)
41
+ ├─ package.json
42
+ ├─ tsconfig.json
43
+ └─ README.md
44
+ ```
45
+
46
+ ## CLI Commands
47
+
48
+ Global flags:
49
+
50
+ - `-q, --quiet` — suppress non-essential output
51
+ - `--verbose` — enable diagnostic output (stderr)
52
+ - `--json-errors` — output errors as JSON (stderr)
53
+
54
+ ### `unispec validate [file]`
55
+ Validates a UniSpec document against the official JSON Schemas.
56
+
57
+ Options:
58
+
59
+ - `--stdin` — read spec from stdin (or pass `-` as file)
60
+
61
+ ### `unispec normalize [file]`
62
+ Normalizes a UniSpec document into deterministic canonical form.
63
+
64
+ Options:
65
+
66
+ - `--stdin` — read spec from stdin (or pass `-` as file)
67
+ - `--format <format>` — `json|yaml`
68
+ - `--output <file>` — write output to file (use `-` for stdout)
69
+
70
+ ### `unispec diff <oldFile> <newFile>`
71
+ Computes a structural diff between two UniSpec documents.
72
+
73
+ Options:
74
+
75
+ - `--format <format>` — `json|yaml`
76
+ - `--output <file>` — write output to file (use `-` for stdout)
77
+
78
+ ### `unispec convert [file] --to <target>`
79
+ Converts UniSpec into other representations.
80
+
81
+ Targets:
82
+
83
+ - `openapi` — outputs OpenAPI 3.1 JSON/YAML
84
+ - `graphql-sdl` — outputs GraphQL SDL
85
+ - `ws-model` — outputs a dashboard-friendly WebSocket model
86
+
87
+ Options:
88
+
89
+ - `--stdin` — read spec from stdin (or pass `-` as file)
90
+ - `--format <format>` — `json|yaml` (for JSON-like targets)
91
+ - `--output <file>` — write output to file (use `-` for stdout)
92
+
93
+ ### `unispec dev`
94
+ Local orchestrator mode.
95
+
96
+ Options:
97
+
98
+ - `--config <path>` — path to config file (default: `unispec.config.json|yaml|yml`)
99
+ - `--host <host>` — bind host (default: `127.0.0.1`)
100
+ - `--port <port>` — bind port (default: `4141`)
101
+ - `--watch` — watch config/spec files and refresh aggregated snapshot
102
+
103
+ Planned responsibilities:
104
+
105
+ - Watch local specs and/or connected services
106
+ - Aggregate multiple UniSpec documents (microservices mode)
107
+ - Serve a local dashboard and endpoints (e.g. `/unispec.json` for aggregated view)
108
+ - Provide a single workflow to validate/normalize/diff before publishing to Registry
109
+
110
+ ---
111
+
112
+ ## Dev server (local orchestrator)
113
+
114
+ `unispec dev` starts a local HTTP API that aggregates multiple UniSpec documents.
115
+
116
+ Default bind:
117
+
118
+ - Host: `127.0.0.1`
119
+ - Port: `4141`
120
+
121
+ Endpoints:
122
+
123
+ - `GET /health` -> `OK`
124
+ - `GET /unispec.json` -> aggregated JSON
125
+
126
+ Aggregation format (A):
127
+
128
+ ```json
129
+ {
130
+ "version": 1,
131
+ "generatedAt": "2025-12-28T12:00:00.000Z",
132
+ "services": [
133
+ {
134
+ "name": "users",
135
+ "specPath": "./services/users/unispec.yaml",
136
+ "valid": true,
137
+ "spec": {}
138
+ }
139
+ ]
140
+ }
141
+ ```
142
+
143
+ If at least one service is invalid, `/unispec.json` responds with status `422` and includes per-service errors.
144
+
145
+ ### Example
146
+
147
+ Create `unispec.config.json` (see `unispec.config.example.json`):
148
+
149
+ ```bash
150
+ cp unispec.config.example.json unispec.config.json
151
+ ```
152
+
153
+ Start the server:
154
+
155
+ ```bash
156
+ npm run dev -- dev --verbose
157
+ ```
158
+
159
+ Run using the bundled examples:
160
+
161
+ ```bash
162
+ npm run dev -- dev -- --config example/unispec.config.json --verbose
163
+ ```
164
+
165
+ Then open:
166
+
167
+ - `http://127.0.0.1:4141/health`
168
+ - `http://127.0.0.1:4141/unispec.json`
169
+
170
+ ---
171
+
172
+ ## Development
173
+
174
+ ### Requirements
175
+
176
+ - Node.js >= 18
177
+
178
+ ### Install
179
+
180
+ ```bash
181
+ npm install
182
+ ```
183
+
184
+ ### Run CLI in dev mode
185
+
186
+ ```bash
187
+ npm run dev -- --help
188
+ ```
189
+
190
+ ### Build
191
+
192
+ ```bash
193
+ npm run build
194
+ ```
195
+
196
+ ### Run built CLI
197
+
198
+ ```bash
199
+ npm run start -- --help
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Related Repositories
205
+
206
+ | Repository | Purpose |
207
+ |---|---|
208
+ | `unispec-spec` | UniSpec format definition (schemas, examples) |
209
+ | `unispec-core` | Core Engine (loader/validator/normalizer/diff/converters) |
210
+ | `unispec-js-adapters` | Framework integrations (Express/NestJS/etc.) |
211
+ | `unispec-registry` | (Future) publishing/reading specs and version history |
212
+
213
+ ---
214
+
215
+ ## Contributing
216
+
217
+ Contributions are welcome.
218
+
219
+ Please read `windsurfrules.yml` before making changes.
220
+
221
+ ---
222
+
223
+ ## License
224
+
225
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { registerConvertCommand, registerDevCommand, registerDiffCommand, registerNormalizeCommand, registerValidateCommand, } from "./commands/index.js";
4
+ function errorToJson(err, includeStack) {
5
+ const message = err instanceof Error ? err.message : String(err);
6
+ const code = err instanceof Error && "code" in err ? String(err.code) : undefined;
7
+ const stack = includeStack && err instanceof Error ? err.stack : undefined;
8
+ return { error: { message, ...(code ? { code } : {}), ...(stack ? { stack } : {}) } };
9
+ }
10
+ const program = new Command();
11
+ program
12
+ .name("unispec")
13
+ .description("UniSpec CLI and local orchestrator")
14
+ .version("0.1.0")
15
+ .option("-q, --quiet", "Suppress non-essential output")
16
+ .option("--verbose", "Enable verbose diagnostic output (stderr)")
17
+ .option("--json-errors", "Output errors as JSON to stderr");
18
+ registerValidateCommand(program);
19
+ registerNormalizeCommand(program);
20
+ registerDiffCommand(program);
21
+ registerConvertCommand(program);
22
+ registerDevCommand(program);
23
+ program.parseAsync(process.argv).catch((err) => {
24
+ const opts = program.opts();
25
+ const verbose = opts.verbose ?? false;
26
+ const jsonErrors = opts["jsonErrors"] ?? opts["json-errors"] ?? false;
27
+ if (jsonErrors) {
28
+ process.stderr.write(`${JSON.stringify(errorToJson(err, verbose), null, 2)}\n`);
29
+ }
30
+ else {
31
+ const message = err instanceof Error ? err.message : String(err);
32
+ process.stderr.write(`Error: ${message}\n`);
33
+ }
34
+ process.exitCode = 1;
35
+ });
36
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +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,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;AAE5B,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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerConvertCommand(program: Command): void;
3
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../src/commands/convert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2D7D"}
@@ -0,0 +1,55 @@
1
+ import { loadUniSpec, toGraphQLSDL, toOpenAPI, toWebSocketModel } from "@unispechq/unispec-core";
2
+ import { readSpecInput } from "../io/readSpecInput.js";
3
+ import { writeText } from "../io/writeFileOrStdout.js";
4
+ import { stringifyOutput } from "../io/writeOutput.js";
5
+ export function registerConvertCommand(program) {
6
+ program
7
+ .command("convert")
8
+ .description("Convert UniSpec document to another format")
9
+ .argument("[file]", "Path to UniSpec file (.json/.yaml) or '-' for stdin")
10
+ .option("--stdin", "Read spec from stdin")
11
+ .requiredOption("--to <target>", "Target: openapi|graphql-sdl|ws-model")
12
+ .option("--format <format>", "Output format for JSON-like targets: json|yaml", "json")
13
+ .option("--output <file>", "Write output to file (default: stdout). Use '-' for stdout")
14
+ .action(async function (file, options) {
15
+ const parentOpts = this.parent?.opts?.();
16
+ const verbose = parentOpts?.verbose ?? false;
17
+ if (verbose) {
18
+ const source = options.stdin || file === "-" ? "stdin" : String(file ?? "");
19
+ process.stderr.write(`Reading spec from ${source}\\n`);
20
+ process.stderr.write(`Conversion target: ${String(options.to)}\\n`);
21
+ }
22
+ const raw = await readSpecInput({ filePath: file, stdin: options.stdin });
23
+ const doc = await loadUniSpec(raw);
24
+ const target = String(options.to);
25
+ if (target === "openapi") {
26
+ const out = toOpenAPI(doc);
27
+ await writeText(options.output, stringifyOutput(out, options.format));
28
+ if (verbose) {
29
+ const dest = options.output && options.output !== "-" ? options.output : "stdout";
30
+ process.stderr.write(`Wrote OpenAPI output to ${dest}\\n`);
31
+ }
32
+ return;
33
+ }
34
+ if (target === "graphql-sdl") {
35
+ const out = toGraphQLSDL(doc);
36
+ await writeText(options.output, out.sdl.endsWith("\n") ? out.sdl : `${out.sdl}\n`);
37
+ if (verbose) {
38
+ const dest = options.output && options.output !== "-" ? options.output : "stdout";
39
+ process.stderr.write(`Wrote GraphQL SDL output to ${dest}\\n`);
40
+ }
41
+ return;
42
+ }
43
+ if (target === "ws-model") {
44
+ const out = toWebSocketModel(doc);
45
+ await writeText(options.output, stringifyOutput(out, options.format));
46
+ if (verbose) {
47
+ const dest = options.output && options.output !== "-" ? options.output : "stdout";
48
+ process.stderr.write(`Wrote WebSocket model output to ${dest}\\n`);
49
+ }
50
+ return;
51
+ }
52
+ throw new Error(`Unknown conversion target: ${target}`);
53
+ });
54
+ }
55
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../../src/commands/convert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerDevCommand(program: Command): void;
3
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+GzD"}
@@ -0,0 +1,108 @@
1
+ import { watch } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { loadConfig } from "../config/loadConfig.js";
4
+ import { findConfigPath } from "../config/loadConfig.js";
5
+ import { aggregateFromConfig } from "../dev/aggregate.js";
6
+ import { startDevServer } from "../dev/server.js";
7
+ export function registerDevCommand(program) {
8
+ program
9
+ .command("dev")
10
+ .description("Local orchestrator mode: aggregate specs and serve a local API")
11
+ .option("--config <path>", "Path to unispec config file")
12
+ .option("--host <host>", "Host to bind", "127.0.0.1")
13
+ .option("--port <port>", "Port to bind", "4141")
14
+ .option("--watch", "Watch config/spec files and refresh aggregated snapshot")
15
+ .action(async function (options) {
16
+ const parentOpts = this.parent?.opts?.();
17
+ const verbose = parentOpts?.verbose ?? false;
18
+ const host = String(options.host ?? "127.0.0.1");
19
+ const port = Number(options.port ?? 4141);
20
+ let snapshot;
21
+ let snapshotError;
22
+ const loadAndAggregate = async () => {
23
+ const config = await loadConfig({ explicitPath: options.config });
24
+ const cfg = config ?? { version: 1, services: [] };
25
+ return aggregateFromConfig(cfg);
26
+ };
27
+ const refreshSnapshot = async () => {
28
+ try {
29
+ snapshot = await loadAndAggregate();
30
+ snapshotError = undefined;
31
+ if (verbose) {
32
+ process.stderr.write("Refreshed aggregated snapshot\n");
33
+ }
34
+ }
35
+ catch (err) {
36
+ const message = err instanceof Error ? err.message : String(err);
37
+ snapshotError = { message };
38
+ if (verbose) {
39
+ process.stderr.write(`Failed to refresh snapshot: ${message}\n`);
40
+ }
41
+ }
42
+ };
43
+ await refreshSnapshot();
44
+ const getConfig = async () => {
45
+ const config = await loadConfig({ explicitPath: options.config });
46
+ return config ?? { version: 1, services: [] };
47
+ };
48
+ const getAggregated = async () => {
49
+ if (snapshot) {
50
+ return snapshot;
51
+ }
52
+ if (snapshotError) {
53
+ return {
54
+ version: 1,
55
+ generatedAt: new Date().toISOString(),
56
+ services: [
57
+ {
58
+ name: "orchestrator",
59
+ specPath: "config",
60
+ valid: false,
61
+ errors: [snapshotError],
62
+ },
63
+ ],
64
+ };
65
+ }
66
+ return loadAndAggregate();
67
+ };
68
+ if (options.watch) {
69
+ const configPath = await findConfigPath({ explicitPath: options.config });
70
+ const cfg = await getConfig();
71
+ const serviceSpecs = Array.isArray(cfg.services) ? cfg.services.map((s) => String(s.spec ?? "")) : [];
72
+ const watchTargets = [
73
+ ...(configPath ? [configPath] : []),
74
+ ...serviceSpecs.filter(Boolean).map((p) => resolve(process.cwd(), p)),
75
+ ];
76
+ let timer;
77
+ const scheduleRefresh = () => {
78
+ if (timer)
79
+ clearTimeout(timer);
80
+ timer = setTimeout(() => {
81
+ void refreshSnapshot();
82
+ }, 200);
83
+ };
84
+ for (const target of watchTargets) {
85
+ try {
86
+ watch(target, { persistent: true }, () => scheduleRefresh());
87
+ if (verbose) {
88
+ process.stderr.write(`Watching ${target}\n`);
89
+ }
90
+ }
91
+ catch (err) {
92
+ if (verbose) {
93
+ const message = err instanceof Error ? err.message : String(err);
94
+ process.stderr.write(`Failed to watch ${target}: ${message}\n`);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ await startDevServer({
100
+ host,
101
+ port,
102
+ getConfig,
103
+ getAggregated,
104
+ verbose,
105
+ });
106
+ });
107
+ }
108
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerDiffCommand(program: Command): void;
3
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+B1D"}
@@ -0,0 +1,32 @@
1
+ import { diffUniSpec, loadUniSpec } from "@unispechq/unispec-core";
2
+ import { readSpecFile } from "../io/readSpecFile.js";
3
+ import { writeText } from "../io/writeFileOrStdout.js";
4
+ import { stringifyOutput } from "../io/writeOutput.js";
5
+ export function registerDiffCommand(program) {
6
+ program
7
+ .command("diff")
8
+ .description("Compute a structural diff between two UniSpec documents")
9
+ .argument("<oldFile>", "Path to old UniSpec file")
10
+ .argument("<newFile>", "Path to new UniSpec file")
11
+ .option("--format <format>", "Output format: json|yaml", "json")
12
+ .option("--output <file>", "Write output to file (default: stdout). Use '-' for stdout")
13
+ .action(async function (oldFile, newFile, options) {
14
+ const parentOpts = this.parent?.opts?.();
15
+ const verbose = parentOpts?.verbose ?? false;
16
+ if (verbose) {
17
+ process.stderr.write(`Reading old spec from ${oldFile}\n`);
18
+ process.stderr.write(`Reading new spec from ${newFile}\n`);
19
+ }
20
+ const oldRaw = await readSpecFile(String(oldFile));
21
+ const newRaw = await readSpecFile(String(newFile));
22
+ const oldDoc = await loadUniSpec(oldRaw);
23
+ const newDoc = await loadUniSpec(newRaw);
24
+ const result = diffUniSpec(oldDoc, newDoc);
25
+ await writeText(options.output, stringifyOutput(result, options.format));
26
+ if (verbose) {
27
+ const dest = options.output && options.output !== "-" ? options.output : "stdout";
28
+ process.stderr.write(`Wrote diff output to ${dest}\n`);
29
+ }
30
+ });
31
+ }
32
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAOvD,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,yDAAyD,CAAC;SACtE,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;SACjD,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;SACjD,MAAM,CAAC,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,CAAC;SAC/D,MAAM,CAAC,iBAAiB,EAAE,4DAA4D,CAAC;SACvF,MAAM,CAAC,KAAK,WAA0B,OAAe,EAAE,OAAe,EAAE,OAAgB;QACvF,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,EAAwD,CAAC;QAC/F,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,IAAI,KAAK,CAAC;QAE7C,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,OAAO,IAAI,CAAC,CAAC;YAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,OAAO,IAAI,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAEnD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QAEzC,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,MAAM,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,MAAsB,CAAC,CAAC,CAAC;QAEzF,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YAClF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,IAAI,IAAI,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./validate.js";
2
+ export * from "./normalize.js";
3
+ export * from "./diff.js";
4
+ export * from "./convert.js";
5
+ export * from "./dev.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,6 @@
1
+ export * from "./validate.js";
2
+ export * from "./normalize.js";
3
+ export * from "./diff.js";
4
+ export * from "./convert.js";
5
+ export * from "./dev.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerNormalizeCommand(program: Command): void;
3
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/commands/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAazC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2B/D"}
@@ -0,0 +1,30 @@
1
+ import { loadUniSpec, normalizeUniSpec } from "@unispechq/unispec-core";
2
+ import { readSpecInput } from "../io/readSpecInput.js";
3
+ import { writeText } from "../io/writeFileOrStdout.js";
4
+ import { stringifyOutput } from "../io/writeOutput.js";
5
+ export function registerNormalizeCommand(program) {
6
+ program
7
+ .command("normalize")
8
+ .description("Normalize a UniSpec document into deterministic canonical form")
9
+ .argument("[file]", "Path to UniSpec file (.json/.yaml) or '-' for stdin")
10
+ .option("--stdin", "Read spec from stdin")
11
+ .option("--format <format>", "Output format: json|yaml", "json")
12
+ .option("--output <file>", "Write output to file (default: stdout). Use '-' for stdout")
13
+ .action(async function (file, options) {
14
+ const parentOpts = this.parent?.opts?.();
15
+ const verbose = parentOpts?.verbose ?? false;
16
+ if (verbose) {
17
+ const source = options.stdin || file === "-" ? "stdin" : String(file ?? "");
18
+ process.stderr.write(`Reading spec from ${source}\n`);
19
+ }
20
+ const raw = await readSpecInput({ filePath: file, stdin: options.stdin });
21
+ const doc = await loadUniSpec(raw);
22
+ const normalized = normalizeUniSpec(doc);
23
+ await writeText(options.output, stringifyOutput(normalized, options.format));
24
+ if (verbose) {
25
+ const dest = options.output && options.output !== "-" ? options.output : "stdout";
26
+ process.stderr.write(`Wrote normalized output to ${dest}\n`);
27
+ }
28
+ });
29
+ }
30
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/commands/normalize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAEvD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAQvD,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,gEAAgE,CAAC;SAC7E,QAAQ,CAAC,QAAQ,EAAE,qDAAqD,CAAC;SACzE,MAAM,CAAC,SAAS,EAAE,sBAAsB,CAAC;SACzC,MAAM,CAAC,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,CAAC;SAC/D,MAAM,CAAC,iBAAiB,EAAE,4DAA4D,CAAC;SACvF,MAAM,CAAC,KAAK,WAA0B,IAAwB,EAAE,OAAgB;QAC/E,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,EAAwD,CAAC;QAC/F,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,IAAI,KAAK,CAAC;QAE7C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YAC5E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,MAAM,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1E,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,MAAsB,CAAC,CAAC,CAAC;QAE7F,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YAClF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,IAAI,IAAI,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerValidateCommand(program: Command): void;
3
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8B9D"}
@@ -0,0 +1,31 @@
1
+ import { loadUniSpec, validateUniSpec } from "@unispechq/unispec-core";
2
+ import { readSpecInput } from "../io/readSpecInput.js";
3
+ import { writeOutput } from "../io/writeOutput.js";
4
+ export function registerValidateCommand(program) {
5
+ program
6
+ .command("validate")
7
+ .description("Validate a UniSpec document against the official JSON Schemas")
8
+ .argument("[file]", "Path to UniSpec file (.json/.yaml) or '-' for stdin")
9
+ .option("--stdin", "Read spec from stdin")
10
+ .action(async function (file, options) {
11
+ const parentOpts = this.parent?.opts?.();
12
+ const quiet = parentOpts?.quiet ?? false;
13
+ const verbose = parentOpts?.verbose ?? false;
14
+ const raw = await readSpecInput({ filePath: file, stdin: options.stdin });
15
+ const doc = await loadUniSpec(raw);
16
+ const result = await validateUniSpec(doc);
17
+ if (result.valid) {
18
+ if (!quiet) {
19
+ process.stdout.write("OK\n");
20
+ }
21
+ process.exitCode = 0;
22
+ return;
23
+ }
24
+ if (verbose) {
25
+ process.stderr.write("Validation failed\n");
26
+ }
27
+ writeOutput(result, "json");
28
+ process.exitCode = 1;
29
+ });
30
+ }
31
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,+DAA+D,CAAC;SAC5E,QAAQ,CAAC,QAAQ,EAAE,qDAAqD,CAAC;SACzE,MAAM,CAAC,SAAS,EAAE,sBAAsB,CAAC;SACzC,MAAM,CAAC,KAAK,WAA0B,IAAwB,EAAE,OAA4B;QAC3F,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,EAAwD,CAAC;QAC/F,MAAM,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,KAAK,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,IAAI,KAAK,CAAC;QAE7C,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1E,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;QAE1C,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAC9C,CAAC;QAED,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface UniSpecPlatformConfig {
2
+ version?: number;
3
+ services?: Array<{
4
+ name: string;
5
+ spec: string;
6
+ }>;
7
+ }
8
+ export interface LoadConfigOptions {
9
+ explicitPath?: string;
10
+ }
11
+ export declare function findConfigPath(options?: LoadConfigOptions): Promise<string | undefined>;
12
+ export declare function loadConfig(options?: LoadConfigOptions): Promise<UniSpecPlatformConfig | undefined>;
13
+ //# sourceMappingURL=loadConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadConfig.d.ts","sourceRoot":"","sources":["../../src/config/loadConfig.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkBD,wBAAsB,cAAc,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAajG;AAED,wBAAsB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAY5G"}
@@ -0,0 +1,42 @@
1
+ import { access, readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import YAML from "yaml";
4
+ async function fileExists(path) {
5
+ try {
6
+ await access(path);
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ function parseByExt(filePath, raw) {
14
+ if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
15
+ return YAML.parse(raw);
16
+ }
17
+ return JSON.parse(raw);
18
+ }
19
+ export async function findConfigPath(options = {}) {
20
+ const candidates = options.explicitPath
21
+ ? [options.explicitPath]
22
+ : ["unispec.config.json", "unispec.config.yaml", "unispec.config.yml"];
23
+ for (const rel of candidates) {
24
+ const abs = resolve(process.cwd(), rel);
25
+ if (await fileExists(abs)) {
26
+ return abs;
27
+ }
28
+ }
29
+ return undefined;
30
+ }
31
+ export async function loadConfig(options = {}) {
32
+ const abs = await findConfigPath(options);
33
+ if (abs) {
34
+ const raw = (await readFile(abs, "utf8")).trim();
35
+ if (!raw) {
36
+ return {};
37
+ }
38
+ return parseByExt(abs, raw);
39
+ }
40
+ return undefined;
41
+ }
42
+ //# sourceMappingURL=loadConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../../src/config/loadConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AAcxB,KAAK,UAAU,UAAU,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB,EAAE,GAAW;IAC/C,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAA6B,EAAE;IAClE,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY;QACrC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;QACxB,CAAC,CAAC,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,oBAAoB,CAAC,CAAC;IAEzE,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;QACxC,IAAI,MAAM,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAA6B,EAAE;IAC9D,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,GAAG,GAAG,CAAC,MAAM,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,UAAU,CAAC,GAAG,EAAE,GAAG,CAA0B,CAAC;IACvD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,15 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,56 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,3 @@
1
+ export * from "./aggregate.js";
2
+ export * from "./server.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./aggregate.js";
2
+ export * from "./server.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,12 @@
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
+ }
11
+ export declare function startDevServer(options: DevServerOptions): Promise<http.Server>;
12
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dev/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,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;CACnB;AASD,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAuCpF"}
@@ -0,0 +1,44 @@
1
+ import http from "node:http";
2
+ import { aggregateFromConfig } from "./aggregate.js";
3
+ function writeJson(res, status, body) {
4
+ const json = JSON.stringify(body, null, 2);
5
+ res.statusCode = status;
6
+ res.setHeader("content-type", "application/json; charset=utf-8");
7
+ res.end(`${json}\n`);
8
+ }
9
+ export async function startDevServer(options) {
10
+ const server = http.createServer(async (req, res) => {
11
+ const method = req.method ?? "GET";
12
+ const url = req.url ?? "/";
13
+ if (method === "GET" && url === "/health") {
14
+ res.statusCode = 200;
15
+ res.setHeader("content-type", "text/plain; charset=utf-8");
16
+ res.end("OK\n");
17
+ return;
18
+ }
19
+ if (method === "GET" && url === "/unispec.json") {
20
+ try {
21
+ const aggregated = options.getAggregated
22
+ ? await options.getAggregated()
23
+ : await aggregateFromConfig(await options.getConfig());
24
+ const hasErrors = aggregated.services.some((s) => !s.valid);
25
+ writeJson(res, hasErrors ? 422 : 200, aggregated);
26
+ }
27
+ catch (err) {
28
+ const message = err instanceof Error ? err.message : String(err);
29
+ writeJson(res, 500, { error: { message } });
30
+ }
31
+ return;
32
+ }
33
+ writeJson(res, 404, { error: { message: "Not found" } });
34
+ });
35
+ await new Promise((resolve, reject) => {
36
+ server.once("error", reject);
37
+ server.listen(options.port, options.host, () => resolve());
38
+ });
39
+ if (options.verbose) {
40
+ process.stderr.write(`Dev server listening on http://${options.host}:${options.port}\n`);
41
+ }
42
+ return server;
43
+ }
44
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dev/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAYrD,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,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAyB;IAC5D,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,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAC3D,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"}
@@ -0,0 +1,6 @@
1
+ export * from "./readSpecFile.js";
2
+ export * from "./readSpecInput.js";
3
+ export * from "./readStdinText.js";
4
+ export * from "./writeOutput.js";
5
+ export * from "./writeFileOrStdout.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/io/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./readSpecFile.js";
2
+ export * from "./readSpecInput.js";
3
+ export * from "./readStdinText.js";
4
+ export * from "./writeOutput.js";
5
+ export * from "./writeFileOrStdout.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/io/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function readSpecFile(filePath: string): Promise<object>;
2
+ //# sourceMappingURL=readSpecFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readSpecFile.d.ts","sourceRoot":"","sources":["../../src/io/readSpecFile.ts"],"names":[],"mappings":"AAQA,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBpE"}
@@ -0,0 +1,27 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import YAML from "yaml";
4
+ function isObjectLike(value) {
5
+ return typeof value === "object" && value !== null;
6
+ }
7
+ export async function readSpecFile(filePath) {
8
+ const abs = resolve(process.cwd(), filePath);
9
+ const raw = await readFile(abs, "utf8");
10
+ const trimmed = raw.trim();
11
+ if (!trimmed) {
12
+ throw new Error(`Spec file is empty: ${filePath}`);
13
+ }
14
+ if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
15
+ const parsed = YAML.parse(trimmed);
16
+ if (!isObjectLike(parsed)) {
17
+ throw new Error(`Spec file must contain an object at the root: ${filePath}`);
18
+ }
19
+ return parsed;
20
+ }
21
+ const parsed = JSON.parse(trimmed);
22
+ if (!isObjectLike(parsed)) {
23
+ throw new Error(`Spec file must contain an object at the root: ${filePath}`);
24
+ }
25
+ return parsed;
26
+ }
27
+ //# sourceMappingURL=readSpecFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readSpecFile.js","sourceRoot":"","sources":["../../src/io/readSpecFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAE3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5D,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,iDAAiD,QAAQ,EAAE,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,iDAAiD,QAAQ,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface ReadSpecInputOptions {
2
+ filePath?: string;
3
+ stdin?: boolean;
4
+ }
5
+ export declare function readSpecInput(options: ReadSpecInputOptions): Promise<object>;
6
+ //# sourceMappingURL=readSpecInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readSpecInput.d.ts","sourceRoot":"","sources":["../../src/io/readSpecInput.ts"],"names":[],"mappings":"AAgDA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAalF"}
@@ -0,0 +1,54 @@
1
+ import YAML from "yaml";
2
+ import { readStdinText } from "./readStdinText.js";
3
+ import { readSpecFile } from "./readSpecFile.js";
4
+ function isObjectLike(value) {
5
+ return typeof value === "object" && value !== null;
6
+ }
7
+ function parseSpecText(sourceHint, text) {
8
+ const trimmed = text.trim();
9
+ if (!trimmed) {
10
+ throw new Error(`Spec input is empty: ${sourceHint}`);
11
+ }
12
+ const ext = sourceHint.toLowerCase();
13
+ if (ext.endsWith(".yaml") || ext.endsWith(".yml")) {
14
+ const parsed = YAML.parse(trimmed);
15
+ if (!isObjectLike(parsed)) {
16
+ throw new Error(`Spec input must contain an object at the root: ${sourceHint}`);
17
+ }
18
+ return parsed;
19
+ }
20
+ if (ext.endsWith(".json")) {
21
+ const parsed = JSON.parse(trimmed);
22
+ if (!isObjectLike(parsed)) {
23
+ throw new Error(`Spec input must contain an object at the root: ${sourceHint}`);
24
+ }
25
+ return parsed;
26
+ }
27
+ // Autodetect: try JSON first, then YAML.
28
+ try {
29
+ const parsedJson = JSON.parse(trimmed);
30
+ if (!isObjectLike(parsedJson)) {
31
+ throw new Error(`Spec input must contain an object at the root: ${sourceHint}`);
32
+ }
33
+ return parsedJson;
34
+ }
35
+ catch {
36
+ const parsedYaml = YAML.parse(trimmed);
37
+ if (!isObjectLike(parsedYaml)) {
38
+ throw new Error(`Spec input must contain an object at the root: ${sourceHint}`);
39
+ }
40
+ return parsedYaml;
41
+ }
42
+ }
43
+ export async function readSpecInput(options) {
44
+ const filePath = options.filePath;
45
+ if (options.stdin || filePath === "-") {
46
+ const text = await readStdinText();
47
+ return parseSpecText(filePath ?? "stdin", text);
48
+ }
49
+ if (!filePath) {
50
+ throw new Error("Missing spec input: provide <file> or --stdin");
51
+ }
52
+ return readSpecFile(filePath);
53
+ }
54
+ //# sourceMappingURL=readSpecInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readSpecInput.js","sourceRoot":"","sources":["../../src/io/readSpecInput.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,SAAS,aAAa,CAAC,UAAkB,EAAE,IAAY;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IAErC,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,kDAAkD,UAAU,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,kDAAkD,UAAU,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC;QACH,MAAM,UAAU,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,kDAAkD,UAAU,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,UAAU,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,kDAAkD,UAAU,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA6B;IAC/D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAElC,IAAI,OAAO,CAAC,KAAK,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;QACnC,OAAO,aAAa,CAAC,QAAQ,IAAI,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function readStdinText(): Promise<string>;
2
+ //# sourceMappingURL=readStdin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readStdin.d.ts","sourceRoot":"","sources":["../../src/io/readStdin.ts"],"names":[],"mappings":"AAAA,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAiBrD"}
@@ -0,0 +1,17 @@
1
+ export async function readStdinText() {
2
+ return new Promise((resolve, reject) => {
3
+ let data = "";
4
+ process.stdin.setEncoding("utf8");
5
+ process.stdin.on("data", (chunk) => {
6
+ data += chunk;
7
+ });
8
+ process.stdin.on("end", () => resolve(data));
9
+ process.stdin.on("error", (err) => reject(err));
10
+ if (process.stdin.isTTY) {
11
+ // If stdin is a TTY, there may be no data coming; resolve empty.
12
+ // Commands that rely on stdin should validate non-empty input.
13
+ resolve("");
14
+ }
15
+ });
16
+ }
17
+ //# sourceMappingURL=readStdin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readStdin.js","sourceRoot":"","sources":["../../src/io/readStdin.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAC;QAEd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YACjC,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAEhD,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACxB,iEAAiE;YACjE,+DAA+D;YAC/D,OAAO,CAAC,EAAE,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function readStdinText(): Promise<string>;
2
+ //# sourceMappingURL=readStdinText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readStdinText.d.ts","sourceRoot":"","sources":["../../src/io/readStdinText.ts"],"names":[],"mappings":"AAAA,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAerD"}
@@ -0,0 +1,15 @@
1
+ export async function readStdinText() {
2
+ return new Promise((resolve, reject) => {
3
+ let data = "";
4
+ process.stdin.setEncoding("utf8");
5
+ process.stdin.on("data", (chunk) => {
6
+ data += chunk;
7
+ });
8
+ process.stdin.on("end", () => resolve(data));
9
+ process.stdin.on("error", (err) => reject(err));
10
+ if (process.stdin.isTTY) {
11
+ resolve("");
12
+ }
13
+ });
14
+ }
15
+ //# sourceMappingURL=readStdinText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readStdinText.js","sourceRoot":"","sources":["../../src/io/readStdinText.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,EAAE,CAAC;QAEd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YACjC,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAEhD,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACxB,OAAO,CAAC,EAAE,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function writeText(outputPath: string | undefined, text: string): Promise<void>;
2
+ //# sourceMappingURL=writeFileOrStdout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeFileOrStdout.d.ts","sourceRoot":"","sources":["../../src/io/writeFileOrStdout.ts"],"names":[],"mappings":"AAGA,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3F"}
@@ -0,0 +1,11 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ export async function writeText(outputPath, text) {
4
+ if (!outputPath || outputPath === "-") {
5
+ process.stdout.write(text);
6
+ return;
7
+ }
8
+ const abs = resolve(process.cwd(), outputPath);
9
+ await writeFile(abs, text, "utf8");
10
+ }
11
+ //# sourceMappingURL=writeFileOrStdout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeFileOrStdout.js","sourceRoot":"","sources":["../../src/io/writeFileOrStdout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,UAA8B,EAAE,IAAY;IAC1E,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;IAC/C,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export type OutputFormat = "json" | "yaml";
2
+ export declare function stringifyOutput(value: unknown, format: OutputFormat): string;
3
+ export declare function writeOutput(value: unknown, format: OutputFormat): void;
4
+ //# sourceMappingURL=writeOutput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeOutput.d.ts","sourceRoot":"","sources":["../../src/io/writeOutput.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3C,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAM5E;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAEtE"}
@@ -0,0 +1,11 @@
1
+ import YAML from "yaml";
2
+ export function stringifyOutput(value, format) {
3
+ if (format === "yaml") {
4
+ return YAML.stringify(value);
5
+ }
6
+ return `${JSON.stringify(value, null, 2)}\n`;
7
+ }
8
+ export function writeOutput(value, format) {
9
+ process.stdout.write(stringifyOutput(value, format));
10
+ }
11
+ //# sourceMappingURL=writeOutput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writeOutput.js","sourceRoot":"","sources":["../../src/io/writeOutput.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,MAAoB;IAClE,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,MAAoB;IAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AACvD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@unispechq/unispec-platform",
3
+ "version": "0.1.0",
4
+ "description": "CLI and local orchestrator for UniSpec (validate/normalize/diff/convert/dev).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "unispec": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json",
16
+ "dev": "tsx src/cli.ts",
17
+ "start": "node dist/cli.js",
18
+ "test": "npm run build && node --test tests/*.test.mjs",
19
+ "release:patch": "node scripts/release.js patch",
20
+ "release:minor": "node scripts/release.js minor",
21
+ "release:major": "node scripts/release.js major"
22
+ },
23
+ "dependencies": {
24
+ "@unispechq/unispec-core": "^0.2.2",
25
+ "commander": "^12.1.0",
26
+ "yaml": "^2.6.1"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^24.10.1",
30
+ "tsx": "^4.19.2",
31
+ "typescript": "^5.0.0"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }