@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.
- package/README.md +225 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +36 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/convert.d.ts +3 -0
- package/dist/commands/convert.d.ts.map +1 -0
- package/dist/commands/convert.js +55 -0
- package/dist/commands/convert.js.map +1 -0
- package/dist/commands/dev.d.ts +3 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +108 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/diff.d.ts +3 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +32 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/normalize.d.ts +3 -0
- package/dist/commands/normalize.d.ts.map +1 -0
- package/dist/commands/normalize.js +30 -0
- package/dist/commands/normalize.js.map +1 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +31 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/config/loadConfig.d.ts +13 -0
- package/dist/config/loadConfig.d.ts.map +1 -0
- package/dist/config/loadConfig.js +42 -0
- package/dist/config/loadConfig.js.map +1 -0
- package/dist/dev/aggregate.d.ts +15 -0
- package/dist/dev/aggregate.d.ts.map +1 -0
- package/dist/dev/aggregate.js +56 -0
- package/dist/dev/aggregate.js.map +1 -0
- package/dist/dev/index.d.ts +3 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +3 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/server.d.ts +12 -0
- package/dist/dev/server.d.ts.map +1 -0
- package/dist/dev/server.js +44 -0
- package/dist/dev/server.js.map +1 -0
- package/dist/io/index.d.ts +6 -0
- package/dist/io/index.d.ts.map +1 -0
- package/dist/io/index.js +6 -0
- package/dist/io/index.js.map +1 -0
- package/dist/io/readSpecFile.d.ts +2 -0
- package/dist/io/readSpecFile.d.ts.map +1 -0
- package/dist/io/readSpecFile.js +27 -0
- package/dist/io/readSpecFile.js.map +1 -0
- package/dist/io/readSpecInput.d.ts +6 -0
- package/dist/io/readSpecInput.d.ts.map +1 -0
- package/dist/io/readSpecInput.js +54 -0
- package/dist/io/readSpecInput.js.map +1 -0
- package/dist/io/readStdin.d.ts +2 -0
- package/dist/io/readStdin.d.ts.map +1 -0
- package/dist/io/readStdin.js +17 -0
- package/dist/io/readStdin.js.map +1 -0
- package/dist/io/readStdinText.d.ts +2 -0
- package/dist/io/readStdinText.d.ts.map +1 -0
- package/dist/io/readStdinText.js +15 -0
- package/dist/io/readStdinText.js.map +1 -0
- package/dist/io/writeFileOrStdout.d.ts +2 -0
- package/dist/io/writeFileOrStdout.d.ts.map +1 -0
- package/dist/io/writeFileOrStdout.js +11 -0
- package/dist/io/writeFileOrStdout.js.map +1 -0
- package/dist/io/writeOutput.d.ts +4 -0
- package/dist/io/writeOutput.d.ts.map +1 -0
- package/dist/io/writeOutput.js +11 -0
- package/dist/io/writeOutput.js.map +1 -0
- 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 @@
|
|
|
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
|
package/dist/cli.js.map
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/io/index.js
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|
+
}
|