@voltro/plugin-openapi 0.69.1 → 0.71.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/CHANGELOG.md +1369 -111
- package/dist/index.d.ts +146 -2
- package/dist/index.js +448 -91
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,65 +1,66 @@
|
|
|
1
1
|
import { Effect as e, JSONSchema as t } from "effect";
|
|
2
2
|
import { definePlugin as n } from "@voltro/protocol";
|
|
3
3
|
import { pluginEnv as r } from "@voltro/env";
|
|
4
|
+
import { publicSurfaceDigest as i } from "@voltro/protocol/rest";
|
|
4
5
|
//#region src/rpc.ts
|
|
5
|
-
var
|
|
6
|
+
var a = "/rpc", o = "rpc", s = (e) => e.ast._tag === "NeverKeyword", c = (e, t) => `${e} ${t}`, l = (e) => `Voltro rpc ${e}. This \`/rpc/<name>\` path is a documentation projection of the procedure's input/output schemas — live calls go through ${e === "query" || e === "stream" ? "the batched `POST /rpc` endpoint (first snapshot / element) or the WebSocket rpc channel (reactive/streaming)" : "the batched `POST /rpc` endpoint"}, NOT a REST request to this path. The request body below is the rpc payload.`, u = (e, t) => {
|
|
6
7
|
let { kind: n, name: r } = e, i = {
|
|
7
8
|
required: !0,
|
|
8
|
-
content: { "application/json": { schema:
|
|
9
|
-
},
|
|
9
|
+
content: { "application/json": { schema: p(e.input, t) } }
|
|
10
|
+
}, a = (() => {
|
|
10
11
|
if (n === "stream") return {
|
|
11
12
|
description: "Stream of elements. The rpc transport pushes each element as it is produced (WebSocket) or drains to the first element over `POST /rpc`. The schema below is ONE element, not the whole response.",
|
|
12
|
-
content: { "application/x-ndjson": { schema:
|
|
13
|
+
content: { "application/x-ndjson": { schema: p(e.element, t) } }
|
|
13
14
|
};
|
|
14
|
-
let r =
|
|
15
|
+
let r = p(e.output, t);
|
|
15
16
|
return {
|
|
16
17
|
description: n === "query" ? "OK — the query snapshot (the first/initial snapshot over `POST /rpc`; the WebSocket channel then pushes reactive updates)." : "OK",
|
|
17
18
|
content: { "application/json": { schema: r } }
|
|
18
19
|
};
|
|
19
|
-
})(), u =
|
|
20
|
+
})(), u = s(e.error) ? {} : { 422: {
|
|
20
21
|
description: "A typed error from the procedure — one of its declared error variants (the rpc failure channel).",
|
|
21
|
-
content: { "application/json": { schema:
|
|
22
|
+
content: { "application/json": { schema: p(e.error, t) } }
|
|
22
23
|
} };
|
|
23
24
|
return {
|
|
24
|
-
tags: [
|
|
25
|
-
summary:
|
|
26
|
-
description:
|
|
25
|
+
tags: [o],
|
|
26
|
+
summary: c(n, r),
|
|
27
|
+
description: l(n),
|
|
27
28
|
"x-voltro-rpc": {
|
|
28
29
|
kind: n,
|
|
29
30
|
procedure: r
|
|
30
31
|
},
|
|
31
32
|
requestBody: i,
|
|
32
33
|
responses: {
|
|
33
|
-
200:
|
|
34
|
+
200: a,
|
|
34
35
|
...u,
|
|
35
|
-
500:
|
|
36
|
+
500: v("Internal Server Error — an untyped defect.")
|
|
36
37
|
}
|
|
37
38
|
};
|
|
38
|
-
},
|
|
39
|
+
}, d = (e, t) => {
|
|
39
40
|
let n = {};
|
|
40
41
|
for (let r of e) {
|
|
41
|
-
let e = `${
|
|
42
|
-
n[e] = { post:
|
|
42
|
+
let e = `${a}/${r.name}`;
|
|
43
|
+
n[e] = { post: u(r, t) };
|
|
43
44
|
}
|
|
44
45
|
return n;
|
|
45
|
-
},
|
|
46
|
+
}, f = (e) => e.replace(/:([A-Za-z0-9_]+)/g, "{$1}"), p = (e, n) => {
|
|
46
47
|
let { $schema: r, $defs: i, ...a } = t.make(e);
|
|
47
48
|
if (i && typeof i == "object") for (let [e, t] of Object.entries(i)) n[e] = t;
|
|
48
49
|
return a;
|
|
49
|
-
},
|
|
50
|
-
if (Array.isArray(e)) return e.map(
|
|
50
|
+
}, m = (e) => {
|
|
51
|
+
if (Array.isArray(e)) return e.map(m);
|
|
51
52
|
if (e && typeof e == "object") {
|
|
52
53
|
let t = {};
|
|
53
|
-
for (let [n, r] of Object.entries(e)) t[n] = n === "$ref" && typeof r == "string" && r.startsWith("#/$defs/") ? `#/components/schemas/${r.slice(8)}` :
|
|
54
|
+
for (let [n, r] of Object.entries(e)) t[n] = n === "$ref" && typeof r == "string" && r.startsWith("#/$defs/") ? `#/components/schemas/${r.slice(8)}` : m(r);
|
|
54
55
|
return t;
|
|
55
56
|
}
|
|
56
57
|
return e;
|
|
57
|
-
},
|
|
58
|
+
}, h = (e, t) => {
|
|
58
59
|
if (!e) return {
|
|
59
60
|
parameters: [],
|
|
60
61
|
requestBody: void 0
|
|
61
62
|
};
|
|
62
|
-
let n =
|
|
63
|
+
let n = p(e, t), r = n.properties ?? {}, i = new Set(n.required ?? []), a = [];
|
|
63
64
|
for (let [e, t] of [["query", "query"], ["params", "path"]]) {
|
|
64
65
|
let n = r[e];
|
|
65
66
|
if (!n || typeof n != "object") continue;
|
|
@@ -79,7 +80,7 @@ var i = "/rpc", a = "rpc", o = (e) => e.ast._tag === "NeverKeyword", s = (e, t)
|
|
|
79
80
|
content: { "application/json": { schema: o } }
|
|
80
81
|
} : void 0
|
|
81
82
|
};
|
|
82
|
-
},
|
|
83
|
+
}, g = "VoltroHttpError", _ = {
|
|
83
84
|
type: "object",
|
|
84
85
|
description: "The serve pipeline's error envelope.",
|
|
85
86
|
properties: {
|
|
@@ -97,79 +98,351 @@ var i = "/rpc", a = "rpc", o = (e) => e.ast._tag === "NeverKeyword", s = (e, t)
|
|
|
97
98
|
}
|
|
98
99
|
},
|
|
99
100
|
required: ["error"]
|
|
100
|
-
},
|
|
101
|
+
}, v = (e) => ({
|
|
101
102
|
description: e,
|
|
102
|
-
content: { "application/json": { schema: { $ref: `#/components/schemas/${
|
|
103
|
-
}),
|
|
104
|
-
|
|
103
|
+
content: { "application/json": { schema: { $ref: `#/components/schemas/${g}` } } }
|
|
104
|
+
}), y = "ProblemDetails", b = {
|
|
105
|
+
type: "object",
|
|
106
|
+
description: "RFC 9457 Problem Details. Extension members carry the error's own fields.",
|
|
107
|
+
properties: {
|
|
108
|
+
type: {
|
|
109
|
+
type: "string",
|
|
110
|
+
format: "uri",
|
|
111
|
+
description: "The error type — `urn:voltro:error:<Tag>`."
|
|
112
|
+
},
|
|
113
|
+
title: { type: "string" },
|
|
114
|
+
status: { type: "integer" },
|
|
115
|
+
detail: { type: "string" }
|
|
116
|
+
},
|
|
117
|
+
required: [
|
|
118
|
+
"type",
|
|
119
|
+
"title",
|
|
120
|
+
"status"
|
|
121
|
+
],
|
|
122
|
+
additionalProperties: !0
|
|
123
|
+
}, x = (e, t, n = 0) => {
|
|
124
|
+
if (n > 6) return null;
|
|
125
|
+
let r = e.$ref;
|
|
126
|
+
if (typeof r == "string") {
|
|
127
|
+
let e = t[r.split("/").pop() ?? ""];
|
|
128
|
+
return e !== void 0 && typeof e == "object" ? x(e, t, n + 1) : null;
|
|
129
|
+
}
|
|
130
|
+
if (e.example !== void 0) return e.example;
|
|
131
|
+
if (Array.isArray(e.examples) && e.examples.length > 0) return e.examples[0];
|
|
132
|
+
if (e.const !== void 0) return e.const;
|
|
133
|
+
if (Array.isArray(e.enum) && e.enum.length > 0) return e.enum[0];
|
|
134
|
+
let i = e.anyOf ?? e.oneOf;
|
|
135
|
+
if (Array.isArray(i) && i.length > 0) return x(i.find((e) => e.type !== "null") ?? i[0], t, n + 1);
|
|
136
|
+
let a = e.allOf;
|
|
137
|
+
if (Array.isArray(a) && a.length > 0) {
|
|
138
|
+
let e = a.map((e) => x(e, t, n + 1));
|
|
139
|
+
return e.every((e) => typeof e == "object" && !!e && !Array.isArray(e)) ? Object.assign({}, ...e) : e[0];
|
|
140
|
+
}
|
|
141
|
+
let o = e.type;
|
|
142
|
+
switch (Array.isArray(o) ? o.find((e) => e !== "null") : o) {
|
|
143
|
+
case "string": {
|
|
144
|
+
let t = e.format;
|
|
145
|
+
if (t === "date-time") return "2026-01-01T00:00:00.000Z";
|
|
146
|
+
if (t === "date") return "2026-01-01";
|
|
147
|
+
if (t === "email") return "user@example.com";
|
|
148
|
+
if (t === "uri" || t === "url") return "https://example.com";
|
|
149
|
+
if (t === "uuid") return "00000000-0000-4000-8000-000000000000";
|
|
150
|
+
let n = typeof e.minLength == "number" ? e.minLength : 0;
|
|
151
|
+
return "string".padEnd(Math.max(n, 6), "x");
|
|
152
|
+
}
|
|
153
|
+
case "integer": return typeof e.minimum == "number" ? e.minimum : 1;
|
|
154
|
+
case "number": return typeof e.minimum == "number" ? e.minimum : 1;
|
|
155
|
+
case "boolean": return !0;
|
|
156
|
+
case "null": return null;
|
|
157
|
+
case "array": {
|
|
158
|
+
let r = e.items;
|
|
159
|
+
return r !== void 0 && typeof r == "object" && !Array.isArray(r) ? [x(r, t, n + 1)] : [];
|
|
160
|
+
}
|
|
161
|
+
case "object": {
|
|
162
|
+
let r = e.properties ?? {}, i = {};
|
|
163
|
+
for (let [e, a] of Object.entries(r)) i[e] = x(a, t, n + 1);
|
|
164
|
+
return i;
|
|
165
|
+
}
|
|
166
|
+
default: return typeof e.properties == "object" ? x({
|
|
167
|
+
...e,
|
|
168
|
+
type: "object"
|
|
169
|
+
}, t, n) : null;
|
|
170
|
+
}
|
|
171
|
+
}, S = {
|
|
172
|
+
400: {
|
|
173
|
+
tag: "InvalidRequest",
|
|
174
|
+
detail: "Invalid request"
|
|
175
|
+
},
|
|
176
|
+
401: {
|
|
177
|
+
tag: "Unauthorized",
|
|
178
|
+
detail: "Authentication required"
|
|
179
|
+
},
|
|
180
|
+
403: {
|
|
181
|
+
tag: "Forbidden",
|
|
182
|
+
detail: "Missing required scope: <scope>"
|
|
183
|
+
},
|
|
184
|
+
410: {
|
|
185
|
+
tag: "Gone",
|
|
186
|
+
detail: "Gone"
|
|
187
|
+
},
|
|
188
|
+
500: {
|
|
189
|
+
tag: "InternalServerError",
|
|
190
|
+
detail: "Internal Server Error"
|
|
191
|
+
}
|
|
192
|
+
}, C = {
|
|
193
|
+
400: "Bad Request",
|
|
194
|
+
401: "Unauthorized",
|
|
195
|
+
403: "Forbidden",
|
|
196
|
+
404: "Not Found",
|
|
197
|
+
409: "Conflict",
|
|
198
|
+
410: "Gone",
|
|
199
|
+
422: "Unprocessable Content",
|
|
200
|
+
429: "Too Many Requests",
|
|
201
|
+
500: "Internal Server Error"
|
|
202
|
+
}, w = (e, t, n) => {
|
|
203
|
+
let r = t.format.errors === "problem-details" && t.format.encodeError === void 0, i = r ? "application/problem+json" : "application/json", a = /* @__PURE__ */ new Map();
|
|
204
|
+
for (let e of t.errorResponses) {
|
|
205
|
+
let t = a.get(e.status) ?? [], i = p(e.schema, n), o = x(i, n), { _tag: s, ...c } = typeof o == "object" && o ? o : {}, l = r ? {
|
|
206
|
+
type: `urn:voltro:error:${e.tag}`,
|
|
207
|
+
title: e.tag.replace(/([a-z0-9])([A-Z])/g, "$1 $2"),
|
|
208
|
+
status: e.status,
|
|
209
|
+
...c
|
|
210
|
+
} : {
|
|
211
|
+
error: e.tag,
|
|
212
|
+
...c
|
|
213
|
+
};
|
|
214
|
+
t.push({
|
|
215
|
+
tag: e.tag,
|
|
216
|
+
example: l,
|
|
217
|
+
schema: r ? { allOf: [{ $ref: `#/components/schemas/${y}` }, {
|
|
218
|
+
type: "object",
|
|
219
|
+
properties: { type: { const: `urn:voltro:error:${e.tag}` } }
|
|
220
|
+
}] } : i
|
|
221
|
+
}), a.set(e.status, t);
|
|
222
|
+
}
|
|
223
|
+
let o = (e, t) => {
|
|
224
|
+
if (!r) return v(t);
|
|
225
|
+
let n = S[e], a = n === void 0 ? {} : { example: {
|
|
226
|
+
type: `urn:voltro:error:${n.tag}`,
|
|
227
|
+
title: n.tag.replace(/([a-z])([A-Z])/g, "$1 $2"),
|
|
228
|
+
status: e,
|
|
229
|
+
detail: n.detail
|
|
230
|
+
} };
|
|
231
|
+
return {
|
|
232
|
+
description: t,
|
|
233
|
+
content: { [i]: {
|
|
234
|
+
schema: { $ref: `#/components/schemas/${y}` },
|
|
235
|
+
...a
|
|
236
|
+
} }
|
|
237
|
+
};
|
|
238
|
+
}, s = {};
|
|
239
|
+
s[400] = o(400, "Invalid request — the input failed schema decode."), t.security !== "none" && (s[401] = o(401, "Unauthorized — no valid bearer.")), (t.security === "required" || (e.guards?.length ?? 0) > 0) && (s[403] = o(403, "Forbidden — the caller lacks a required scope."));
|
|
240
|
+
for (let [e, t] of [...a.entries()].sort((e, t) => e[0] - t[0])) {
|
|
241
|
+
let n = t.map((e) => e.tag);
|
|
242
|
+
s[String(e)] = {
|
|
243
|
+
description: `${C[e] ?? "Error"} — ${n.join(" | ")}`,
|
|
244
|
+
content: { [i]: {
|
|
245
|
+
schema: t.length === 1 ? t[0].schema : { oneOf: t.map((e) => e.schema) },
|
|
246
|
+
example: t[0].example
|
|
247
|
+
} }
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return e.sunset !== void 0 && (s[410] = o(410, `Gone — the route is past its sunset date (${e.sunset}).`)), s[500] = o(500, "Internal Server Error."), s;
|
|
251
|
+
}, T = (e, t, n) => {
|
|
252
|
+
let r = p(t.input, n), i = r.properties ?? {}, a = new Set(r.required ?? []), o = new Set(t.pathParams), s = [], c = {}, l = [], u = e.example?.request, d = (e) => typeof u == "object" && u && e in u ? { example: u[e] } : {};
|
|
253
|
+
for (let [t, n] of Object.entries(i)) o.has(t) ? s.push({
|
|
254
|
+
name: t,
|
|
255
|
+
in: "path",
|
|
256
|
+
required: !0,
|
|
257
|
+
schema: n,
|
|
258
|
+
...d(t)
|
|
259
|
+
}) : e.method === "GET" ? s.push({
|
|
260
|
+
name: t,
|
|
261
|
+
in: "query",
|
|
262
|
+
required: a.has(t),
|
|
263
|
+
schema: n,
|
|
264
|
+
...d(t)
|
|
265
|
+
}) : (c[t] = n, a.has(t) && l.push(t));
|
|
266
|
+
return {
|
|
267
|
+
parameters: s,
|
|
268
|
+
requestBody: e.method !== "GET" && Object.keys(c).length > 0 ? {
|
|
269
|
+
required: l.length > 0,
|
|
270
|
+
content: {
|
|
271
|
+
"application/json": { schema: {
|
|
272
|
+
...r,
|
|
273
|
+
properties: c,
|
|
274
|
+
required: l
|
|
275
|
+
} },
|
|
276
|
+
"application/x-www-form-urlencoded": { schema: {
|
|
277
|
+
...r,
|
|
278
|
+
properties: c,
|
|
279
|
+
required: l
|
|
280
|
+
} }
|
|
281
|
+
}
|
|
282
|
+
} : void 0
|
|
283
|
+
};
|
|
284
|
+
}, E = (e, t, n) => {
|
|
285
|
+
if (e.streaming === !0) return {
|
|
286
|
+
description: "Server-Sent Events stream (`snapshot`, then `delta` per change)",
|
|
287
|
+
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
288
|
+
};
|
|
289
|
+
let r = p(t.output, n), i = t.format, a = i.pagination === "link-header" && D(r, n, i.cursorField), o = i.encodeSuccess === void 0 ? i.envelope === "data" ? {
|
|
290
|
+
type: "object",
|
|
291
|
+
properties: {
|
|
292
|
+
data: r,
|
|
293
|
+
...a ? { next: {
|
|
294
|
+
type: "string",
|
|
295
|
+
description: "The next page's cursor, when there is one."
|
|
296
|
+
} } : {}
|
|
297
|
+
},
|
|
298
|
+
required: ["data"]
|
|
299
|
+
} : i.envelope === "jsonapi" ? {
|
|
300
|
+
type: "object",
|
|
301
|
+
properties: {
|
|
302
|
+
data: { description: "One resource or an array of resources — `{ type, id, attributes }`." },
|
|
303
|
+
links: {
|
|
304
|
+
type: "object",
|
|
305
|
+
properties: { next: { type: "string" } }
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
required: ["data"]
|
|
309
|
+
} : r : { description: `Encoded by the '${i.name}' format.` }, s = i.envelope === "jsonapi" ? "application/vnd.api+json" : "application/json", c = {
|
|
310
|
+
...a ? { Link: {
|
|
311
|
+
description: "RFC 8288 — `<url>; rel=\"next\"` when there is a next page.",
|
|
312
|
+
schema: { type: "string" }
|
|
313
|
+
} } : {},
|
|
314
|
+
...e.etag === !0 ? { ETag: {
|
|
315
|
+
description: "Weak validator; send it back as `If-None-Match` for a 304.",
|
|
316
|
+
schema: { type: "string" }
|
|
317
|
+
} } : {}
|
|
318
|
+
};
|
|
319
|
+
return {
|
|
320
|
+
description: "OK",
|
|
321
|
+
...Object.keys(c).length > 0 ? { headers: c } : {},
|
|
322
|
+
content: { [s]: {
|
|
323
|
+
schema: o,
|
|
324
|
+
...O(e.example?.response, o, n)
|
|
325
|
+
} }
|
|
326
|
+
};
|
|
327
|
+
}, D = (e, t, n) => {
|
|
328
|
+
let r = e.$ref, i = (typeof r == "string" ? t[r.split("/").pop() ?? ""] : e)?.properties;
|
|
329
|
+
return typeof i == "object" && !!i && n in i;
|
|
330
|
+
}, O = (e, t, n) => e === void 0 ? {
|
|
331
|
+
example: x(t, n),
|
|
332
|
+
"x-voltro-example": "derived"
|
|
333
|
+
} : { example: e }, k = (e) => ({
|
|
334
|
+
...e.input === void 0 ? {} : { 400: v("Invalid request — the input failed schema decode.") },
|
|
105
335
|
...e.guards !== void 0 && e.guards.length > 0 ? {
|
|
106
|
-
401:
|
|
107
|
-
403:
|
|
336
|
+
401: v("Unauthorized — a guard rejected the unauthenticated request."),
|
|
337
|
+
403: v("Forbidden — a guard rejected the request (e.g. a required scope is missing).")
|
|
108
338
|
} : {},
|
|
109
|
-
...e.sunset === void 0 ? {} : { 410:
|
|
110
|
-
500:
|
|
339
|
+
...e.sunset === void 0 ? {} : { 410: v(`Gone — the route is past its sunset date (${e.sunset}).`) },
|
|
340
|
+
500: v("Internal Server Error.")
|
|
111
341
|
});
|
|
112
|
-
function
|
|
113
|
-
let
|
|
342
|
+
function A(e, t = {}, n = {}, r = {}) {
|
|
343
|
+
let a = {}, o = {}, s = e.some((e) => e.guards !== void 0 && e.guards.length > 0), c = e.some((e) => e.projection !== void 0 && e.projection.security !== "none"), l = e.some((e) => e.projection?.format.errors === "problem-details" && e.projection.format.encodeError === void 0), u = /* @__PURE__ */ new Set();
|
|
114
344
|
for (let t of e) {
|
|
115
|
-
let { parameters:
|
|
116
|
-
|
|
117
|
-
|
|
345
|
+
let e = t.projection, { parameters: n, requestBody: r } = e === void 0 ? h(t.input, o) : T(t, e, o), i = t.etag === !0 && t.method === "GET" && t.streaming !== !0, s = i ? [...n, {
|
|
346
|
+
name: "If-None-Match",
|
|
347
|
+
in: "header",
|
|
348
|
+
required: !1,
|
|
349
|
+
description: "A previously returned ETag. A matching validator returns 304 without a body.",
|
|
350
|
+
schema: { type: "string" }
|
|
351
|
+
}] : n, c = i ? { 304: {
|
|
352
|
+
description: "Not Modified",
|
|
353
|
+
headers: { ETag: {
|
|
354
|
+
description: "The current representation validator.",
|
|
355
|
+
schema: { type: "string" }
|
|
356
|
+
} }
|
|
357
|
+
} } : {}, l = t.guards !== void 0 && t.guards.length > 0;
|
|
358
|
+
if (r !== void 0) {
|
|
359
|
+
let e = r.content;
|
|
118
360
|
e["application/json"] = {
|
|
119
361
|
...e["application/json"],
|
|
120
|
-
|
|
362
|
+
...O(t.example?.request, e["application/json"].schema, o)
|
|
121
363
|
};
|
|
122
364
|
}
|
|
123
|
-
let
|
|
365
|
+
let d = e === void 0 ? l ? { security: [{ bearerAuth: [] }] } : {} : e.security === "required" ? { security: [{ bearerAuth: [] }] } : e.security === "optional" ? { security: [{ bearerAuth: [] }, {}] } : {}, m = t.tags ?? (t.version === void 0 ? void 0 : [t.version]);
|
|
366
|
+
for (let e of m ?? []) u.add(e);
|
|
367
|
+
let g = e?.version ?? t.version, _ = {
|
|
368
|
+
...t.operationId === void 0 ? {} : { operationId: t.operationId },
|
|
124
369
|
...t.summary ? { summary: t.summary } : {},
|
|
125
370
|
...t.description ? { description: t.description } : {},
|
|
126
371
|
...t.deprecated === void 0 ? {} : { deprecated: !0 },
|
|
127
372
|
...t.sunset === void 0 ? {} : { "x-sunset": t.sunset },
|
|
128
|
-
...
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
},
|
|
132
|
-
...
|
|
133
|
-
|
|
134
|
-
...a ? { security: [{ bearerAuth: [] }] } : {},
|
|
135
|
-
responses: {
|
|
373
|
+
...m === void 0 ? {} : { tags: m },
|
|
374
|
+
...g === void 0 ? {} : { "x-voltro-api-version": g },
|
|
375
|
+
...s.length > 0 ? { parameters: s } : {},
|
|
376
|
+
...r ? { requestBody: r } : {},
|
|
377
|
+
...d,
|
|
378
|
+
responses: e === void 0 ? {
|
|
136
379
|
200: t.streaming === !0 ? {
|
|
137
380
|
description: "Server-Sent Events stream (`snapshot`, then `delta` per change)",
|
|
138
381
|
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
139
382
|
} : {
|
|
140
383
|
description: "OK",
|
|
141
384
|
content: { "application/json": {
|
|
142
|
-
schema:
|
|
143
|
-
...t.example?.response
|
|
385
|
+
schema: p(t.output, o),
|
|
386
|
+
...O(t.example?.response, p(t.output, o), o)
|
|
144
387
|
} }
|
|
145
388
|
},
|
|
146
|
-
...
|
|
389
|
+
...k(t),
|
|
390
|
+
...c
|
|
391
|
+
} : {
|
|
392
|
+
200: E(t, e, o),
|
|
393
|
+
...c,
|
|
394
|
+
...w(t, e, o)
|
|
147
395
|
}
|
|
148
|
-
}
|
|
149
|
-
|
|
396
|
+
};
|
|
397
|
+
e?.format.describe !== void 0 && (_ = e.format.describe(_, {
|
|
398
|
+
tag: e.tag,
|
|
399
|
+
method: t.method,
|
|
400
|
+
path: t.path
|
|
401
|
+
}));
|
|
402
|
+
let v = f(t.path), y = a[v] ?? {};
|
|
403
|
+
y[t.method.toLowerCase()] = _, a[v] = y;
|
|
150
404
|
}
|
|
151
|
-
let
|
|
152
|
-
if (
|
|
153
|
-
let e =
|
|
154
|
-
for (let [t, n] of Object.entries(e))
|
|
405
|
+
let v = n.procedures ?? [];
|
|
406
|
+
if (v.length > 0) {
|
|
407
|
+
let e = d(v, o);
|
|
408
|
+
for (let [t, n] of Object.entries(e)) a[t] = {
|
|
155
409
|
...n,
|
|
156
|
-
...
|
|
410
|
+
...a[t] ?? {}
|
|
157
411
|
};
|
|
158
412
|
}
|
|
413
|
+
let x = [...u].sort().map((e) => ({
|
|
414
|
+
name: e,
|
|
415
|
+
...r.tagDescriptions?.[e] === void 0 ? {} : { description: r.tagDescriptions[e] },
|
|
416
|
+
...r.tagExternalDocs?.[e] === void 0 ? {} : { externalDocs: r.tagExternalDocs[e] }
|
|
417
|
+
})), S = (r.servers ?? []).map((e) => ({
|
|
418
|
+
url: e.url,
|
|
419
|
+
...e.description === void 0 ? {} : { description: e.description },
|
|
420
|
+
...e.environment === void 0 ? {} : { "x-voltro-environment": e.environment }
|
|
421
|
+
}));
|
|
159
422
|
return {
|
|
160
423
|
openapi: "3.1.0",
|
|
161
424
|
info: {
|
|
162
425
|
title: t.title ?? "API",
|
|
163
426
|
version: t.version ?? "1.0.0",
|
|
164
|
-
...t.description ? { description: t.description } : {}
|
|
427
|
+
...t.description ? { description: t.description } : {},
|
|
428
|
+
...t.contact === void 0 ? {} : { contact: t.contact },
|
|
429
|
+
...t.license === void 0 ? {} : { license: t.license },
|
|
430
|
+
...t.termsOfService === void 0 ? {} : { termsOfService: t.termsOfService },
|
|
431
|
+
...t.logo === void 0 ? {} : { "x-logo": t.logo }
|
|
165
432
|
},
|
|
166
|
-
|
|
433
|
+
...S.length > 0 ? { servers: S } : {},
|
|
434
|
+
...r.externalDocs === void 0 ? {} : { externalDocs: r.externalDocs },
|
|
435
|
+
...r.environment === void 0 ? {} : { "x-voltro-environment": r.environment },
|
|
436
|
+
...e.some((e) => e.projection !== void 0) ? { "x-voltro-surface-digest": i(e) } : {},
|
|
437
|
+
...x.length > 0 ? { tags: x } : {},
|
|
438
|
+
paths: m(a),
|
|
167
439
|
components: {
|
|
168
440
|
schemas: {
|
|
169
|
-
...
|
|
170
|
-
[
|
|
441
|
+
...m(o),
|
|
442
|
+
[g]: _,
|
|
443
|
+
...l ? { [y]: b } : {}
|
|
171
444
|
},
|
|
172
|
-
...
|
|
445
|
+
...s || c ? { securitySchemes: { bearerAuth: {
|
|
173
446
|
type: "http",
|
|
174
447
|
scheme: "bearer"
|
|
175
448
|
} } } : {}
|
|
@@ -178,73 +451,157 @@ function y(e, t = {}, n = {}) {
|
|
|
178
451
|
}
|
|
179
452
|
//#endregion
|
|
180
453
|
//#region src/swaggerUi.ts
|
|
181
|
-
var
|
|
454
|
+
var j = "5.17.14", M = "sha384-wxLW6kwyHktdDGr6Pv1zgm/VGJh99lfUbzSn6HNHBENZlCN7W602k9VkGdxuFvPn", N = "sha384-wmyclcVGX/WhUkdkATwhaK1X1JtiNrr2EoYJ+diV3vj4v6OC5yCeSu+yW13SYJep", P = `https://unpkg.com/swagger-ui-dist@${j}/swagger-ui.css`, F = `https://unpkg.com/swagger-ui-dist@${j}/swagger-ui-bundle.js`, I = (e, t = "API docs", n = {}) => `<!doctype html>
|
|
182
455
|
<html lang="en">
|
|
183
456
|
<head>
|
|
184
457
|
<meta charset="utf-8" />
|
|
185
458
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
186
459
|
<title>${t}</title>
|
|
187
|
-
<link rel="stylesheet" href="${
|
|
460
|
+
<link rel="stylesheet" href="${P}" integrity="${M}" crossorigin="anonymous" />
|
|
188
461
|
<style>body { margin: 0 }</style>
|
|
189
462
|
</head>
|
|
190
463
|
<body>
|
|
191
464
|
<div id="swagger-ui"></div>
|
|
192
|
-
<script src="${
|
|
465
|
+
<script src="${F}" integrity="${N}" crossorigin="anonymous"><\/script>
|
|
193
466
|
<script>
|
|
194
|
-
window.ui = SwaggerUIBundle({ url: ${JSON.stringify(e)}, dom_id: '#swagger-ui', deepLinking: true });
|
|
467
|
+
window.ui = SwaggerUIBundle({ ${n.urls !== void 0 && n.urls.length > 0 ? `urls: ${JSON.stringify(n.urls)}, ${n.primaryName === void 0 ? "" : `'urls.primaryName': ${JSON.stringify(n.primaryName)}, `}` : `url: ${JSON.stringify(e)}, `}dom_id: '#swagger-ui', deepLinking: true });
|
|
195
468
|
<\/script>
|
|
196
469
|
</body>
|
|
197
|
-
</html>`,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
470
|
+
</html>`, L = r([
|
|
471
|
+
{
|
|
472
|
+
name: "OPENAPI_DOCS_TOKEN",
|
|
473
|
+
required: !1,
|
|
474
|
+
secret: !0,
|
|
475
|
+
description: "Bearer token required on GET /openapi.json + GET /docs; unset leaves them public."
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
name: "VOLTRO_PUBLIC_API_ORIGIN",
|
|
479
|
+
required: !1,
|
|
480
|
+
secret: !1,
|
|
481
|
+
description: "The public origin of this api (https://api.example.com) — the default servers[0] of the spec."
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "VOLTRO_ENVIRONMENT",
|
|
485
|
+
required: !1,
|
|
486
|
+
secret: !1,
|
|
487
|
+
description: "The environment this instance serves (dev | staging | production) — x-voltro-environment on the spec."
|
|
488
|
+
}
|
|
489
|
+
]), R = (e, t) => {
|
|
203
490
|
if (e) return (t.headers.authorization ?? t.headers.Authorization) === `Bearer ${e}` ? void 0 : {
|
|
204
491
|
status: 401,
|
|
205
492
|
body: "unauthorized",
|
|
206
493
|
contentType: "text/plain"
|
|
207
494
|
};
|
|
208
|
-
},
|
|
209
|
-
let
|
|
210
|
-
|
|
495
|
+
}, z = (e) => e.projection?.version ?? e.version, B = (e) => {
|
|
496
|
+
let t = /^v(\d+)$/.exec(e);
|
|
497
|
+
return t === null ? -1 : Number(t[1]);
|
|
498
|
+
}, V = (e) => [...new Set(e.map(z).filter((e) => e !== void 0))].sort((e, t) => B(t) - B(e) || t.localeCompare(e)), H = (e, t) => {
|
|
499
|
+
let n = t.filter((t) => z(t) === e && t.sunset !== void 0).map((e) => e.sunset).sort();
|
|
500
|
+
return n.length > 0 ? `${e} — sunset ${n[0]}` : e;
|
|
501
|
+
}, U = (t) => {
|
|
502
|
+
let r = t.specPath ?? "/openapi.json", i = t.docsPath ?? "/docs", a = L.read("OPENAPI_DOCS_TOKEN", t.token), o = t.name ? `@voltro/plugin-openapi#${t.name}` : "@voltro/plugin-openapi", s = t.routes ?? [], c = t.procedures ?? [], l = c, u = t.includeAppRoutes ?? !0, d = t.includeAppProcedures ?? !1, f = s, p = t.environment ?? L.read("VOLTRO_ENVIRONMENT", void 0) ?? (process.env.NODE_ENV === "production" ? "production" : "dev"), m = L.read("VOLTRO_PUBLIC_API_ORIGIN", void 0), h = t.servers ?? (m === void 0 ? [] : [{
|
|
503
|
+
url: m,
|
|
504
|
+
description: `${p} — this instance`,
|
|
505
|
+
environment: p
|
|
506
|
+
}]), g = {
|
|
507
|
+
...t.tagDescriptions === void 0 ? {} : { tagDescriptions: t.tagDescriptions },
|
|
508
|
+
...t.tagExternalDocs === void 0 ? {} : { tagExternalDocs: t.tagExternalDocs },
|
|
509
|
+
...t.externalDocs === void 0 ? {} : { externalDocs: t.externalDocs },
|
|
510
|
+
...h.length > 0 ? { servers: h } : {},
|
|
511
|
+
environment: p
|
|
512
|
+
}, _ = (e) => JSON.stringify(A(e === void 0 ? f : f.filter((t) => z(t) === e), t.info ?? {}, e === void 0 ? { procedures: l } : {}, g), null, 2), v = /* @__PURE__ */ new Map(), y = () => {
|
|
513
|
+
v = new Map([["", _()], ...V(f).map((e) => [e, _(e)])]);
|
|
514
|
+
};
|
|
515
|
+
y();
|
|
516
|
+
let b = (e) => {
|
|
517
|
+
if (!u) return;
|
|
211
518
|
let t = new Set(s.map((e) => `${e.method} ${e.path}`)), n = e.filter((e) => !t.has(`${e.method} ${e.path}`));
|
|
212
|
-
|
|
213
|
-
},
|
|
519
|
+
f = [...s, ...n], y();
|
|
520
|
+
}, x = (e) => {
|
|
521
|
+
if (!d) return;
|
|
522
|
+
let t = new Set(c.map((e) => e.name));
|
|
523
|
+
l = [...c, ...e.filter((e) => !t.has(e.name))], y();
|
|
524
|
+
}, S = r.endsWith(".json") ? r.slice(0, -5) : `${r}/versions`, C = (e) => `${S}/${e}.json`, w = (e) => ({
|
|
525
|
+
status: 404,
|
|
526
|
+
contentType: "application/json",
|
|
527
|
+
body: JSON.stringify({
|
|
528
|
+
error: `no API version '${e}' is mounted`,
|
|
529
|
+
versions: V(f)
|
|
530
|
+
})
|
|
531
|
+
}), T = [{
|
|
214
532
|
method: "GET",
|
|
215
533
|
path: r,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
534
|
+
match: "exact",
|
|
535
|
+
handle: async (e) => {
|
|
536
|
+
let t = R(a, e);
|
|
537
|
+
if (t) return t;
|
|
538
|
+
let n = new URLSearchParams(e.query).get("version") ?? void 0;
|
|
539
|
+
return n !== void 0 && !v.has(n) ? w(n) : {
|
|
540
|
+
status: 200,
|
|
541
|
+
body: v.get(n ?? ""),
|
|
542
|
+
contentType: "application/json"
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
}, {
|
|
546
|
+
method: "GET",
|
|
547
|
+
path: S,
|
|
548
|
+
handle: async (e) => {
|
|
549
|
+
let t = R(a, e);
|
|
550
|
+
if (t) return t;
|
|
551
|
+
let n = /\/([^/]+)\.json$/.exec(e.path)?.[1];
|
|
552
|
+
return n === void 0 || n === "" || !v.has(n) ? w(n ?? "") : {
|
|
553
|
+
status: 200,
|
|
554
|
+
body: v.get(n),
|
|
555
|
+
contentType: "application/json"
|
|
556
|
+
};
|
|
220
557
|
}
|
|
221
558
|
}];
|
|
222
|
-
return i !== !1 &&
|
|
559
|
+
return i !== !1 && T.push({
|
|
223
560
|
method: "GET",
|
|
224
561
|
path: i,
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
562
|
+
match: "exact",
|
|
563
|
+
handle: async (e) => {
|
|
564
|
+
let n = R(a, e);
|
|
565
|
+
if (n) return n;
|
|
566
|
+
let i = V(f), o = i.length > 1 ? [...i.map((e) => ({
|
|
567
|
+
url: C(e),
|
|
568
|
+
name: H(e, f)
|
|
569
|
+
})), {
|
|
570
|
+
url: r,
|
|
571
|
+
name: "all versions"
|
|
572
|
+
}] : void 0;
|
|
573
|
+
return {
|
|
574
|
+
status: 200,
|
|
575
|
+
body: I(r, t.info?.title ?? "API docs", o === void 0 ? {} : {
|
|
576
|
+
urls: o,
|
|
577
|
+
primaryName: o[0].name
|
|
578
|
+
}),
|
|
579
|
+
contentType: "text/html; charset=utf-8"
|
|
580
|
+
};
|
|
229
581
|
}
|
|
230
582
|
}), n({
|
|
231
583
|
name: o,
|
|
232
584
|
description: "OpenAPI 3.1 spec + Swagger-UI docs from defineRestRoute descriptors + rpc procedures.",
|
|
233
585
|
permissions: [],
|
|
234
|
-
declaredEnv:
|
|
235
|
-
httpRoutes:
|
|
236
|
-
onRestSurface:
|
|
586
|
+
declaredEnv: L.declared,
|
|
587
|
+
httpRoutes: T,
|
|
588
|
+
onRestSurface: b,
|
|
589
|
+
onProcedureSurface: x,
|
|
237
590
|
onActivate: (t) => e.sync(() => {
|
|
238
591
|
t.logger.info("openapi docs active", {
|
|
239
592
|
specPath: r,
|
|
240
593
|
docsPath: i,
|
|
241
|
-
routes:
|
|
242
|
-
appRoutes:
|
|
243
|
-
procedures:
|
|
594
|
+
routes: f.length,
|
|
595
|
+
appRoutes: u ? "included once mounted" : "excluded",
|
|
596
|
+
procedures: l.length,
|
|
597
|
+
appProcedures: d ? "included once discovered" : "excluded",
|
|
598
|
+
environment: p,
|
|
599
|
+
servers: h.map((e) => e.url),
|
|
600
|
+
versions: V(f),
|
|
244
601
|
tokenGated: !!a
|
|
245
602
|
});
|
|
246
603
|
})
|
|
247
604
|
});
|
|
248
605
|
};
|
|
249
606
|
//#endregion
|
|
250
|
-
export { y as generateOpenApiSpec,
|
|
607
|
+
export { y as PROBLEM_DETAILS_SCHEMA_NAME, x as deriveExample, A as generateOpenApiSpec, V as mountedVersions, U as openapiPlugin, I as swaggerUiHtml, H as versionLabel };
|