@kubb/adapter-oas 5.0.0-beta.106 → 5.0.0-beta.108
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 +10 -8
- package/dist/index.cjs +83 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +83 -8
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -15,8 +15,8 @@ import { childName, enumPropName, extractRefName, macroDiscriminatorEnum, macroE
|
|
|
15
15
|
const DEFAULT_PARSER_OPTIONS = {
|
|
16
16
|
dateType: "string",
|
|
17
17
|
integerType: "bigint",
|
|
18
|
-
unknownType: "
|
|
19
|
-
emptySchemaType: "
|
|
18
|
+
unknownType: "unknown",
|
|
19
|
+
emptySchemaType: "unknown",
|
|
20
20
|
enumSuffix: "enum"
|
|
21
21
|
};
|
|
22
22
|
/**
|
|
@@ -224,6 +224,20 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
|
|
|
224
224
|
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
225
225
|
}
|
|
226
226
|
//#endregion
|
|
227
|
+
//#region ../../internals/utils/src/errors.ts
|
|
228
|
+
/**
|
|
229
|
+
* Extracts a human-readable message from any thrown value.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```ts
|
|
233
|
+
* getErrorMessage(new Error('oops')) // 'oops'
|
|
234
|
+
* getErrorMessage('plain string') // 'plain string'
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
function getErrorMessage(value) {
|
|
238
|
+
return value instanceof Error ? value.message : String(value);
|
|
239
|
+
}
|
|
240
|
+
//#endregion
|
|
227
241
|
//#region ../../internals/utils/src/runtime.ts
|
|
228
242
|
/**
|
|
229
243
|
* Detects the JavaScript runtime executing the current process and exposes its name and version.
|
|
@@ -326,11 +340,50 @@ async function read(path) {
|
|
|
326
340
|
//#endregion
|
|
327
341
|
//#region src/load/source.ts
|
|
328
342
|
const urlRegExp = /^https?:\/+/i;
|
|
343
|
+
/**
|
|
344
|
+
* Node reports every connection failure as `TypeError: fetch failed` and keeps the useful part
|
|
345
|
+
* (`connect ECONNREFUSED 127.0.0.1:8000`) on `cause`, one level deeper again when a host resolves
|
|
346
|
+
* to several addresses and the attempts collect into an `AggregateError`.
|
|
347
|
+
*/
|
|
348
|
+
function describeFetchFailure(error) {
|
|
349
|
+
if (error instanceof AggregateError && error.errors.length > 0) return describeFetchFailure(error.errors[0]);
|
|
350
|
+
if (error instanceof Error && error.cause instanceof Error) return describeFetchFailure(error.cause) || error.message;
|
|
351
|
+
return getErrorMessage(error);
|
|
352
|
+
}
|
|
353
|
+
function helpForStatus(status) {
|
|
354
|
+
if (status === 401 || status === 403) return "The server refused the request. Kubb sends no credentials, so serve the document without authentication or download it and set `input` to the local file.";
|
|
355
|
+
if (status === 404) return "Check the URL. Open it in a browser or with `curl` to confirm it serves the OpenAPI document.";
|
|
356
|
+
if (status >= 500) return "The server failed while serving the document. Check that it is healthy, then run Kubb again.";
|
|
357
|
+
return "Open the URL in a browser or with `curl` to see what the server returns, then point `input` at a URL that serves the OpenAPI document.";
|
|
358
|
+
}
|
|
359
|
+
async function fetchSource(url) {
|
|
360
|
+
try {
|
|
361
|
+
return await fetch(url);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw new Diagnostics.Error({
|
|
364
|
+
code: Diagnostics.code.inputUnreachable,
|
|
365
|
+
severity: "error",
|
|
366
|
+
message: `Cannot reach ${url.href}: ${describeFetchFailure(error)}`,
|
|
367
|
+
help: "Check that the host is running and reachable from this machine. For a local server, start it and confirm the port matches the one in `input`.",
|
|
368
|
+
location: { kind: "config" },
|
|
369
|
+
cause: error instanceof Error ? error : void 0
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
329
373
|
async function readSource(sourcePath) {
|
|
330
374
|
if (urlRegExp.test(sourcePath)) {
|
|
331
375
|
const url = new URL(sourcePath);
|
|
332
|
-
const response = await
|
|
333
|
-
if (!response.ok)
|
|
376
|
+
const response = await fetchSource(url);
|
|
377
|
+
if (!response.ok) {
|
|
378
|
+
const status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);
|
|
379
|
+
throw new Diagnostics.Error({
|
|
380
|
+
code: Diagnostics.code.inputRequestFailed,
|
|
381
|
+
severity: "error",
|
|
382
|
+
message: `The server at ${url.href} answered with HTTP ${status} instead of the OpenAPI document.`,
|
|
383
|
+
help: helpForStatus(response.status),
|
|
384
|
+
location: { kind: "config" }
|
|
385
|
+
});
|
|
386
|
+
}
|
|
334
387
|
return response.text();
|
|
335
388
|
}
|
|
336
389
|
return read(sourcePath);
|
|
@@ -346,7 +399,8 @@ async function resolveSource(sourcePath) {
|
|
|
346
399
|
}
|
|
347
400
|
/**
|
|
348
401
|
* Throws a coded `KUBB_INPUT_NOT_FOUND` diagnostic when a local input path does not exist.
|
|
349
|
-
* URLs are skipped
|
|
402
|
+
* URLs are skipped: a remote input reports `KUBB_INPUT_REQUEST_FAILED` or `KUBB_INPUT_UNREACHABLE`
|
|
403
|
+
* from the request itself. A malformed but readable file is left for `parseDocument` to surface
|
|
350
404
|
* its parse error instead.
|
|
351
405
|
*/
|
|
352
406
|
async function assertInputExists(input) {
|
|
@@ -426,6 +480,24 @@ async function parseFromConfig(source) {
|
|
|
426
480
|
return parseDocument(resolved);
|
|
427
481
|
}
|
|
428
482
|
/**
|
|
483
|
+
* Asserts the parsed input is an OpenAPI or Swagger document.
|
|
484
|
+
*
|
|
485
|
+
* {@link validateDocument} keeps spec violations non-fatal so imperfect but usable documents still
|
|
486
|
+
* generate. That leniency also swallowed input that is not a document at all, which then produced
|
|
487
|
+
* an empty build with a success exit code. A missing version field is the one failure that cannot
|
|
488
|
+
* be a usable document, so it is fatal regardless of the `validate` option.
|
|
489
|
+
*/
|
|
490
|
+
function assertDocument(document) {
|
|
491
|
+
if (document && ("openapi" in document || "swagger" in document)) return;
|
|
492
|
+
throw new Diagnostics.Error({
|
|
493
|
+
code: Diagnostics.code.invalidDocument,
|
|
494
|
+
severity: "error",
|
|
495
|
+
message: "The resolved `input` is not an OpenAPI or Swagger document: it declares no `openapi` or `swagger` version.",
|
|
496
|
+
help: "Point `input` at a document that declares `openapi` or `swagger`. If you pass an object, pass the spec itself rather than a wrapper such as `{ path }` or `{ data }`.",
|
|
497
|
+
location: { kind: "config" }
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
429
501
|
* Validates an OpenAPI document using `@readme/openapi-parser` with colorized error output.
|
|
430
502
|
*
|
|
431
503
|
* @example
|
|
@@ -1600,9 +1672,9 @@ function convertObject({ schema, name, nullable, defaultValue, rawOptions, optio
|
|
|
1600
1672
|
/**
|
|
1601
1673
|
* Converts an OAS 3.1 `prefixItems` tuple into a `TupleSchemaNode`.
|
|
1602
1674
|
*/
|
|
1603
|
-
function convertTuple({ schema, name, nullable, defaultValue, rawOptions, parse }) {
|
|
1675
|
+
function convertTuple({ schema, name, nullable, defaultValue, rawOptions, options, parse }) {
|
|
1604
1676
|
const tupleItems = (schema.prefixItems ?? []).map((item) => parse({ schema: item }, rawOptions));
|
|
1605
|
-
const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? ast.factory.createSchema({ type:
|
|
1677
|
+
const rest = schema.items === false ? void 0 : !schema.items || schema.items === true ? ast.factory.createSchema({ type: options.unknownType }) : parse({ schema: schema.items }, rawOptions);
|
|
1606
1678
|
return createNode({
|
|
1607
1679
|
schema,
|
|
1608
1680
|
name,
|
|
@@ -2468,13 +2540,16 @@ const adapterOas = createAdapter((options) => {
|
|
|
2468
2540
|
},
|
|
2469
2541
|
async validate(input, options) {
|
|
2470
2542
|
await assertInputExists(input);
|
|
2471
|
-
|
|
2543
|
+
const document = await parseDocument(input);
|
|
2544
|
+
assertDocument(document);
|
|
2545
|
+
await validateDocument(document, options);
|
|
2472
2546
|
},
|
|
2473
2547
|
async parse(source) {
|
|
2474
2548
|
const cached = inputCache.get(source);
|
|
2475
2549
|
if (cached) return cached;
|
|
2476
2550
|
const promise = (async () => {
|
|
2477
2551
|
const document = await parseFromConfig(source);
|
|
2552
|
+
assertDocument(document);
|
|
2478
2553
|
if (validate) await validateDocument(document);
|
|
2479
2554
|
parsedDocument = document;
|
|
2480
2555
|
const refs = createRefs(document);
|