@unispechq/unispec-platform 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +21 -41
  2. package/dist/cli.js +2 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/index.d.ts +1 -2
  5. package/dist/commands/index.d.ts.map +1 -1
  6. package/dist/commands/index.js +1 -2
  7. package/dist/commands/index.js.map +1 -1
  8. package/dist/commands/portal.d.ts +3 -0
  9. package/dist/commands/portal.d.ts.map +1 -0
  10. package/dist/commands/portal.js +90 -0
  11. package/dist/commands/portal.js.map +1 -0
  12. package/package.json +1 -1
  13. package/dist/commands/dev.d.ts +0 -3
  14. package/dist/commands/dev.d.ts.map +0 -1
  15. package/dist/commands/dev.js +0 -211
  16. package/dist/commands/dev.js.map +0 -1
  17. package/dist/commands/start.d.ts +0 -3
  18. package/dist/commands/start.d.ts.map +0 -1
  19. package/dist/commands/start.js +0 -125
  20. package/dist/commands/start.js.map +0 -1
  21. package/dist/dev/aggregate.d.ts +0 -15
  22. package/dist/dev/aggregate.d.ts.map +0 -1
  23. package/dist/dev/aggregate.js +0 -56
  24. package/dist/dev/aggregate.js.map +0 -1
  25. package/dist/dev/index.d.ts +0 -3
  26. package/dist/dev/index.d.ts.map +0 -1
  27. package/dist/dev/index.js +0 -3
  28. package/dist/dev/index.js.map +0 -1
  29. package/dist/dev/server.d.ts +0 -17
  30. package/dist/dev/server.d.ts.map +0 -1
  31. package/dist/dev/server.js +0 -503
  32. package/dist/dev/server.js.map +0 -1
  33. package/dist/dev/ui.d.ts +0 -3
  34. package/dist/dev/ui.d.ts.map +0 -1
  35. package/dist/dev/ui.js +0 -93
  36. package/dist/dev/ui.js.map +0 -1
  37. package/dist/workspace/storage.d.ts +0 -7
  38. package/dist/workspace/storage.d.ts.map +0 -1
  39. package/dist/workspace/storage.js +0 -60
  40. package/dist/workspace/storage.js.map +0 -1
  41. package/dist/workspace/types.d.ts +0 -64
  42. package/dist/workspace/types.d.ts.map +0 -1
  43. package/dist/workspace/types.js +0 -2
  44. package/dist/workspace/types.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
@@ -1,15 +0,0 @@
1
- import type { UniSpecPlatformConfig } from "../config/loadConfig.js";
2
- export interface AggregatedService {
3
- name: string;
4
- specPath: string;
5
- valid: boolean;
6
- errors?: unknown;
7
- spec?: unknown;
8
- }
9
- export interface AggregateResult {
10
- version: number;
11
- generatedAt: string;
12
- services: AggregatedService[];
13
- }
14
- export declare function aggregateFromConfig(config: UniSpecPlatformConfig): Promise<AggregateResult>;
15
- //# sourceMappingURL=aggregate.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../src/dev/aggregate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGrE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0DjG"}
@@ -1,56 +0,0 @@
1
- import { loadUniSpec, normalizeUniSpec, validateUniSpec } from "@unispechq/unispec-core";
2
- import { readSpecFile } from "../io/readSpecFile.js";
3
- export async function aggregateFromConfig(config) {
4
- const version = typeof config.version === "number" ? config.version : 1;
5
- const services = Array.isArray(config.services) ? config.services : [];
6
- const results = [];
7
- for (const service of services) {
8
- const name = String(service?.name ?? "");
9
- const specPath = String(service?.spec ?? "");
10
- if (!name || !specPath) {
11
- results.push({
12
- name: name || "unknown",
13
- specPath,
14
- valid: false,
15
- errors: [{ message: "Invalid config entry: service.name and service.spec are required" }],
16
- });
17
- continue;
18
- }
19
- try {
20
- const raw = await readSpecFile(specPath);
21
- const doc = await loadUniSpec(raw);
22
- const validation = await validateUniSpec(doc);
23
- if (!validation.valid) {
24
- results.push({
25
- name,
26
- specPath,
27
- valid: false,
28
- errors: validation.errors,
29
- });
30
- continue;
31
- }
32
- const normalized = normalizeUniSpec(doc);
33
- results.push({
34
- name,
35
- specPath,
36
- valid: true,
37
- spec: normalized,
38
- });
39
- }
40
- catch (err) {
41
- const message = err instanceof Error ? err.message : String(err);
42
- results.push({
43
- name,
44
- specPath,
45
- valid: false,
46
- errors: [{ message }],
47
- });
48
- }
49
- }
50
- return {
51
- version,
52
- generatedAt: new Date().toISOString(),
53
- services: results,
54
- };
55
- }
56
- //# sourceMappingURL=aggregate.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/dev/aggregate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEzF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAgBrD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAA6B;IACrE,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvE,MAAM,OAAO,GAAwB,EAAE,CAAC;IAExC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QAE7C,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,IAAI,IAAI,SAAS;gBACvB,QAAQ;gBACR,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,kEAAkE,EAAE,CAAC;aAC1F,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;YAE9C,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;gBACtB,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI;oBACJ,QAAQ;oBACR,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;YACzC,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI;gBACJ,QAAQ;gBACR,KAAK,EAAE,IAAI;gBACX,IAAI,EAAE,UAAU;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI;gBACJ,QAAQ;gBACR,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,QAAQ,EAAE,OAAO;KAClB,CAAC;AACJ,CAAC"}
@@ -1,3 +0,0 @@
1
- export * from "./aggregate.js";
2
- export * from "./server.js";
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
package/dist/dev/index.js DELETED
@@ -1,3 +0,0 @@
1
- export * from "./aggregate.js";
2
- export * from "./server.js";
3
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
@@ -1,17 +0,0 @@
1
- import http from "node:http";
2
- import type { AggregateResult } from "./aggregate.js";
3
- import type { UniSpecPlatformConfig } from "../config/loadConfig.js";
4
- export interface DevServerOptions {
5
- host: string;
6
- port: number;
7
- getConfig: () => Promise<UniSpecPlatformConfig>;
8
- getAggregated?: () => Promise<AggregateResult>;
9
- verbose?: boolean;
10
- ui?: boolean;
11
- uiDevUrl?: string;
12
- mode?: "dev" | "start";
13
- workspaceDir?: string;
14
- configPath?: string;
15
- }
16
- export declare function startDevServer(options: DevServerOptions): Promise<http.Server>;
17
- //# sourceMappingURL=server.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dev/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAK7B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAKrE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAwcD,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CA+KpF"}
@@ -1,503 +0,0 @@
1
- import http from "node:http";
2
- import net from "node:net";
3
- import { parse as parseYaml } from "yaml";
4
- import { aggregateFromConfig } from "./aggregate.js";
5
- import { getEmbeddedIndexHtml, getEmbeddedUiJs } from "./ui.js";
6
- import { loadWorkspaceState, saveWorkspaceState } from "../workspace/storage.js";
7
- function writeJson(res, status, body) {
8
- const json = JSON.stringify(body, null, 2);
9
- res.statusCode = status;
10
- res.setHeader("content-type", "application/json; charset=utf-8");
11
- res.end(`${json}\n`);
12
- }
13
- function writeHtml(res, status, html) {
14
- res.statusCode = status;
15
- res.setHeader("content-type", "text/html; charset=utf-8");
16
- res.end(html);
17
- }
18
- function writeJs(res, status, js) {
19
- res.statusCode = status;
20
- res.setHeader("content-type", "application/javascript; charset=utf-8");
21
- res.end(js);
22
- }
23
- async function readRequestBody(req, maxBytes) {
24
- const chunks = [];
25
- let total = 0;
26
- for await (const chunk of req) {
27
- const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
28
- total += buf.length;
29
- if (total > maxBytes) {
30
- throw new Error(`Request body too large (>${maxBytes} bytes)`);
31
- }
32
- chunks.push(buf);
33
- }
34
- return Buffer.concat(chunks).toString("utf8");
35
- }
36
- function parseJsonBody(text) {
37
- if (!text.trim())
38
- return undefined;
39
- return JSON.parse(text);
40
- }
41
- function getWorkspaceInfo(options) {
42
- const workspaceDir = options.workspaceDir;
43
- if (!workspaceDir)
44
- return undefined;
45
- return {
46
- mode: options.mode ?? "dev",
47
- configPath: options.configPath,
48
- api: {
49
- unispecUrl: "/unispec.json",
50
- workspaceStateUrl: "/workspace/state",
51
- testsRunUrl: "/tests/run",
52
- },
53
- storage: {
54
- workspaceDir,
55
- workspaceFile: `${workspaceDir.replace(/\\/g, "/")}/workspace.json`,
56
- },
57
- };
58
- }
59
- function parseTestSpec(format, content) {
60
- const data = format === "yaml" ? parseYaml(content) : JSON.parse(content);
61
- if (!data || typeof data !== "object") {
62
- throw new Error("Invalid test spec: expected an object");
63
- }
64
- return data;
65
- }
66
- function interpolateString(input, vars) {
67
- return input.replace(/\{\{([a-zA-Z0-9_\-\.]+)\}\}/g, (_m, name) => {
68
- const v = vars[String(name)];
69
- return v !== undefined ? v : "";
70
- });
71
- }
72
- function interpolateUnknown(input, vars) {
73
- if (typeof input === "string") {
74
- return interpolateString(input, vars);
75
- }
76
- if (Array.isArray(input)) {
77
- return input.map((v) => interpolateUnknown(v, vars));
78
- }
79
- if (input && typeof input === "object") {
80
- const out = {};
81
- for (const [k, v] of Object.entries(input)) {
82
- out[k] = interpolateUnknown(v, vars);
83
- }
84
- return out;
85
- }
86
- return input;
87
- }
88
- function getByPath(obj, path) {
89
- if (!path)
90
- return undefined;
91
- const p = path.startsWith("$.") ? path.slice(2) : path.startsWith("$") ? path.slice(1) : path;
92
- if (!p)
93
- return obj;
94
- let cur = obj;
95
- const parts = p.split(".");
96
- for (const part of parts) {
97
- if (cur === undefined || cur === null)
98
- return undefined;
99
- const m = /^([a-zA-Z0-9_\-]+)(\[(\d+)\])?$/.exec(part);
100
- if (!m)
101
- return undefined;
102
- const key = m[1];
103
- cur = cur[key];
104
- if (m[2]) {
105
- const idx = Number(m[3]);
106
- if (!Array.isArray(cur))
107
- return undefined;
108
- cur = cur[idx];
109
- }
110
- }
111
- return cur;
112
- }
113
- function jsonContains(haystack, needle) {
114
- if (needle === undefined)
115
- return true;
116
- if (needle === null || typeof needle !== "object") {
117
- return Object.is(haystack, needle);
118
- }
119
- if (Array.isArray(needle)) {
120
- if (!Array.isArray(haystack))
121
- return false;
122
- return needle.every((n, i) => jsonContains(haystack[i], n));
123
- }
124
- if (!haystack || typeof haystack !== "object" || Array.isArray(haystack))
125
- return false;
126
- for (const [k, v] of Object.entries(needle)) {
127
- if (!jsonContains(haystack[k], v))
128
- return false;
129
- }
130
- return true;
131
- }
132
- async function executeRequest(reqSpec, baseUrl, vars) {
133
- const method = interpolateString((reqSpec.method ?? "GET").toUpperCase(), vars);
134
- let url;
135
- if (reqSpec.url) {
136
- url = interpolateString(String(reqSpec.url), vars);
137
- }
138
- else if (reqSpec.path) {
139
- if (!baseUrl) {
140
- throw new Error("Request requires baseUrl (suite.baseUrl) when using request.path");
141
- }
142
- const path = interpolateString(String(reqSpec.path), vars);
143
- url = new URL(path, baseUrl).toString();
144
- }
145
- else {
146
- throw new Error("Request must include url or path");
147
- }
148
- const headers = {};
149
- for (const [k, v] of Object.entries(reqSpec.headers ?? {})) {
150
- headers[k.toLowerCase()] = interpolateString(String(v), vars);
151
- }
152
- let body;
153
- if (reqSpec.body !== undefined) {
154
- const nextBody = interpolateUnknown(reqSpec.body, vars);
155
- body = JSON.stringify(nextBody);
156
- if (!headers["content-type"]) {
157
- headers["content-type"] = "application/json";
158
- }
159
- }
160
- const res = await fetch(url, { method, headers, body });
161
- const bodyText = await res.text();
162
- let bodyJson;
163
- try {
164
- bodyJson = bodyText ? JSON.parse(bodyText) : undefined;
165
- }
166
- catch {
167
- bodyJson = undefined;
168
- }
169
- return { res, bodyText, bodyJson };
170
- }
171
- function applyExtract(extract, response, vars) {
172
- if (!extract)
173
- return;
174
- for (const [varName, rule] of Object.entries(extract)) {
175
- let value;
176
- if (rule.from === "status") {
177
- value = response.res.status;
178
- }
179
- else if (rule.from === "header") {
180
- const name = rule.name ? String(rule.name) : "";
181
- value = name ? response.res.headers.get(name) : undefined;
182
- }
183
- else {
184
- value = getByPath(response.bodyJson, rule.path);
185
- }
186
- if (value !== undefined && value !== null) {
187
- vars[varName] = String(value);
188
- }
189
- }
190
- }
191
- function assertResponse(expect, response) {
192
- if (!expect)
193
- return { ok: true };
194
- if (expect.status !== undefined && response.res.status !== expect.status) {
195
- return { ok: false, error: { message: `Expected status ${expect.status}, got ${response.res.status}` } };
196
- }
197
- if (expect.headers) {
198
- for (const [k, v] of Object.entries(expect.headers)) {
199
- const actual = response.res.headers.get(k);
200
- if (actual !== String(v)) {
201
- return { ok: false, error: { message: `Expected header ${k}=${String(v)}, got ${actual ?? "null"}` } };
202
- }
203
- }
204
- }
205
- if (expect.bodyContains !== undefined) {
206
- if (!response.bodyText.includes(String(expect.bodyContains))) {
207
- return { ok: false, error: { message: `Expected body to contain '${String(expect.bodyContains)}'` } };
208
- }
209
- }
210
- if (expect.json !== undefined) {
211
- if (JSON.stringify(response.bodyJson) !== JSON.stringify(expect.json)) {
212
- return { ok: false, error: { message: "Expected json to equal" } };
213
- }
214
- }
215
- if (expect.jsonContains !== undefined) {
216
- if (!jsonContains(response.bodyJson, expect.jsonContains)) {
217
- return { ok: false, error: { message: "Expected json to contain" } };
218
- }
219
- }
220
- return { ok: true };
221
- }
222
- async function runSingleTest(input) {
223
- try {
224
- const spec = parseTestSpec(input.format, input.content);
225
- const vars = {};
226
- const steps = Array.isArray(spec.steps) && spec.steps.length
227
- ? spec.steps
228
- : spec.request
229
- ? [{ name: spec.steps?.[0]?.name, request: spec.request, expect: spec.expect, extract: spec.extract }]
230
- : [];
231
- if (!steps.length) {
232
- throw new Error("Test spec must include request or steps");
233
- }
234
- const stepResults = [];
235
- let lastStatus;
236
- for (let i = 0; i < steps.length; i++) {
237
- const step = steps[i];
238
- const stepName = String(step.name ?? `step_${i + 1}`);
239
- const response = await executeRequest(step.request, input.baseUrl, vars);
240
- lastStatus = response.res.status;
241
- const asserted = assertResponse(step.expect, response);
242
- if (!asserted.ok) {
243
- stepResults.push({ name: stepName, ok: false, status: response.res.status, error: asserted.error });
244
- return { id: input.id, title: input.title, ok: false, status: lastStatus, error: asserted.error, steps: stepResults };
245
- }
246
- applyExtract(step.extract, { res: response.res, bodyJson: response.bodyJson }, vars);
247
- stepResults.push({ name: stepName, ok: true, status: response.res.status });
248
- }
249
- return { id: input.id, title: input.title, ok: true, status: lastStatus, steps: stepResults };
250
- }
251
- catch (err) {
252
- const message = err instanceof Error ? err.message : String(err);
253
- return { id: input.id, title: input.title, ok: false, error: { message } };
254
- }
255
- }
256
- function shouldProxyUi(options) {
257
- return Boolean(options.uiDevUrl);
258
- }
259
- function getForwardedPort(hostHeader) {
260
- if (!hostHeader)
261
- return undefined;
262
- const idx = hostHeader.lastIndexOf(":");
263
- if (idx === -1)
264
- return undefined;
265
- const port = hostHeader.slice(idx + 1);
266
- return port ? port : undefined;
267
- }
268
- function buildProxyHeaders(target, req) {
269
- const headers = { ...req.headers };
270
- const originalHost = req.headers.host;
271
- const forwardedFor = req.socket.remoteAddress;
272
- const forwardedPort = getForwardedPort(originalHost);
273
- delete headers.host;
274
- headers["x-forwarded-host"] = originalHost;
275
- headers["x-forwarded-proto"] = "http";
276
- if (forwardedPort) {
277
- headers["x-forwarded-port"] = forwardedPort;
278
- }
279
- if (forwardedFor) {
280
- headers["x-forwarded-for"] = forwardedFor;
281
- }
282
- headers.host = target.host;
283
- return headers;
284
- }
285
- function proxyHttpRequest(target, req, res) {
286
- const headers = buildProxyHeaders(target, req);
287
- const proxyReq = http.request({
288
- protocol: target.protocol,
289
- hostname: target.hostname,
290
- port: target.port || (target.protocol === "https:" ? 443 : 80),
291
- method: req.method,
292
- path: req.url,
293
- headers,
294
- }, (proxyRes) => {
295
- res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
296
- proxyRes.pipe(res);
297
- });
298
- proxyReq.on("error", (err) => {
299
- res.statusCode = 502;
300
- res.setHeader("content-type", "application/json; charset=utf-8");
301
- res.end(JSON.stringify({ error: { message: `UI proxy error: ${err.message}` } }, null, 2) + "\n");
302
- });
303
- req.pipe(proxyReq);
304
- }
305
- function proxyWebSocketUpgrade(target, req, socket, head) {
306
- const port = Number(target.port || (target.protocol === "https:" ? 443 : 80));
307
- const upstream = net.connect(port, target.hostname, () => {
308
- const lines = [];
309
- const path = req.url ?? "/";
310
- lines.push(`${req.method ?? "GET"} ${path} HTTP/1.1`);
311
- const originalHost = req.headers.host;
312
- const forwardedFor = req.socket.remoteAddress;
313
- const forwardedPort = getForwardedPort(originalHost);
314
- const targetHost = target.host || `${target.hostname}:${port}`;
315
- const headers = { ...req.headers };
316
- headers.host = targetHost;
317
- headers["x-forwarded-host"] = originalHost;
318
- headers["x-forwarded-proto"] = "http";
319
- if (forwardedPort)
320
- headers["x-forwarded-port"] = forwardedPort;
321
- if (forwardedFor)
322
- headers["x-forwarded-for"] = forwardedFor;
323
- for (const [k, v] of Object.entries(headers)) {
324
- if (v === undefined)
325
- continue;
326
- if (Array.isArray(v)) {
327
- for (const vv of v)
328
- lines.push(`${k}: ${vv}`);
329
- }
330
- else {
331
- lines.push(`${k}: ${v}`);
332
- }
333
- }
334
- lines.push("\r\n");
335
- upstream.write(lines.join("\r\n"));
336
- if (head && head.length) {
337
- upstream.write(head);
338
- }
339
- socket.pipe(upstream);
340
- upstream.pipe(socket);
341
- });
342
- upstream.on("error", () => {
343
- try {
344
- socket.end();
345
- }
346
- catch {
347
- // ignore
348
- }
349
- });
350
- }
351
- export async function startDevServer(options) {
352
- const uiEnabled = options.ui ?? true;
353
- const uiDevUrl = options.uiDevUrl ? new URL(options.uiDevUrl) : undefined;
354
- const workspaceInfo = getWorkspaceInfo(options);
355
- const server = http.createServer(async (req, res) => {
356
- const method = req.method ?? "GET";
357
- const url = req.url ?? "/";
358
- if (method === "GET" && url === "/health") {
359
- res.statusCode = 200;
360
- res.setHeader("content-type", "text/plain; charset=utf-8");
361
- res.end("OK\n");
362
- return;
363
- }
364
- if (method === "GET" && url === "/unispec.json") {
365
- try {
366
- const aggregated = options.getAggregated
367
- ? await options.getAggregated()
368
- : await aggregateFromConfig(await options.getConfig());
369
- const hasErrors = aggregated.services.some((s) => !s.valid);
370
- writeJson(res, hasErrors ? 422 : 200, aggregated);
371
- }
372
- catch (err) {
373
- const message = err instanceof Error ? err.message : String(err);
374
- writeJson(res, 500, { error: { message } });
375
- }
376
- return;
377
- }
378
- if (method === "GET" && url === "/workspace.json") {
379
- if (!workspaceInfo) {
380
- writeJson(res, 404, { error: { message: "Workspace storage is not configured" } });
381
- return;
382
- }
383
- writeJson(res, 200, workspaceInfo);
384
- return;
385
- }
386
- if (url === "/workspace/state") {
387
- if (!workspaceInfo) {
388
- writeJson(res, 404, { error: { message: "Workspace storage is not configured" } });
389
- return;
390
- }
391
- if (method === "GET") {
392
- const state = await loadWorkspaceState(workspaceInfo.storage.workspaceDir);
393
- writeJson(res, 200, state);
394
- return;
395
- }
396
- if (method === "PUT") {
397
- try {
398
- const raw = await readRequestBody(req, 2 * 1024 * 1024);
399
- const parsed = parseJsonBody(raw);
400
- if (!parsed || typeof parsed !== "object") {
401
- writeJson(res, 400, { error: { message: "Invalid JSON body" } });
402
- return;
403
- }
404
- const next = parsed;
405
- await saveWorkspaceState(workspaceInfo.storage.workspaceDir, next);
406
- const saved = await loadWorkspaceState(workspaceInfo.storage.workspaceDir);
407
- writeJson(res, 200, saved);
408
- }
409
- catch (err) {
410
- const message = err instanceof Error ? err.message : String(err);
411
- writeJson(res, 500, { error: { message } });
412
- }
413
- return;
414
- }
415
- writeJson(res, 405, { error: { message: "Method not allowed" } });
416
- return;
417
- }
418
- if (method === "POST" && url === "/tests/run") {
419
- if (!workspaceInfo) {
420
- writeJson(res, 404, { error: { message: "Workspace storage is not configured" } });
421
- return;
422
- }
423
- try {
424
- const raw = await readRequestBody(req, 2 * 1024 * 1024);
425
- const payload = (parseJsonBody(raw) ?? {});
426
- const suiteId = payload.suiteId ? String(payload.suiteId) : undefined;
427
- const testIds = Array.isArray(payload.testIds) ? payload.testIds.map((x) => String(x)) : undefined;
428
- const state = await loadWorkspaceState(workspaceInfo.storage.workspaceDir);
429
- let suiteBaseUrl;
430
- let runList = state.tests;
431
- if (suiteId) {
432
- const suite = state.testSuites.find((s) => s.id === suiteId);
433
- if (!suite) {
434
- writeJson(res, 404, { error: { message: `Suite not found: ${suiteId}` } });
435
- return;
436
- }
437
- suiteBaseUrl = suite.baseUrl;
438
- runList = suite.tests
439
- .map((id) => state.tests.find((t) => t.id === id))
440
- .filter((t) => Boolean(t));
441
- }
442
- if (testIds && testIds.length) {
443
- runList = testIds
444
- .map((id) => state.tests.find((t) => t.id === id))
445
- .filter((t) => Boolean(t));
446
- }
447
- const results = [];
448
- for (const t of runList) {
449
- results.push(await runSingleTest({
450
- id: String(t.id),
451
- title: String(t.title ?? t.id),
452
- format: t.format === "json" ? "json" : "yaml",
453
- content: String(t.content ?? ""),
454
- baseUrl: suiteBaseUrl,
455
- }));
456
- }
457
- const ok = results.every((r) => r.ok);
458
- writeJson(res, ok ? 200 : 422, {
459
- suiteId: suiteId ?? null,
460
- ok,
461
- results,
462
- });
463
- }
464
- catch (err) {
465
- const message = err instanceof Error ? err.message : String(err);
466
- writeJson(res, 500, { error: { message } });
467
- }
468
- return;
469
- }
470
- if (uiEnabled) {
471
- if (uiDevUrl && shouldProxyUi(options)) {
472
- proxyHttpRequest(uiDevUrl, req, res);
473
- return;
474
- }
475
- if (method === "GET" && (url === "/" || url === "/index.html")) {
476
- writeHtml(res, 200, getEmbeddedIndexHtml());
477
- return;
478
- }
479
- if (method === "GET" && url === "/ui.js") {
480
- writeJs(res, 200, getEmbeddedUiJs());
481
- return;
482
- }
483
- }
484
- writeJson(res, 404, { error: { message: "Not found" } });
485
- });
486
- server.on("upgrade", (req, socket, head) => {
487
- const uiDev = uiDevUrl;
488
- if (!uiDev || !uiEnabled) {
489
- socket.destroy();
490
- return;
491
- }
492
- proxyWebSocketUpgrade(uiDev, req, socket, head);
493
- });
494
- await new Promise((resolve, reject) => {
495
- server.once("error", reject);
496
- server.listen(options.port, options.host, () => resolve());
497
- });
498
- if (options.verbose) {
499
- process.stderr.write(`Dev server listening on http://${options.host}:${options.port}\n`);
500
- }
501
- return server;
502
- }
503
- //# sourceMappingURL=server.js.map