@tetsujs/openapi 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -3
- package/dist/src/annotations.d.ts +40 -0
- package/dist/src/annotations.d.ts.map +1 -1
- package/dist/src/controller.d.ts +26 -9
- package/dist/src/controller.d.ts.map +1 -1
- package/dist/src/document.d.ts +7 -0
- package/dist/src/document.d.ts.map +1 -1
- package/dist/src/emit.d.ts +3 -1
- package/dist/src/emit.d.ts.map +1 -1
- package/dist/src/envelopes.d.ts +74 -0
- package/dist/src/envelopes.d.ts.map +1 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +298 -112
- package/dist/src/index.js.map +10 -9
- package/dist/src/json-schema.d.ts +91 -0
- package/dist/src/json-schema.d.ts.map +1 -0
- package/dist/src/operation.d.ts +10 -1
- package/dist/src/operation.d.ts.map +1 -1
- package/dist/src/page.d.ts +20 -0
- package/dist/src/page.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/annotations.ts +43 -0
- package/src/controller.ts +61 -22
- package/src/document.ts +9 -0
- package/src/emit.ts +3 -1
- package/src/envelopes.ts +242 -0
- package/src/index.ts +19 -6
- package/src/json-schema.ts +108 -0
- package/src/operation.ts +312 -147
- package/src/page.ts +65 -6
package/dist/src/index.js
CHANGED
|
@@ -102,6 +102,104 @@ function failureName(status, error) {
|
|
|
102
102
|
return name || `Failure${status}`;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// packages/openapi/src/envelopes.ts
|
|
106
|
+
function envelopes(components) {
|
|
107
|
+
const declared = new Map;
|
|
108
|
+
const defined = new Map;
|
|
109
|
+
const refs = new Map;
|
|
110
|
+
const codes = new Map;
|
|
111
|
+
const names = new Map;
|
|
112
|
+
const reported = new Set;
|
|
113
|
+
return {
|
|
114
|
+
declare(status, code, schema) {
|
|
115
|
+
const key = keyOf(status, code);
|
|
116
|
+
if (!declared.has(key)) {
|
|
117
|
+
declared.set(key, schema);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
ref(status, code, schema, warn) {
|
|
121
|
+
const key = keyOf(status, code);
|
|
122
|
+
const definition = declared.get(key) ?? defined.get(key) ?? schema;
|
|
123
|
+
defined.set(key, definition);
|
|
124
|
+
const difference = differenceOf(definition, schema);
|
|
125
|
+
if (difference && !reported.has(`${key} ${difference}`)) {
|
|
126
|
+
reported.add(`${key} ${difference}`);
|
|
127
|
+
warn(`the ${status} "${code}" is described by one definition in the document, and a definition that differs from it in ${difference} is not described`);
|
|
128
|
+
}
|
|
129
|
+
const existing = refs.get(key);
|
|
130
|
+
if (existing) {
|
|
131
|
+
return existing;
|
|
132
|
+
}
|
|
133
|
+
const created = components.ref(nameOf(status, code), definition);
|
|
134
|
+
refs.set(key, created);
|
|
135
|
+
codes.set(created.$ref, code);
|
|
136
|
+
return created;
|
|
137
|
+
},
|
|
138
|
+
codeOf(ref) {
|
|
139
|
+
return codes.get(ref);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
function nameOf(status, code) {
|
|
143
|
+
const name = failureName(status, code);
|
|
144
|
+
const holder = names.get(name);
|
|
145
|
+
if (holder === undefined || holder === keyOf(status, code)) {
|
|
146
|
+
names.set(name, keyOf(status, code));
|
|
147
|
+
return name;
|
|
148
|
+
}
|
|
149
|
+
return `${name}${status}`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function envelopeCode(schema) {
|
|
153
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
154
|
+
if (!required.includes("error")) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const error = schema.properties?.error;
|
|
158
|
+
if (typeof error?.const === "string") {
|
|
159
|
+
return error.const;
|
|
160
|
+
}
|
|
161
|
+
if (Array.isArray(error?.enum) && error.enum.length === 1 && typeof error.enum[0] === "string") {
|
|
162
|
+
return error.enum[0];
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
function branchesOf(schema) {
|
|
167
|
+
const keys = Object.keys(schema);
|
|
168
|
+
if (keys.length !== 1) {
|
|
169
|
+
return [schema];
|
|
170
|
+
}
|
|
171
|
+
if (Array.isArray(schema.anyOf)) {
|
|
172
|
+
return schema.anyOf.flatMap(branchesOf);
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(schema.oneOf)) {
|
|
175
|
+
const branches = schema.oneOf.flatMap(branchesOf);
|
|
176
|
+
const codes = branches.map(envelopeCode);
|
|
177
|
+
if (codes.every((code) => code !== undefined) && new Set(codes).size === codes.length) {
|
|
178
|
+
return branches;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return [schema];
|
|
182
|
+
}
|
|
183
|
+
function keyOf(status, code) {
|
|
184
|
+
return `${status} ${code}`;
|
|
185
|
+
}
|
|
186
|
+
function differenceOf(kept, other) {
|
|
187
|
+
const fields = (schema) => Object.keys(schema.properties ?? {});
|
|
188
|
+
const keptFields = fields(kept);
|
|
189
|
+
const otherFields = fields(other);
|
|
190
|
+
const missing = otherFields.filter((name) => !keptFields.includes(name));
|
|
191
|
+
const extra = keptFields.filter((name) => !otherFields.includes(name));
|
|
192
|
+
const changed = [...missing, ...extra];
|
|
193
|
+
if (changed.length > 0) {
|
|
194
|
+
return `fields: ${changed.join(", ")}`;
|
|
195
|
+
}
|
|
196
|
+
const required = (schema) => JSON.stringify([...Array.isArray(schema.required) ? schema.required : []].sort());
|
|
197
|
+
return required(kept) === required(other) ? undefined : "which fields are required";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// packages/openapi/src/operation.ts
|
|
201
|
+
import { errorBody } from "@tetsujs/core";
|
|
202
|
+
|
|
105
203
|
// packages/openapi/src/emit.ts
|
|
106
204
|
import { toJsonSchema } from "@tetsujs/core";
|
|
107
205
|
var target = { target: "draft-2020-12" };
|
|
@@ -192,7 +290,7 @@ function objectProperties(schema, where, warn) {
|
|
|
192
290
|
}
|
|
193
291
|
|
|
194
292
|
// packages/openapi/src/operation.ts
|
|
195
|
-
function operationOf(entry, options, components, ids, warn) {
|
|
293
|
+
function operationOf(entry, options, components, envelopes, ids, warn) {
|
|
196
294
|
const schema = entry.def.schema;
|
|
197
295
|
const parameters = [
|
|
198
296
|
...pathParameters(entry.path, schema?.params, warn),
|
|
@@ -211,9 +309,36 @@ function operationOf(entry, options, components, ids, warn) {
|
|
|
211
309
|
...parameters.length > 0 ? { parameters } : {},
|
|
212
310
|
...body ? { requestBody: body } : {},
|
|
213
311
|
...contributed.security.length > 0 ? { security: securityOf(contributed.conditions) } : {},
|
|
214
|
-
responses: responses(entry, options, contributed, components, warn)
|
|
312
|
+
responses: responses(entry, options, contributed, components, envelopes, warn)
|
|
215
313
|
};
|
|
216
314
|
}
|
|
315
|
+
function declareEnvelopes(entry, envelopes) {
|
|
316
|
+
for (const [status, schema] of declaredResponses(entry)) {
|
|
317
|
+
const described = schema === null ? undefined : emitted(schema, "", () => {});
|
|
318
|
+
for (const branch of described ? branchesOf(described) : []) {
|
|
319
|
+
const code = envelopeCode(branch);
|
|
320
|
+
if (code !== undefined) {
|
|
321
|
+
envelopes.declare(Number(status), code, branch);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function declaredResponses(entry) {
|
|
327
|
+
const response = entry.def.schema?.response;
|
|
328
|
+
if (!response) {
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
if ("~standard" in response) {
|
|
332
|
+
return [["200", response]];
|
|
333
|
+
}
|
|
334
|
+
return Object.entries(response);
|
|
335
|
+
}
|
|
336
|
+
function branchesFor(schema, status, envelopes, warn) {
|
|
337
|
+
return branchesOf(schema).map((branch) => {
|
|
338
|
+
const code = envelopeCode(branch);
|
|
339
|
+
return code === undefined ? branch : envelopes.ref(status, code, branch, warn);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
217
342
|
function securityOf(conditions) {
|
|
218
343
|
let combinations = [{}];
|
|
219
344
|
for (const alternatives of conditions) {
|
|
@@ -317,94 +442,147 @@ function carriesBinary(root) {
|
|
|
317
442
|
}
|
|
318
443
|
return false;
|
|
319
444
|
}
|
|
320
|
-
function responses(entry, options, contributed, components, warn) {
|
|
321
|
-
const
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
445
|
+
function responses(entry, options, contributed, components, envelopes, warn) {
|
|
446
|
+
const answers = new Map;
|
|
447
|
+
const add = (status, answer) => {
|
|
448
|
+
answers.set(status, [...answers.get(status) ?? [], answer]);
|
|
449
|
+
};
|
|
450
|
+
for (const [status, schema] of declaredResponses(entry)) {
|
|
451
|
+
const described = schema === null ? undefined : emitted(schema, "a response", warn);
|
|
452
|
+
add(status, {
|
|
453
|
+
description: describeStatus(status),
|
|
454
|
+
placeholder: true,
|
|
455
|
+
schemas: described ? branchesFor(described, Number(status), envelopes, warn) : []
|
|
456
|
+
});
|
|
329
457
|
}
|
|
330
|
-
if (!
|
|
331
|
-
|
|
458
|
+
if (![...answers.keys()].some((status) => status.startsWith("2"))) {
|
|
459
|
+
add("200", {
|
|
460
|
+
description: "Successful response",
|
|
461
|
+
placeholder: true,
|
|
462
|
+
schemas: []
|
|
463
|
+
});
|
|
332
464
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
};
|
|
465
|
+
for (const [status, answer] of failures(entry, options, contributed, components, envelopes, warn)) {
|
|
466
|
+
add(String(status), answer);
|
|
467
|
+
}
|
|
468
|
+
const described = {};
|
|
469
|
+
for (const [status, list] of answers) {
|
|
470
|
+
described[status] = merge(list, envelopes);
|
|
471
|
+
}
|
|
472
|
+
return described;
|
|
337
473
|
}
|
|
338
|
-
function
|
|
474
|
+
function failures(entry, options, contributed, components, envelopes, warn) {
|
|
339
475
|
const schema = entry.def.schema;
|
|
340
|
-
const
|
|
341
|
-
const
|
|
342
|
-
const
|
|
343
|
-
|
|
476
|
+
const found = [];
|
|
477
|
+
const envelopeRef = (status, error, message, fields) => {
|
|
478
|
+
const described = envelope(status, error, message, fields);
|
|
479
|
+
const ref = error === undefined ? components.ref(failureName(status), described) : envelopes.ref(status, error, described, warn);
|
|
480
|
+
return ref;
|
|
344
481
|
};
|
|
345
|
-
const envelopeRef = (status, error, message) => components.ref(failureName(status, error), envelope(status, error, message));
|
|
346
482
|
if (schema?.params || schema?.query || schema?.headers || schema?.body) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
483
|
+
found.push([
|
|
484
|
+
options.validationStatus,
|
|
485
|
+
{
|
|
486
|
+
description: "Request failed schema validation",
|
|
487
|
+
schemas: [
|
|
488
|
+
envelopes.ref(options.validationStatus, "VALIDATION_FAILED", validationFailed(options.validationStatus), warn)
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
]);
|
|
351
492
|
}
|
|
352
493
|
for (const requirement of contributed.security) {
|
|
353
494
|
const status = requirement.status ?? 401;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
495
|
+
found.push([
|
|
496
|
+
status,
|
|
497
|
+
{
|
|
498
|
+
description: requirement.description ?? `Request did not satisfy ${requirement.name}`,
|
|
499
|
+
schemas: [envelopeRef(status, requirement.error, requirement.message)]
|
|
500
|
+
}
|
|
501
|
+
]);
|
|
358
502
|
}
|
|
359
503
|
for (const response of contributed.responses) {
|
|
360
504
|
const described = response.schema ? emitted(response.schema, "a hook's response", warn) : undefined;
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
505
|
+
found.push([
|
|
506
|
+
response.status,
|
|
507
|
+
{
|
|
508
|
+
description: response.description,
|
|
509
|
+
schemas: described ? branchesFor(described, response.status, envelopes, warn) : [
|
|
510
|
+
envelopeRef(response.status, response.error, response.message, response.fields)
|
|
511
|
+
],
|
|
512
|
+
...response.headers ? { headers: response.headers } : {}
|
|
513
|
+
}
|
|
514
|
+
]);
|
|
365
515
|
}
|
|
366
516
|
if (schema?.body || entry.def.bodyType) {
|
|
367
517
|
const unparsable = parseFailure(entry.def.bodyType);
|
|
368
518
|
if (unparsable) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
519
|
+
found.push([
|
|
520
|
+
400,
|
|
521
|
+
{
|
|
522
|
+
description: "Body could not be parsed in the declared shape",
|
|
523
|
+
schemas: [envelopeRef(400, unparsable.error, unparsable.message)]
|
|
524
|
+
}
|
|
525
|
+
]);
|
|
526
|
+
}
|
|
527
|
+
found.push([
|
|
528
|
+
413,
|
|
529
|
+
{
|
|
530
|
+
description: "Body exceeded the configured size limit",
|
|
531
|
+
schemas: [
|
|
532
|
+
envelopeRef(413, "BODY_TOO_LARGE", "Body exceeds the configured limit")
|
|
533
|
+
]
|
|
534
|
+
}
|
|
535
|
+
]);
|
|
536
|
+
}
|
|
537
|
+
found.push([
|
|
538
|
+
500,
|
|
539
|
+
{
|
|
540
|
+
description: "The request failed and nothing mapped the failure",
|
|
541
|
+
schemas: [
|
|
542
|
+
envelopeRef(500, "INTERNAL_SERVER_ERROR", "Internal Server Error")
|
|
543
|
+
]
|
|
544
|
+
}
|
|
545
|
+
]);
|
|
546
|
+
return found;
|
|
547
|
+
}
|
|
548
|
+
function merge(list, envelopes) {
|
|
549
|
+
const schemas = distinct(list.flatMap((answer) => answer.schemas));
|
|
550
|
+
const worded = list.filter((answer) => !answer.placeholder);
|
|
551
|
+
const headers = {};
|
|
552
|
+
for (const answer of list) {
|
|
553
|
+
for (const [name, header] of Object.entries(answer.headers ?? {})) {
|
|
554
|
+
headers[name] ??= header;
|
|
373
555
|
}
|
|
374
|
-
add(413, {
|
|
375
|
-
description: "Body exceeded the configured size limit",
|
|
376
|
-
schema: envelopeRef(413, "BODY_TOO_LARGE", "Body exceeds the configured limit")
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
add(500, {
|
|
380
|
-
description: "The request failed and nothing mapped the failure",
|
|
381
|
-
schema: envelopeRef(500, "INTERNAL_SERVER_ERROR", "Internal Server Error")
|
|
382
|
-
});
|
|
383
|
-
const responses = {};
|
|
384
|
-
for (const [status, list] of failures) {
|
|
385
|
-
responses[status] = merge(declared[status], list);
|
|
386
556
|
}
|
|
387
|
-
return
|
|
557
|
+
return {
|
|
558
|
+
description: (worded.length > 0 ? worded : list).map((answer) => answer.description).join("; "),
|
|
559
|
+
...Object.keys(headers).length > 0 ? { headers } : {},
|
|
560
|
+
...schemas.length > 0 ? {
|
|
561
|
+
content: {
|
|
562
|
+
"application/json": { schema: unionOf(schemas, envelopes) }
|
|
563
|
+
}
|
|
564
|
+
} : {}
|
|
565
|
+
};
|
|
388
566
|
}
|
|
389
|
-
function
|
|
390
|
-
const descriptions = [
|
|
391
|
-
...declared ? [declared.description] : [],
|
|
392
|
-
...list.map((failure) => failure.description)
|
|
393
|
-
];
|
|
394
|
-
const schemas = distinct([
|
|
395
|
-
...declaredSchemas(declared),
|
|
396
|
-
...list.map((failure) => failure.schema)
|
|
397
|
-
]);
|
|
567
|
+
function unionOf(schemas, envelopes) {
|
|
398
568
|
const [only] = schemas;
|
|
569
|
+
if (schemas.length === 1 && only) {
|
|
570
|
+
return only;
|
|
571
|
+
}
|
|
572
|
+
const mapping = {};
|
|
573
|
+
for (const schema of schemas) {
|
|
574
|
+
const ref = schema.$ref;
|
|
575
|
+
const code = typeof ref === "string" ? envelopes.codeOf(ref) : undefined;
|
|
576
|
+
if (typeof ref !== "string" || code === undefined) {
|
|
577
|
+
return { anyOf: schemas };
|
|
578
|
+
}
|
|
579
|
+
mapping[code] = ref;
|
|
580
|
+
}
|
|
399
581
|
return {
|
|
400
|
-
|
|
401
|
-
|
|
582
|
+
anyOf: schemas,
|
|
583
|
+
discriminator: { propertyName: "error", mapping }
|
|
402
584
|
};
|
|
403
585
|
}
|
|
404
|
-
function declaredSchemas(declared) {
|
|
405
|
-
const schema = declared?.content?.["application/json"]?.schema;
|
|
406
|
-
return schema ? [schema] : [];
|
|
407
|
-
}
|
|
408
586
|
function distinct(schemas) {
|
|
409
587
|
const seen = new Set;
|
|
410
588
|
return schemas.filter((schema) => {
|
|
@@ -416,34 +594,23 @@ function distinct(schemas) {
|
|
|
416
594
|
return true;
|
|
417
595
|
});
|
|
418
596
|
}
|
|
419
|
-
function jsonResponse(schema, description, warn) {
|
|
420
|
-
const described = emitted(schema, "a response", warn);
|
|
421
|
-
if (!described) {
|
|
422
|
-
return { description };
|
|
423
|
-
}
|
|
424
|
-
return {
|
|
425
|
-
description,
|
|
426
|
-
content: { "application/json": { schema: described } }
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
function hasSuccess(declared) {
|
|
430
|
-
return Object.keys(declared).some((status) => status.startsWith("2"));
|
|
431
|
-
}
|
|
432
597
|
function describeStatus(status) {
|
|
433
598
|
if (status === "204") {
|
|
434
599
|
return "No content";
|
|
435
600
|
}
|
|
436
|
-
return status.startsWith("2") ? "Successful response" :
|
|
601
|
+
return status.startsWith("2") ? "Successful response" : errorBody(Number(status)).message;
|
|
437
602
|
}
|
|
438
|
-
function envelope(status, error, message) {
|
|
603
|
+
function envelope(status, error, message, fields = {}) {
|
|
604
|
+
const own = {
|
|
605
|
+
status: { type: "integer", const: status },
|
|
606
|
+
message: message ? { type: "string", examples: [message] } : { type: "string" },
|
|
607
|
+
error: error ? { type: "string", const: error } : { type: "string" }
|
|
608
|
+
};
|
|
609
|
+
const added = Object.keys(fields).filter((name) => !Object.hasOwn(own, name));
|
|
439
610
|
return {
|
|
440
611
|
type: "object",
|
|
441
|
-
required: ["status", "message", "error"],
|
|
442
|
-
properties: {
|
|
443
|
-
status: { type: "integer", const: status },
|
|
444
|
-
message: message ? { type: "string", examples: [message] } : { type: "string" },
|
|
445
|
-
error: error ? { type: "string", const: error } : { type: "string" }
|
|
446
|
-
}
|
|
612
|
+
required: ["status", "message", "error", ...added],
|
|
613
|
+
properties: { ...fields, ...own }
|
|
447
614
|
};
|
|
448
615
|
}
|
|
449
616
|
function validationFailed(status) {
|
|
@@ -485,14 +652,24 @@ import { onMount, route } from "@tetsujs/core";
|
|
|
485
652
|
// packages/openapi/src/page.ts
|
|
486
653
|
var defaults = {
|
|
487
654
|
scalar: {
|
|
488
|
-
script: "https://cdn.jsdelivr.net/npm/@scalar/api-reference"
|
|
655
|
+
script: "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.72.1/dist/browser/standalone.js",
|
|
656
|
+
integrity: {
|
|
657
|
+
script: "sha384-U11tb2XnKvmwt8RlTvnwUnYgrN+ur4Xyh9htLhjajWNR/Oyl5AX5DEz00qRmlrmK"
|
|
658
|
+
}
|
|
489
659
|
},
|
|
490
660
|
"swagger-ui": {
|
|
491
|
-
script: "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
|
|
492
|
-
style: "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css"
|
|
661
|
+
script: "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.33.0/swagger-ui-bundle.js",
|
|
662
|
+
style: "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.33.0/swagger-ui.css",
|
|
663
|
+
integrity: {
|
|
664
|
+
script: "sha384-YDALVcy8kj8yltLBVi1vBiBAUqdxvus673gM8XKwiy6aDUJFXivF/KCufekjYbVf",
|
|
665
|
+
style: "sha384-Ov4/wv3j2bmct8cDc5X4ngJZohVPzEmc6uDPH8WeljUxO5vtoykvMEfbu9Vh6RaW"
|
|
666
|
+
}
|
|
493
667
|
},
|
|
494
668
|
redoc: {
|
|
495
|
-
script: "https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js"
|
|
669
|
+
script: "https://cdn.jsdelivr.net/npm/redoc@2.5.4/bundles/redoc.standalone.js",
|
|
670
|
+
integrity: {
|
|
671
|
+
script: "sha384-w447zOpYfw/1Tv/5AK9NfHTlQIqE3RVR6KY62jCyy9zNDgO64cMwGGP1Fj0zJVf5"
|
|
672
|
+
}
|
|
496
673
|
}
|
|
497
674
|
};
|
|
498
675
|
function docsPage(options) {
|
|
@@ -506,11 +683,11 @@ function docsPage(options) {
|
|
|
506
683
|
'<meta charset="utf-8" />',
|
|
507
684
|
'<meta name="viewport" content="width=device-width, initial-scale=1" />',
|
|
508
685
|
`<title>${title}</title>`,
|
|
509
|
-
assets.style ? `<link rel="stylesheet" href="${escapeHtml(assets.style)}" />` : "",
|
|
686
|
+
assets.style ? `<link rel="stylesheet" href="${escapeHtml(assets.style)}"${verified(assets.integrity?.style)} />` : "",
|
|
510
687
|
"</head>",
|
|
511
688
|
"<body>",
|
|
512
689
|
body(options.ui, url),
|
|
513
|
-
`<script src="${escapeHtml(assets.script)}"></script>`,
|
|
690
|
+
`<script src="${escapeHtml(assets.script)}"${verified(assets.integrity?.script)}></script>`,
|
|
514
691
|
bootstrap(options.ui, url),
|
|
515
692
|
"</body>",
|
|
516
693
|
"</html>"
|
|
@@ -537,6 +714,9 @@ function bootstrap(ui, url) {
|
|
|
537
714
|
].join(`
|
|
538
715
|
`);
|
|
539
716
|
}
|
|
717
|
+
function verified(integrity) {
|
|
718
|
+
return integrity ? ` integrity="${escapeHtml(integrity)}" crossorigin="anonymous"` : "";
|
|
719
|
+
}
|
|
540
720
|
function escapeHtml(value) {
|
|
541
721
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
542
722
|
}
|
|
@@ -547,7 +727,8 @@ class DocsController {
|
|
|
547
727
|
json;
|
|
548
728
|
ui;
|
|
549
729
|
document;
|
|
550
|
-
|
|
730
|
+
page;
|
|
731
|
+
constructor(options, documentPath, uiPath) {
|
|
551
732
|
this.options = options;
|
|
552
733
|
const hidden = !options.documentSelf;
|
|
553
734
|
const json = route({
|
|
@@ -560,12 +741,12 @@ class DocsController {
|
|
|
560
741
|
method: "GET",
|
|
561
742
|
path: "/docs",
|
|
562
743
|
docs: { summary: "Rendered documentation", tags: ["docs"], hidden },
|
|
563
|
-
handler: () => new Response(page, {
|
|
744
|
+
handler: () => new Response(this.page, {
|
|
564
745
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
565
746
|
})
|
|
566
747
|
});
|
|
567
748
|
this.json = { ...json, path: documentPath };
|
|
568
|
-
this.ui = { ...ui, path: uiPath };
|
|
749
|
+
this.ui = options.ui === false ? undefined : { ...ui, path: uiPath };
|
|
569
750
|
}
|
|
570
751
|
[onMount](app) {
|
|
571
752
|
const generated = openapi(app, {
|
|
@@ -573,6 +754,17 @@ class DocsController {
|
|
|
573
754
|
...this.options.servers ? { servers: this.options.servers } : {}
|
|
574
755
|
});
|
|
575
756
|
this.document = generated.document;
|
|
757
|
+
const ui = this.options.ui ?? "scalar";
|
|
758
|
+
const json = this.json;
|
|
759
|
+
const served = app.entries.find((entry) => entry.def === json);
|
|
760
|
+
if (ui !== false && served) {
|
|
761
|
+
this.page = docsPage({
|
|
762
|
+
ui,
|
|
763
|
+
documentUrl: served.path,
|
|
764
|
+
title: this.options.title ?? this.options.info.title,
|
|
765
|
+
...this.options.assets ? { assets: this.options.assets } : {}
|
|
766
|
+
});
|
|
767
|
+
}
|
|
576
768
|
const report = this.options.onWarning ?? warn;
|
|
577
769
|
for (const warning of generated.warnings) {
|
|
578
770
|
report(warning);
|
|
@@ -580,15 +772,7 @@ class DocsController {
|
|
|
580
772
|
}
|
|
581
773
|
}
|
|
582
774
|
function docs(options) {
|
|
583
|
-
|
|
584
|
-
const uiPath = options.uiPath ?? "/docs";
|
|
585
|
-
const page = docsPage({
|
|
586
|
-
ui: options.ui ?? "scalar",
|
|
587
|
-
documentUrl: documentPath,
|
|
588
|
-
title: options.title ?? options.info.title,
|
|
589
|
-
...options.assets ? { assets: options.assets } : {}
|
|
590
|
-
});
|
|
591
|
-
return new DocsController(options, documentPath, uiPath, page);
|
|
775
|
+
return new DocsController(options, options.path ?? "/openapi.json", options.uiPath ?? "/docs");
|
|
592
776
|
}
|
|
593
777
|
function warn(warning) {
|
|
594
778
|
console.warn(`[openapi] ${warning.route}: ${warning.message}`);
|
|
@@ -600,15 +784,17 @@ function openapi(app, options) {
|
|
|
600
784
|
const warnings = [];
|
|
601
785
|
const schemes = {};
|
|
602
786
|
const components = schemaComponents();
|
|
787
|
+
const envelopes2 = envelopes(components);
|
|
603
788
|
const ids = operationIds();
|
|
789
|
+
const described = app.entries.filter((entry) => !entry.ws && !entry.def.docs?.hidden);
|
|
790
|
+
for (const entry of described) {
|
|
791
|
+
declareEnvelopes(entry, envelopes2);
|
|
792
|
+
}
|
|
604
793
|
const occupied = new Map;
|
|
605
|
-
for (const entry of
|
|
606
|
-
if (entry.ws || entry.def.docs?.hidden) {
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
794
|
+
for (const entry of described) {
|
|
609
795
|
const template = toTemplate(entry.path);
|
|
610
796
|
const route = `${entry.method} ${entry.path}`;
|
|
611
|
-
const operation = operationOf(entry, app.options, components, ids, (message) => {
|
|
797
|
+
const operation = operationOf(entry, app.options, components, envelopes2, ids, (message) => {
|
|
612
798
|
warnings.push({ route, message });
|
|
613
799
|
});
|
|
614
800
|
for (const requirement of contributionsOf(entry.hooks).security) {
|
|
@@ -665,5 +851,5 @@ export {
|
|
|
665
851
|
secured
|
|
666
852
|
};
|
|
667
853
|
|
|
668
|
-
//# debugId=
|
|
854
|
+
//# debugId=8D8E4C09AE6F825C64756E2164756E21
|
|
669
855
|
//# sourceMappingURL=index.js.map
|