@voltro/plugin-openapi 0.84.0 → 0.86.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/dist/index.js CHANGED
@@ -1,489 +1,10 @@
1
- import { Effect as e, JSONSchema as t } from "effect";
2
- import { definePlugin as n } from "@voltro/protocol";
3
- import { pluginEnv as r } from "@voltro/env";
4
- import { publicSurfaceDigest as i } from "@voltro/protocol/rest";
5
- //#region src/rpc.ts
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) => {
7
- let { kind: n, name: r } = e, i = {
8
- required: !0,
9
- content: { "application/json": { schema: p(e.input, t) } }
10
- }, a = (() => {
11
- if (n === "stream") return {
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.",
13
- content: { "application/x-ndjson": { schema: p(e.element, t) } }
14
- };
15
- let r = p(e.output, t);
16
- return {
17
- description: n === "query" ? "OK — the query snapshot (the first/initial snapshot over `POST /rpc`; the WebSocket channel then pushes reactive updates)." : "OK",
18
- content: { "application/json": { schema: r } }
19
- };
20
- })(), u = s(e.error) ? {} : { 422: {
21
- description: "A typed error from the procedure — one of its declared error variants (the rpc failure channel).",
22
- content: { "application/json": { schema: p(e.error, t) } }
23
- } };
24
- return {
25
- tags: [o],
26
- summary: c(n, r),
27
- description: l(n),
28
- "x-voltro-rpc": {
29
- kind: n,
30
- procedure: r
31
- },
32
- requestBody: i,
33
- responses: {
34
- 200: a,
35
- ...u,
36
- 500: v("Internal Server Error — an untyped defect.")
37
- }
38
- };
39
- }, d = (e, t) => {
40
- let n = {};
41
- for (let r of e) {
42
- let e = `${a}/${r.name}`;
43
- n[e] = { post: u(r, t) };
44
- }
45
- return n;
46
- }, f = (e) => e.replace(/:([A-Za-z0-9_]+)/g, "{$1}"), p = (e, n) => {
47
- let { $schema: r, $defs: i, ...a } = t.make(e);
48
- if (i && typeof i == "object") for (let [e, t] of Object.entries(i)) n[e] = t;
49
- return a;
50
- }, m = (e) => {
51
- if (Array.isArray(e)) return e.map(m);
52
- if (e && typeof e == "object") {
53
- let t = {};
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);
55
- return t;
56
- }
57
- return e;
58
- }, h = (e, t) => {
59
- if (!e) return {
60
- parameters: [],
61
- requestBody: void 0
62
- };
63
- let n = p(e, t), r = n.properties ?? {}, i = new Set(n.required ?? []), a = [];
64
- for (let [e, t] of [["query", "query"], ["params", "path"]]) {
65
- let n = r[e];
66
- if (!n || typeof n != "object") continue;
67
- let i = n.properties ?? {}, o = new Set(n.required ?? []);
68
- for (let [e, n] of Object.entries(i)) a.push({
69
- name: e,
70
- in: t,
71
- required: t === "path" || o.has(e),
72
- schema: n
73
- });
74
- }
75
- let o = r.body;
76
- return {
77
- parameters: a,
78
- requestBody: o && typeof o == "object" ? {
79
- required: i.has("body"),
80
- content: { "application/json": { schema: o } }
81
- } : void 0
82
- };
83
- }, g = "VoltroHttpError", _ = {
84
- type: "object",
85
- description: "The serve pipeline's error envelope.",
86
- properties: {
87
- error: {
88
- type: "string",
89
- description: "Human-readable error message."
90
- },
91
- detail: {
92
- type: "string",
93
- description: "Decode failure detail (400 only)."
94
- },
95
- replacement: {
96
- type: "string",
97
- description: "Replacement endpoint hint (410 only)."
98
- }
99
- },
100
- required: ["error"]
101
- }, v = (e) => ({
102
- description: e,
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
- ...e.cache !== void 0 && e.method === "GET" ? {
319
- "Cache-Control": {
320
- description: `\`public, max-age=${e.cache.maxAge}\` for a request without a credential; \`private, no-cache\` for one with \`Authorization\` or a cookie.`,
321
- schema: { type: "string" }
322
- },
323
- ...e.cache.tags === void 0 ? {} : {
324
- "Cache-Tag": {
325
- description: "Purge tags, comma-separated (only on a public answer).",
326
- schema: { type: "string" }
327
- },
328
- "Surrogate-Key": {
329
- description: "The same purge tags, space-separated (only on a public answer).",
330
- schema: { type: "string" }
331
- }
332
- }
333
- } : {}
334
- };
335
- return {
336
- description: "OK",
337
- ...Object.keys(c).length > 0 ? { headers: c } : {},
338
- content: { [s]: {
339
- schema: o,
340
- ...O(e.example?.response, o, n)
341
- } }
342
- };
343
- }, D = (e, t, n) => {
344
- let r = e.$ref, i = (typeof r == "string" ? t[r.split("/").pop() ?? ""] : e)?.properties;
345
- return typeof i == "object" && !!i && n in i;
346
- }, O = (e, t, n) => e === void 0 ? {
347
- example: x(t, n),
348
- "x-voltro-example": "derived"
349
- } : { example: e }, k = (e) => ({
350
- ...e.input === void 0 ? {} : { 400: v("Invalid request — the input failed schema decode.") },
351
- ...e.guards !== void 0 && e.guards.length > 0 ? {
352
- 401: v("Unauthorized — a guard rejected the unauthenticated request."),
353
- 403: v("Forbidden — a guard rejected the request (e.g. a required scope is missing).")
354
- } : {},
355
- ...e.sunset === void 0 ? {} : { 410: v(`Gone — the route is past its sunset date (${e.sunset}).`) },
356
- 500: v("Internal Server Error.")
357
- });
358
- function A(e, t = {}, n = {}, r = {}) {
359
- 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();
360
- for (let t of e) {
361
- 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, {
362
- name: "If-None-Match",
363
- in: "header",
364
- required: !1,
365
- description: "A previously returned ETag. A matching validator returns 304 without a body.",
366
- schema: { type: "string" }
367
- }] : n, c = i ? { 304: {
368
- description: "Not Modified",
369
- headers: { ETag: {
370
- description: "The current representation validator.",
371
- schema: { type: "string" }
372
- } }
373
- } } : {}, l = t.guards !== void 0 && t.guards.length > 0;
374
- if (r !== void 0) {
375
- let e = r.content;
376
- e["application/json"] = {
377
- ...e["application/json"],
378
- ...O(t.example?.request, e["application/json"].schema, o)
379
- };
380
- }
381
- 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]);
382
- for (let e of m ?? []) u.add(e);
383
- let g = e?.version ?? t.version, _ = {
384
- ...t.operationId === void 0 ? {} : { operationId: t.operationId },
385
- ...t.summary ? { summary: t.summary } : {},
386
- ...t.description ? { description: t.description } : {},
387
- ...t.deprecated === void 0 ? {} : { deprecated: !0 },
388
- ...t.sunset === void 0 ? {} : { "x-sunset": t.sunset },
389
- ...m === void 0 ? {} : { tags: m },
390
- ...g === void 0 ? {} : { "x-voltro-api-version": g },
391
- ...s.length > 0 ? { parameters: s } : {},
392
- ...r ? { requestBody: r } : {},
393
- ...d,
394
- responses: e === void 0 ? {
395
- 200: t.streaming === !0 ? {
396
- description: "Server-Sent Events stream (`snapshot`, then `delta` per change)",
397
- content: { "text/event-stream": { schema: { type: "string" } } }
398
- } : {
399
- description: "OK",
400
- content: { "application/json": {
401
- schema: p(t.output, o),
402
- ...O(t.example?.response, p(t.output, o), o)
403
- } }
404
- },
405
- ...k(t),
406
- ...c
407
- } : {
408
- 200: E(t, e, o),
409
- ...c,
410
- ...w(t, e, o)
411
- }
412
- };
413
- e?.format.describe !== void 0 && (_ = e.format.describe(_, {
414
- tag: e.tag,
415
- method: t.method,
416
- path: t.path
417
- }));
418
- let v = f(t.path), y = a[v] ?? {};
419
- y[t.method.toLowerCase()] = _, a[v] = y;
420
- }
421
- let v = n.procedures ?? [];
422
- if (v.length > 0) {
423
- let e = d(v, o);
424
- for (let [t, n] of Object.entries(e)) a[t] = {
425
- ...n,
426
- ...a[t] ?? {}
427
- };
428
- }
429
- let x = [...u].sort().map((e) => ({
430
- name: e,
431
- ...r.tagDescriptions?.[e] === void 0 ? {} : { description: r.tagDescriptions[e] },
432
- ...r.tagExternalDocs?.[e] === void 0 ? {} : { externalDocs: r.tagExternalDocs[e] }
433
- })), S = (r.servers ?? []).map((e) => ({
434
- url: e.url,
435
- ...e.description === void 0 ? {} : { description: e.description },
436
- ...e.environment === void 0 ? {} : { "x-voltro-environment": e.environment }
437
- }));
438
- return {
439
- openapi: "3.1.0",
440
- info: {
441
- title: t.title ?? "API",
442
- version: t.version ?? "1.0.0",
443
- ...t.description ? { description: t.description } : {},
444
- ...t.contact === void 0 ? {} : { contact: t.contact },
445
- ...t.license === void 0 ? {} : { license: t.license },
446
- ...t.termsOfService === void 0 ? {} : { termsOfService: t.termsOfService },
447
- ...t.logo === void 0 ? {} : { "x-logo": t.logo }
448
- },
449
- ...S.length > 0 ? { servers: S } : {},
450
- ...r.externalDocs === void 0 ? {} : { externalDocs: r.externalDocs },
451
- ...r.environment === void 0 ? {} : { "x-voltro-environment": r.environment },
452
- ...e.some((e) => e.projection !== void 0) ? { "x-voltro-surface-digest": i(e) } : {},
453
- ...x.length > 0 ? { tags: x } : {},
454
- paths: m(a),
455
- components: {
456
- schemas: {
457
- ...m(o),
458
- [g]: _,
459
- ...l ? { [y]: b } : {}
460
- },
461
- ...s || c ? { securitySchemes: { bearerAuth: {
462
- type: "http",
463
- scheme: "bearer"
464
- } } } : {}
465
- }
466
- };
467
- }
468
- //#endregion
469
- //#region src/swaggerUi.ts
470
- 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>
471
- <html lang="en">
472
- <head>
473
- <meta charset="utf-8" />
474
- <meta name="viewport" content="width=device-width, initial-scale=1" />
475
- <title>${t}</title>
476
- <link rel="stylesheet" href="${P}" integrity="${M}" crossorigin="anonymous" />
477
- <style>body { margin: 0 }</style>
478
- </head>
479
- <body>
480
- <div id="swagger-ui"></div>
481
- <script src="${F}" integrity="${N}" crossorigin="anonymous"><\/script>
482
- <script>
483
- 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 });
484
- <\/script>
485
- </body>
486
- </html>`, L = r([
1
+ import { PROBLEM_DETAILS_SCHEMA_NAME as e, deriveExample as t, generateOpenApiSpec as n } from "./generate.js";
2
+ import { swaggerUiHtml as r } from "./swaggerUi.js";
3
+ import { Effect as i } from "effect";
4
+ import { definePlugin as a } from "@voltro/protocol";
5
+ import { pluginEnv as o } from "@voltro/env";
6
+ //#region src/index.ts
7
+ var s = o([
487
8
  {
488
9
  name: "OPENAPI_DOCS_TOKEN",
489
10
  required: !1,
@@ -502,122 +23,132 @@ var j = "5.17.14", M = "sha384-wxLW6kwyHktdDGr6Pv1zgm/VGJh99lfUbzSn6HNHBENZlCN7W
502
23
  secret: !1,
503
24
  description: "The environment this instance serves (dev | staging | production) — x-voltro-environment on the spec."
504
25
  }
505
- ]), R = (e, t) => {
26
+ ]), c = (e, t) => {
506
27
  if (e) return (t.headers.authorization ?? t.headers.Authorization) === `Bearer ${e}` ? void 0 : {
507
28
  status: 401,
508
29
  body: "unauthorized",
509
30
  contentType: "text/plain"
510
31
  };
511
- }, z = (e) => e.projection?.version ?? e.version, B = (e) => {
32
+ }, l = (e) => e.projection?.version ?? e.version, u = (e) => {
512
33
  let t = /^v(\d+)$/.exec(e);
513
34
  return t === null ? -1 : Number(t[1]);
514
- }, 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) => {
515
- let n = t.filter((t) => z(t) === e && t.sunset !== void 0).map((e) => e.sunset).sort();
516
- return n.length > 0 ? `${e} — sunset ${n[0]}` : e;
517
- }, U = (t) => {
518
- 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 ? [] : [{
519
- url: m,
520
- description: `${p} — this instance`,
521
- environment: p
522
- }]), g = {
523
- ...t.tagDescriptions === void 0 ? {} : { tagDescriptions: t.tagDescriptions },
524
- ...t.tagExternalDocs === void 0 ? {} : { tagExternalDocs: t.tagExternalDocs },
525
- ...t.externalDocs === void 0 ? {} : { externalDocs: t.externalDocs },
526
- ...h.length > 0 ? { servers: h } : {},
527
- environment: p
528
- }, _ = (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 = () => {
529
- v = new Map([["", _()], ...V(f).map((e) => [e, _(e)])]);
35
+ }, d = (e) => [...new Set(e.map(l).filter((e) => e !== void 0))].sort((e, t) => u(t) - u(e) || t.localeCompare(e)), f = (e, t) => {
36
+ let n;
37
+ for (let r of t) {
38
+ if (l(r) !== e) continue;
39
+ let t = r.sunset;
40
+ t !== void 0 && (n === void 0 || t < n) && (n = t);
41
+ }
42
+ return n === void 0 ? e : `${e} — sunset ${n}`;
43
+ }, p = (e) => {
44
+ let t = e.specPath ?? "/openapi.json", o = e.docsPath ?? "/docs", f = s.read("OPENAPI_DOCS_TOKEN", e.token), p = e.name ? `@voltro/plugin-openapi#${e.name}` : "@voltro/plugin-openapi", m = e.routes ?? [], h = e.procedures ?? [], g = h, _ = e.includeAppRoutes ?? !0, v = e.includeAppProcedures ?? !1, y = m, b = e.environment ?? s.read("VOLTRO_ENVIRONMENT", void 0) ?? (process.env.NODE_ENV === "production" ? "production" : "dev"), x = s.read("VOLTRO_PUBLIC_API_ORIGIN", void 0), S = e.servers ?? (x === void 0 ? [] : [{
45
+ url: x,
46
+ description: `${b} — this instance`,
47
+ environment: b
48
+ }]), C = {
49
+ ...e.tagDescriptions === void 0 ? {} : { tagDescriptions: e.tagDescriptions },
50
+ ...e.tagExternalDocs === void 0 ? {} : { tagExternalDocs: e.tagExternalDocs },
51
+ ...e.externalDocs === void 0 ? {} : { externalDocs: e.externalDocs },
52
+ ...S.length > 0 ? { servers: S } : {},
53
+ environment: b
54
+ }, w = (t, r, i) => JSON.stringify(n(t, e.info ?? {}, r ? { procedures: i } : {}, C), null, 2), T = t.endsWith(".json") ? t.slice(0, -5) : `${t}/versions`, E = (e) => `${T}/${e}.json`, D = /* @__PURE__ */ new Map(), O = [], k = "", A = (n = y, i = g) => {
55
+ let a = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map();
56
+ for (let e of n) {
57
+ let t = l(e);
58
+ if (t === void 0) continue;
59
+ let n = a.get(t);
60
+ n === void 0 && (n = [], a.set(t, n)), n.push(e);
61
+ let r = e.sunset, i = s.get(t);
62
+ r !== void 0 && (i === void 0 || r < i) && s.set(t, r);
63
+ }
64
+ let c = [...a.keys()].sort((e, t) => u(t) - u(e) || t.localeCompare(e)), d = new Map([["", w(n, !0, i)], ...c.map((e) => [e, w(a.get(e), !1, i)])]), f = c.length > 1 ? [...c.map((e) => ({
65
+ url: E(e),
66
+ name: s.has(e) ? `${e} — sunset ${s.get(e)}` : e
67
+ })), {
68
+ url: t,
69
+ name: "all versions"
70
+ }] : void 0, p = o === !1 ? "" : r(t, e.info?.title ?? "API docs", f === void 0 ? {} : {
71
+ urls: f,
72
+ primaryName: f[0].name
73
+ });
74
+ D = d, O = c, k = p, y = n, g = i;
530
75
  };
531
- y();
532
- let b = (e) => {
533
- if (!u) return;
534
- let t = new Set(s.map((e) => `${e.method} ${e.path}`)), n = e.filter((e) => !t.has(`${e.method} ${e.path}`));
535
- f = [...s, ...n], y();
536
- }, x = (e) => {
537
- if (!d) return;
538
- let t = new Set(c.map((e) => e.name));
539
- l = [...c, ...e.filter((e) => !t.has(e.name))], y();
540
- }, S = r.endsWith(".json") ? r.slice(0, -5) : `${r}/versions`, C = (e) => `${S}/${e}.json`, w = (e) => ({
76
+ A();
77
+ let j = (e) => {
78
+ if (!_) return;
79
+ let t = new Set(m.map((e) => `${e.method} ${e.path}`)), n = e.filter((e) => !t.has(`${e.method} ${e.path}`));
80
+ A([...m, ...n]);
81
+ }, M = (e) => {
82
+ if (!v) return;
83
+ let t = new Set(h.map((e) => e.name));
84
+ A(y, [...h, ...e.filter((e) => !t.has(e.name))]);
85
+ }, N = (e) => ({
541
86
  status: 404,
542
87
  contentType: "application/json",
543
88
  body: JSON.stringify({
544
89
  error: `no API version '${e}' is mounted`,
545
- versions: V(f)
90
+ versions: O
546
91
  })
547
- }), T = [{
92
+ }), P = [{
548
93
  method: "GET",
549
- path: r,
94
+ path: t,
550
95
  match: "exact",
551
96
  handle: async (e) => {
552
- let t = R(a, e);
97
+ let t = c(f, e);
553
98
  if (t) return t;
554
99
  let n = new URLSearchParams(e.query).get("version") ?? void 0;
555
- return n !== void 0 && !v.has(n) ? w(n) : {
100
+ return n !== void 0 && !D.has(n) ? N(n) : {
556
101
  status: 200,
557
- body: v.get(n ?? ""),
102
+ body: D.get(n ?? ""),
558
103
  contentType: "application/json"
559
104
  };
560
105
  }
561
106
  }, {
562
107
  method: "GET",
563
- path: S,
108
+ path: T,
564
109
  handle: async (e) => {
565
- let t = R(a, e);
110
+ let t = c(f, e);
566
111
  if (t) return t;
567
112
  let n = /\/([^/]+)\.json$/.exec(e.path)?.[1];
568
- return n === void 0 || n === "" || !v.has(n) ? w(n ?? "") : {
113
+ return n === void 0 || n === "" || !D.has(n) ? N(n ?? "") : {
569
114
  status: 200,
570
- body: v.get(n),
115
+ body: D.get(n),
571
116
  contentType: "application/json"
572
117
  };
573
118
  }
574
119
  }];
575
- return i !== !1 && T.push({
120
+ return o !== !1 && P.push({
576
121
  method: "GET",
577
- path: i,
122
+ path: o,
578
123
  match: "exact",
579
- handle: async (e) => {
580
- let n = R(a, e);
581
- if (n) return n;
582
- let i = V(f), o = i.length > 1 ? [...i.map((e) => ({
583
- url: C(e),
584
- name: H(e, f)
585
- })), {
586
- url: r,
587
- name: "all versions"
588
- }] : void 0;
589
- return {
590
- status: 200,
591
- body: I(r, t.info?.title ?? "API docs", o === void 0 ? {} : {
592
- urls: o,
593
- primaryName: o[0].name
594
- }),
595
- contentType: "text/html; charset=utf-8"
596
- };
124
+ handle: async (e) => c(f, e) || {
125
+ status: 200,
126
+ body: k,
127
+ contentType: "text/html; charset=utf-8"
597
128
  }
598
- }), n({
599
- name: o,
129
+ }), a({
130
+ name: p,
600
131
  description: "OpenAPI 3.1 spec + Swagger-UI docs from defineRestRoute descriptors + rpc procedures.",
601
132
  permissions: [],
602
- declaredEnv: L.declared,
603
- httpRoutes: T,
604
- onRestSurface: b,
605
- onProcedureSurface: x,
606
- onActivate: (t) => e.sync(() => {
607
- t.logger.info("openapi docs active", {
608
- specPath: r,
609
- docsPath: i,
610
- routes: f.length,
611
- appRoutes: u ? "included once mounted" : "excluded",
612
- procedures: l.length,
613
- appProcedures: d ? "included once discovered" : "excluded",
614
- environment: p,
615
- servers: h.map((e) => e.url),
616
- versions: V(f),
617
- tokenGated: !!a
133
+ declaredEnv: s.declared,
134
+ httpRoutes: P,
135
+ onRestSurface: j,
136
+ onProcedureSurface: M,
137
+ onActivate: (e) => i.sync(() => {
138
+ e.logger.info("openapi docs active", {
139
+ specPath: t,
140
+ docsPath: o,
141
+ routes: y.length,
142
+ appRoutes: _ ? "included once mounted" : "excluded",
143
+ procedures: g.length,
144
+ appProcedures: v ? "included once discovered" : "excluded",
145
+ environment: b,
146
+ servers: S.map((e) => e.url),
147
+ versions: d(y),
148
+ tokenGated: !!f
618
149
  });
619
150
  })
620
151
  });
621
152
  };
622
153
  //#endregion
623
- 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 };
154
+ export { e as PROBLEM_DETAILS_SCHEMA_NAME, t as deriveExample, n as generateOpenApiSpec, d as mountedVersions, p as openapiPlugin, r as swaggerUiHtml, f as versionLabel };