@voltro/plugin-openapi 0.70.0 → 0.71.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.
- package/CHANGELOG.md +1341 -111
- package/dist/index.d.ts +92 -0
- package/dist/index.js +298 -137
- 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,10 +98,10 @@ 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
|
-
}),
|
|
103
|
+
content: { "application/json": { schema: { $ref: `#/components/schemas/${g}` } } }
|
|
104
|
+
}), y = "ProblemDetails", b = {
|
|
104
105
|
type: "object",
|
|
105
106
|
description: "RFC 9457 Problem Details. Extension members carry the error's own fields.",
|
|
106
107
|
properties: {
|
|
@@ -119,7 +120,76 @@ var i = "/rpc", a = "rpc", o = (e) => e.ast._tag === "NeverKeyword", s = (e, t)
|
|
|
119
120
|
"status"
|
|
120
121
|
],
|
|
121
122
|
additionalProperties: !0
|
|
122
|
-
},
|
|
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 = {
|
|
123
193
|
400: "Bad Request",
|
|
124
194
|
401: "Unauthorized",
|
|
125
195
|
403: "Forbidden",
|
|
@@ -129,66 +199,98 @@ var i = "/rpc", a = "rpc", o = (e) => e.ast._tag === "NeverKeyword", s = (e, t)
|
|
|
129
199
|
422: "Unprocessable Content",
|
|
130
200
|
429: "Too Many Requests",
|
|
131
201
|
500: "Internal Server Error"
|
|
132
|
-
},
|
|
202
|
+
}, w = (e, t, n) => {
|
|
133
203
|
let r = t.format.errors === "problem-details" && t.format.encodeError === void 0, i = r ? "application/problem+json" : "application/json", a = /* @__PURE__ */ new Map();
|
|
134
204
|
for (let e of t.errorResponses) {
|
|
135
|
-
let t = a.get(e.status) ?? []
|
|
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
|
+
};
|
|
136
214
|
t.push({
|
|
137
215
|
tag: e.tag,
|
|
138
|
-
|
|
216
|
+
example: l,
|
|
217
|
+
schema: r ? { allOf: [{ $ref: `#/components/schemas/${y}` }, {
|
|
139
218
|
type: "object",
|
|
140
219
|
properties: { type: { const: `urn:voltro:error:${e.tag}` } }
|
|
141
|
-
}] } :
|
|
220
|
+
}] } : i
|
|
142
221
|
}), a.set(e.status, t);
|
|
143
222
|
}
|
|
144
|
-
let o = (e, t) =>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 = {};
|
|
148
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."));
|
|
149
240
|
for (let [e, t] of [...a.entries()].sort((e, t) => e[0] - t[0])) {
|
|
150
241
|
let n = t.map((e) => e.tag);
|
|
151
242
|
s[String(e)] = {
|
|
152
|
-
description: `${
|
|
153
|
-
content: { [i]: {
|
|
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
|
+
} }
|
|
154
248
|
};
|
|
155
249
|
}
|
|
156
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;
|
|
157
|
-
},
|
|
158
|
-
let r =
|
|
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] } : {};
|
|
159
253
|
for (let [t, n] of Object.entries(i)) o.has(t) ? s.push({
|
|
160
254
|
name: t,
|
|
161
255
|
in: "path",
|
|
162
256
|
required: !0,
|
|
163
|
-
schema: n
|
|
257
|
+
schema: n,
|
|
258
|
+
...d(t)
|
|
164
259
|
}) : e.method === "GET" ? s.push({
|
|
165
260
|
name: t,
|
|
166
261
|
in: "query",
|
|
167
262
|
required: a.has(t),
|
|
168
|
-
schema: n
|
|
263
|
+
schema: n,
|
|
264
|
+
...d(t)
|
|
169
265
|
}) : (c[t] = n, a.has(t) && l.push(t));
|
|
170
266
|
return {
|
|
171
267
|
parameters: s,
|
|
172
268
|
requestBody: e.method !== "GET" && Object.keys(c).length > 0 ? {
|
|
173
269
|
required: l.length > 0,
|
|
174
|
-
content: {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
}
|
|
180
282
|
} : void 0
|
|
181
283
|
};
|
|
182
|
-
},
|
|
284
|
+
}, E = (e, t, n) => {
|
|
183
285
|
if (e.streaming === !0) return {
|
|
184
286
|
description: "Server-Sent Events stream (`snapshot`, then `delta` per change)",
|
|
185
287
|
content: { "text/event-stream": { schema: { type: "string" } } }
|
|
186
288
|
};
|
|
187
|
-
let r =
|
|
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" ? {
|
|
188
290
|
type: "object",
|
|
189
291
|
properties: {
|
|
190
292
|
data: r,
|
|
191
|
-
...
|
|
293
|
+
...a ? { next: {
|
|
192
294
|
type: "string",
|
|
193
295
|
description: "The next page's cursor, when there is one."
|
|
194
296
|
} } : {}
|
|
@@ -204,8 +306,8 @@ var i = "/rpc", a = "rpc", o = (e) => e.ast._tag === "NeverKeyword", s = (e, t)
|
|
|
204
306
|
}
|
|
205
307
|
},
|
|
206
308
|
required: ["data"]
|
|
207
|
-
} : r : { description: `Encoded by the '${i.name}' format.` },
|
|
208
|
-
...
|
|
309
|
+
} : r : { description: `Encoded by the '${i.name}' format.` }, s = i.envelope === "jsonapi" ? "application/vnd.api+json" : "application/json", c = {
|
|
310
|
+
...a ? { Link: {
|
|
209
311
|
description: "RFC 8288 — `<url>; rel=\"next\"` when there is a next page.",
|
|
210
312
|
schema: { type: "string" }
|
|
211
313
|
} } : {},
|
|
@@ -216,45 +318,63 @@ var i = "/rpc", a = "rpc", o = (e) => e.ast._tag === "NeverKeyword", s = (e, t)
|
|
|
216
318
|
};
|
|
217
319
|
return {
|
|
218
320
|
description: "OK",
|
|
219
|
-
...Object.keys(
|
|
220
|
-
content: { [
|
|
221
|
-
schema:
|
|
222
|
-
...e.example?.response
|
|
321
|
+
...Object.keys(c).length > 0 ? { headers: c } : {},
|
|
322
|
+
content: { [s]: {
|
|
323
|
+
schema: o,
|
|
324
|
+
...O(e.example?.response, o, n)
|
|
223
325
|
} }
|
|
224
326
|
};
|
|
225
|
-
},
|
|
226
|
-
|
|
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.") },
|
|
227
335
|
...e.guards !== void 0 && e.guards.length > 0 ? {
|
|
228
|
-
401:
|
|
229
|
-
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).")
|
|
230
338
|
} : {},
|
|
231
|
-
...e.sunset === void 0 ? {} : { 410:
|
|
232
|
-
500:
|
|
339
|
+
...e.sunset === void 0 ? {} : { 410: v(`Gone — the route is past its sunset date (${e.sunset}).`) },
|
|
340
|
+
500: v("Internal Server Error.")
|
|
233
341
|
});
|
|
234
|
-
function
|
|
235
|
-
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();
|
|
236
344
|
for (let t of e) {
|
|
237
|
-
let e = t.projection, { parameters: n, requestBody: r } = e === void 0 ?
|
|
238
|
-
|
|
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) {
|
|
239
359
|
let e = r.content;
|
|
240
360
|
e["application/json"] = {
|
|
241
361
|
...e["application/json"],
|
|
242
|
-
|
|
362
|
+
...O(t.example?.request, e["application/json"].schema, o)
|
|
243
363
|
};
|
|
244
364
|
}
|
|
245
|
-
let
|
|
246
|
-
for (let e of
|
|
247
|
-
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, _ = {
|
|
248
368
|
...t.operationId === void 0 ? {} : { operationId: t.operationId },
|
|
249
369
|
...t.summary ? { summary: t.summary } : {},
|
|
250
370
|
...t.description ? { description: t.description } : {},
|
|
251
371
|
...t.deprecated === void 0 ? {} : { deprecated: !0 },
|
|
252
372
|
...t.sunset === void 0 ? {} : { "x-sunset": t.sunset },
|
|
253
|
-
...
|
|
254
|
-
...
|
|
255
|
-
...
|
|
373
|
+
...m === void 0 ? {} : { tags: m },
|
|
374
|
+
...g === void 0 ? {} : { "x-voltro-api-version": g },
|
|
375
|
+
...s.length > 0 ? { parameters: s } : {},
|
|
256
376
|
...r ? { requestBody: r } : {},
|
|
257
|
-
...
|
|
377
|
+
...d,
|
|
258
378
|
responses: e === void 0 ? {
|
|
259
379
|
200: t.streaming === !0 ? {
|
|
260
380
|
description: "Server-Sent Events stream (`snapshot`, then `delta` per change)",
|
|
@@ -262,52 +382,67 @@ function T(e, t = {}, n = {}, r = {}) {
|
|
|
262
382
|
} : {
|
|
263
383
|
description: "OK",
|
|
264
384
|
content: { "application/json": {
|
|
265
|
-
schema:
|
|
266
|
-
...t.example?.response
|
|
385
|
+
schema: p(t.output, o),
|
|
386
|
+
...O(t.example?.response, p(t.output, o), o)
|
|
267
387
|
} }
|
|
268
388
|
},
|
|
269
|
-
...
|
|
389
|
+
...k(t),
|
|
390
|
+
...c
|
|
270
391
|
} : {
|
|
271
|
-
200:
|
|
272
|
-
...
|
|
392
|
+
200: E(t, e, o),
|
|
393
|
+
...c,
|
|
394
|
+
...w(t, e, o)
|
|
273
395
|
}
|
|
274
396
|
};
|
|
275
|
-
e?.format.describe !== void 0 && (
|
|
397
|
+
e?.format.describe !== void 0 && (_ = e.format.describe(_, {
|
|
276
398
|
tag: e.tag,
|
|
277
399
|
method: t.method,
|
|
278
400
|
path: t.path
|
|
279
401
|
}));
|
|
280
|
-
let
|
|
281
|
-
|
|
402
|
+
let v = f(t.path), y = a[v] ?? {};
|
|
403
|
+
y[t.method.toLowerCase()] = _, a[v] = y;
|
|
282
404
|
}
|
|
283
|
-
let
|
|
284
|
-
if (
|
|
285
|
-
let e =
|
|
286
|
-
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] = {
|
|
287
409
|
...n,
|
|
288
|
-
...
|
|
410
|
+
...a[t] ?? {}
|
|
289
411
|
};
|
|
290
412
|
}
|
|
291
|
-
let
|
|
413
|
+
let x = [...u].sort().map((e) => ({
|
|
292
414
|
name: e,
|
|
293
|
-
...r.tagDescriptions?.[e] === void 0 ? {} : { description: r.tagDescriptions[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 }
|
|
294
421
|
}));
|
|
295
422
|
return {
|
|
296
423
|
openapi: "3.1.0",
|
|
297
424
|
info: {
|
|
298
425
|
title: t.title ?? "API",
|
|
299
426
|
version: t.version ?? "1.0.0",
|
|
300
|
-
...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 }
|
|
301
432
|
},
|
|
302
|
-
...
|
|
303
|
-
|
|
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),
|
|
304
439
|
components: {
|
|
305
440
|
schemas: {
|
|
306
|
-
...
|
|
307
|
-
[
|
|
308
|
-
...
|
|
441
|
+
...m(o),
|
|
442
|
+
[g]: _,
|
|
443
|
+
...l ? { [y]: b } : {}
|
|
309
444
|
},
|
|
310
|
-
...
|
|
445
|
+
...s || c ? { securitySchemes: { bearerAuth: {
|
|
311
446
|
type: "http",
|
|
312
447
|
scheme: "bearer"
|
|
313
448
|
} } } : {}
|
|
@@ -316,104 +451,128 @@ function T(e, t = {}, n = {}, r = {}) {
|
|
|
316
451
|
}
|
|
317
452
|
//#endregion
|
|
318
453
|
//#region src/swaggerUi.ts
|
|
319
|
-
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>
|
|
320
455
|
<html lang="en">
|
|
321
456
|
<head>
|
|
322
457
|
<meta charset="utf-8" />
|
|
323
458
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
324
459
|
<title>${t}</title>
|
|
325
|
-
<link rel="stylesheet" href="${
|
|
460
|
+
<link rel="stylesheet" href="${P}" integrity="${M}" crossorigin="anonymous" />
|
|
326
461
|
<style>body { margin: 0 }</style>
|
|
327
462
|
</head>
|
|
328
463
|
<body>
|
|
329
464
|
<div id="swagger-ui"></div>
|
|
330
|
-
<script src="${
|
|
465
|
+
<script src="${F}" integrity="${N}" crossorigin="anonymous"><\/script>
|
|
331
466
|
<script>
|
|
332
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 });
|
|
333
468
|
<\/script>
|
|
334
469
|
</body>
|
|
335
|
-
</html>`,
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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) => {
|
|
341
490
|
if (e) return (t.headers.authorization ?? t.headers.Authorization) === `Bearer ${e}` ? void 0 : {
|
|
342
491
|
status: 401,
|
|
343
492
|
body: "unauthorized",
|
|
344
493
|
contentType: "text/plain"
|
|
345
494
|
};
|
|
346
|
-
},
|
|
495
|
+
}, z = (e) => e.projection?.version ?? e.version, B = (e) => {
|
|
347
496
|
let t = /^v(\d+)$/.exec(e);
|
|
348
497
|
return t === null ? -1 : Number(t[1]);
|
|
349
|
-
},
|
|
350
|
-
let n = t.filter((t) =>
|
|
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();
|
|
351
500
|
return n.length > 0 ? `${e} — sunset ${n[0]}` : e;
|
|
352
|
-
},
|
|
353
|
-
let r = t.specPath ?? "/openapi.json", i = t.docsPath ?? "/docs", a =
|
|
354
|
-
|
|
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)])]);
|
|
355
514
|
};
|
|
356
|
-
|
|
357
|
-
let
|
|
515
|
+
y();
|
|
516
|
+
let b = (e) => {
|
|
358
517
|
if (!u) return;
|
|
359
518
|
let t = new Set(s.map((e) => `${e.method} ${e.path}`)), n = e.filter((e) => !t.has(`${e.method} ${e.path}`));
|
|
360
|
-
f = [...s, ...n],
|
|
361
|
-
},
|
|
519
|
+
f = [...s, ...n], y();
|
|
520
|
+
}, x = (e) => {
|
|
362
521
|
if (!d) return;
|
|
363
522
|
let t = new Set(c.map((e) => e.name));
|
|
364
|
-
l = [...c, ...e.filter((e) => !t.has(e.name))],
|
|
365
|
-
},
|
|
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) => ({
|
|
366
525
|
status: 404,
|
|
367
526
|
contentType: "application/json",
|
|
368
527
|
body: JSON.stringify({
|
|
369
528
|
error: `no API version '${e}' is mounted`,
|
|
370
|
-
versions:
|
|
529
|
+
versions: V(f)
|
|
371
530
|
})
|
|
372
|
-
}),
|
|
531
|
+
}), T = [{
|
|
373
532
|
method: "GET",
|
|
374
533
|
path: r,
|
|
375
534
|
match: "exact",
|
|
376
535
|
handle: async (e) => {
|
|
377
|
-
let t =
|
|
536
|
+
let t = R(a, e);
|
|
378
537
|
if (t) return t;
|
|
379
538
|
let n = new URLSearchParams(e.query).get("version") ?? void 0;
|
|
380
|
-
return n !== void 0 && !
|
|
539
|
+
return n !== void 0 && !v.has(n) ? w(n) : {
|
|
381
540
|
status: 200,
|
|
382
|
-
body:
|
|
541
|
+
body: v.get(n ?? ""),
|
|
383
542
|
contentType: "application/json"
|
|
384
543
|
};
|
|
385
544
|
}
|
|
386
545
|
}, {
|
|
387
546
|
method: "GET",
|
|
388
|
-
path:
|
|
547
|
+
path: S,
|
|
389
548
|
handle: async (e) => {
|
|
390
|
-
let t =
|
|
549
|
+
let t = R(a, e);
|
|
391
550
|
if (t) return t;
|
|
392
551
|
let n = /\/([^/]+)\.json$/.exec(e.path)?.[1];
|
|
393
|
-
return n === void 0 || n === "" || !
|
|
552
|
+
return n === void 0 || n === "" || !v.has(n) ? w(n ?? "") : {
|
|
394
553
|
status: 200,
|
|
395
|
-
body:
|
|
554
|
+
body: v.get(n),
|
|
396
555
|
contentType: "application/json"
|
|
397
556
|
};
|
|
398
557
|
}
|
|
399
558
|
}];
|
|
400
|
-
return i !== !1 &&
|
|
559
|
+
return i !== !1 && T.push({
|
|
401
560
|
method: "GET",
|
|
402
561
|
path: i,
|
|
403
562
|
match: "exact",
|
|
404
563
|
handle: async (e) => {
|
|
405
|
-
let n =
|
|
564
|
+
let n = R(a, e);
|
|
406
565
|
if (n) return n;
|
|
407
|
-
let i =
|
|
408
|
-
url:
|
|
409
|
-
name:
|
|
566
|
+
let i = V(f), o = i.length > 1 ? [...i.map((e) => ({
|
|
567
|
+
url: C(e),
|
|
568
|
+
name: H(e, f)
|
|
410
569
|
})), {
|
|
411
570
|
url: r,
|
|
412
571
|
name: "all versions"
|
|
413
572
|
}] : void 0;
|
|
414
573
|
return {
|
|
415
574
|
status: 200,
|
|
416
|
-
body:
|
|
575
|
+
body: I(r, t.info?.title ?? "API docs", o === void 0 ? {} : {
|
|
417
576
|
urls: o,
|
|
418
577
|
primaryName: o[0].name
|
|
419
578
|
}),
|
|
@@ -424,10 +583,10 @@ var E = "5.17.14", D = "sha384-wxLW6kwyHktdDGr6Pv1zgm/VGJh99lfUbzSn6HNHBENZlCN7W
|
|
|
424
583
|
name: o,
|
|
425
584
|
description: "OpenAPI 3.1 spec + Swagger-UI docs from defineRestRoute descriptors + rpc procedures.",
|
|
426
585
|
permissions: [],
|
|
427
|
-
declaredEnv:
|
|
428
|
-
httpRoutes:
|
|
429
|
-
onRestSurface:
|
|
430
|
-
onProcedureSurface:
|
|
586
|
+
declaredEnv: L.declared,
|
|
587
|
+
httpRoutes: T,
|
|
588
|
+
onRestSurface: b,
|
|
589
|
+
onProcedureSurface: x,
|
|
431
590
|
onActivate: (t) => e.sync(() => {
|
|
432
591
|
t.logger.info("openapi docs active", {
|
|
433
592
|
specPath: r,
|
|
@@ -436,11 +595,13 @@ var E = "5.17.14", D = "sha384-wxLW6kwyHktdDGr6Pv1zgm/VGJh99lfUbzSn6HNHBENZlCN7W
|
|
|
436
595
|
appRoutes: u ? "included once mounted" : "excluded",
|
|
437
596
|
procedures: l.length,
|
|
438
597
|
appProcedures: d ? "included once discovered" : "excluded",
|
|
439
|
-
|
|
598
|
+
environment: p,
|
|
599
|
+
servers: h.map((e) => e.url),
|
|
600
|
+
versions: V(f),
|
|
440
601
|
tokenGated: !!a
|
|
441
602
|
});
|
|
442
603
|
})
|
|
443
604
|
});
|
|
444
605
|
};
|
|
445
606
|
//#endregion
|
|
446
|
-
export {
|
|
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 };
|