@kubb/core 5.0.0-beta.37 → 5.0.0-beta.39
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/{diagnostics-B0ONXReg.d.ts → diagnostics-DhfW8YpT.d.ts} +309 -350
- package/dist/index.cjs +569 -172
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +164 -119
- package/dist/index.js +548 -147
- package/dist/index.js.map +1 -1
- package/dist/{KubbDriver-CckeYpMG.js → memoryStorage-CNQTs-YG.js} +240 -166
- package/dist/memoryStorage-CNQTs-YG.js.map +1 -0
- package/dist/{KubbDriver-CXoKVRxI.cjs → memoryStorage-DHi1d0To.cjs} +255 -205
- package/dist/memoryStorage-DHi1d0To.cjs.map +1 -0
- package/dist/mocks.cjs +58 -5
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.ts +30 -3
- package/dist/mocks.js +57 -6
- package/dist/mocks.js.map +1 -1
- package/package.json +4 -4
- package/src/KubbDriver.ts +24 -74
- package/src/Telemetry.ts +278 -0
- package/src/constants.ts +14 -21
- package/src/createKubb.ts +22 -50
- package/src/createReporter.ts +75 -12
- package/src/defineGenerator.ts +2 -6
- package/src/defineLogger.ts +13 -1
- package/src/defineResolver.ts +3 -4
- package/src/diagnostics.ts +130 -61
- package/src/index.ts +8 -16
- package/src/mocks.ts +80 -4
- package/src/reporters/cliReporter.ts +90 -0
- package/src/reporters/fileReporter.ts +103 -0
- package/src/reporters/jsonReporter.ts +20 -0
- package/src/reporters/report.ts +85 -0
- package/src/types.ts +0 -1
- package/dist/KubbDriver-CXoKVRxI.cjs.map +0 -1
- package/dist/KubbDriver-CckeYpMG.js.map +0 -1
- package/src/devtools.ts +0 -53
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import "./chunk-C0LytTxp.js";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
|
+
import { styleText } from "node:util";
|
|
3
4
|
import path, { extname, resolve } from "node:path";
|
|
4
5
|
import { collectUsedSchemaNames, createFile, createStreamInput, extractStringsFromNodes, isOperationNode, isSchemaNode, transform } from "@kubb/ast";
|
|
5
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
|
-
import { deflateSync } from "fflate";
|
|
7
|
-
import { x } from "tinyexec";
|
|
8
7
|
//#region ../../internals/utils/src/errors.ts
|
|
9
8
|
/**
|
|
10
9
|
* Thrown when one or more errors occur during a Kubb build.
|
|
@@ -259,6 +258,21 @@ function getElapsedMs(hrStart) {
|
|
|
259
258
|
const ms = seconds * 1e3 + nanoseconds / 1e6;
|
|
260
259
|
return Math.round(ms * 100) / 100;
|
|
261
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```ts
|
|
266
|
+
* formatMs(250) // '250ms'
|
|
267
|
+
* formatMs(1500) // '1.50s'
|
|
268
|
+
* formatMs(90000) // '1m 30.0s'
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
function formatMs(ms) {
|
|
272
|
+
if (ms >= 6e4) return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(1)}s`;
|
|
273
|
+
if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
|
|
274
|
+
return `${Math.round(ms)}ms`;
|
|
275
|
+
}
|
|
262
276
|
//#endregion
|
|
263
277
|
//#region ../../internals/utils/src/promise.ts
|
|
264
278
|
function* chunks(arr, size) {
|
|
@@ -620,11 +634,14 @@ var URLPath = class {
|
|
|
620
634
|
}
|
|
621
635
|
};
|
|
622
636
|
//#endregion
|
|
637
|
+
//#region package.json
|
|
638
|
+
var version = "5.0.0-beta.39";
|
|
639
|
+
//#endregion
|
|
623
640
|
//#region src/constants.ts
|
|
624
641
|
/**
|
|
625
|
-
*
|
|
642
|
+
* OpenTelemetry ingestion endpoint for anonymous usage telemetry.
|
|
626
643
|
*/
|
|
627
|
-
const
|
|
644
|
+
const OTLP_ENDPOINT = "https://otlp.kubb.dev";
|
|
628
645
|
/**
|
|
629
646
|
* Plugin `include` filter types that select operations directly. When one of these is set
|
|
630
647
|
* without a `schemaName` include, the generate phase pre-scans operations to compute the set
|
|
@@ -638,18 +655,6 @@ const OPERATION_FILTER_TYPES = new Set([
|
|
|
638
655
|
"contentType"
|
|
639
656
|
]);
|
|
640
657
|
/**
|
|
641
|
-
* Numeric log-level thresholds used internally to compare verbosity.
|
|
642
|
-
*
|
|
643
|
-
* Higher numbers are more verbose.
|
|
644
|
-
*/
|
|
645
|
-
const logLevel = {
|
|
646
|
-
silent: Number.NEGATIVE_INFINITY,
|
|
647
|
-
error: 0,
|
|
648
|
-
warn: 1,
|
|
649
|
-
info: 3,
|
|
650
|
-
verbose: 4
|
|
651
|
-
};
|
|
652
|
-
/**
|
|
653
658
|
* Stable codes Kubb attaches to a `Diagnostic`. Each maps to a known failure mode
|
|
654
659
|
* and stays stable so it can be referenced in tooling and (later) docs. Reference
|
|
655
660
|
* these instead of inlining the string at a throw site.
|
|
@@ -707,10 +712,6 @@ const diagnosticCode = {
|
|
|
707
712
|
*/
|
|
708
713
|
adapterRequired: "KUBB_ADAPTER_REQUIRED",
|
|
709
714
|
/**
|
|
710
|
-
* The `devtools` config is set to something other than an object.
|
|
711
|
-
*/
|
|
712
|
-
devtoolsInvalid: "KUBB_DEVTOOLS_INVALID",
|
|
713
|
-
/**
|
|
714
715
|
* A resolved output path escapes the output directory, which can stem from a path
|
|
715
716
|
* traversal in the spec or a misconfigured `group.name`.
|
|
716
717
|
*/
|
|
@@ -741,19 +742,19 @@ const diagnosticCode = {
|
|
|
741
742
|
/**
|
|
742
743
|
* Docs major, derived from the package version so the link tracks the published major.
|
|
743
744
|
*/
|
|
744
|
-
const docsMajor =
|
|
745
|
+
const docsMajor = version.split(".")[0] ?? "5";
|
|
745
746
|
/**
|
|
746
747
|
* Narrows a {@link Diagnostic} to the variant for `code`, or `null` when it does not match.
|
|
747
748
|
*
|
|
748
749
|
* @example
|
|
749
750
|
* ```ts
|
|
750
|
-
* const update =
|
|
751
|
+
* const update = narrow(diagnostic, diagnosticCode.updateAvailable)
|
|
751
752
|
* if (update) {
|
|
752
753
|
* console.log(update.latestVersion)
|
|
753
754
|
* }
|
|
754
755
|
* ```
|
|
755
756
|
*/
|
|
756
|
-
function
|
|
757
|
+
function narrow(diagnostic, code) {
|
|
757
758
|
return diagnostic.code === code ? diagnostic : null;
|
|
758
759
|
}
|
|
759
760
|
/**
|
|
@@ -768,59 +769,51 @@ function isKind(kind) {
|
|
|
768
769
|
*
|
|
769
770
|
* @example
|
|
770
771
|
* ```ts
|
|
771
|
-
* if (
|
|
772
|
+
* if (isProblem(diagnostic)) {
|
|
772
773
|
* console.log(diagnostic.location)
|
|
773
774
|
* }
|
|
774
775
|
* ```
|
|
775
776
|
*/
|
|
776
|
-
const
|
|
777
|
+
const isProblem = isKind("problem");
|
|
777
778
|
/**
|
|
778
779
|
* Returns `true` when the diagnostic is a per-plugin {@link PerformanceDiagnostic}.
|
|
779
780
|
*
|
|
780
781
|
* @example
|
|
781
782
|
* ```ts
|
|
782
|
-
* const timings = diagnostics.filter(
|
|
783
|
+
* const timings = diagnostics.filter(isPerformance)
|
|
783
784
|
* ```
|
|
784
785
|
*/
|
|
785
|
-
const
|
|
786
|
+
const isPerformance = isKind("performance");
|
|
786
787
|
/**
|
|
787
788
|
* Returns `true` when the diagnostic is a version-update {@link UpdateDiagnostic}.
|
|
788
789
|
*
|
|
789
790
|
* @example
|
|
790
791
|
* ```ts
|
|
791
|
-
* if (
|
|
792
|
+
* if (isUpdate(diagnostic)) {
|
|
792
793
|
* console.log(diagnostic.latestVersion)
|
|
793
794
|
* }
|
|
794
795
|
* ```
|
|
795
796
|
*/
|
|
796
|
-
const
|
|
797
|
+
const isUpdate = isKind("update");
|
|
797
798
|
/**
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
* @example
|
|
802
|
-
* ```ts
|
|
803
|
-
* throw new DiagnosticError({ code: diagnosticCode.refNotFound, severity: 'error', message: `Could not find ${ref}`, location: { kind: 'schema', pointer: ref, ref } })
|
|
804
|
-
* ```
|
|
799
|
+
* Glyph and accent color per severity, matching the miette/oxlint convention
|
|
800
|
+
* (`×` error, `⚠` warning, `ℹ` advice).
|
|
805
801
|
*/
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
802
|
+
const severityStyle = {
|
|
803
|
+
error: {
|
|
804
|
+
glyph: "×",
|
|
805
|
+
color: "red"
|
|
806
|
+
},
|
|
807
|
+
warning: {
|
|
808
|
+
glyph: "⚠",
|
|
809
|
+
color: "yellow"
|
|
810
|
+
},
|
|
811
|
+
info: {
|
|
812
|
+
glyph: "ℹ",
|
|
813
|
+
color: "blue"
|
|
812
814
|
}
|
|
813
815
|
};
|
|
814
816
|
/**
|
|
815
|
-
* Structural check for a {@link DiagnosticError}, including one thrown from a duplicated
|
|
816
|
-
* `@kubb/core` copy where `instanceof` fails. Matches on the `name` and a `diagnostic`
|
|
817
|
-
* that carries a `code`.
|
|
818
|
-
*/
|
|
819
|
-
function isDiagnosticError(error) {
|
|
820
|
-
if (error instanceof DiagnosticError) return true;
|
|
821
|
-
return error instanceof Error && error.name === "DiagnosticError" && "diagnostic" in error && typeof error.diagnostic === "object" && error.diagnostic !== null && typeof error.diagnostic?.code === "string";
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
817
|
* Explanation for every {@link diagnosticCode}. Use {@link Diagnostics.explain} to look one up
|
|
825
818
|
* and `Diagnostics.docsUrl` for the matching kubb.dev page.
|
|
826
819
|
*/
|
|
@@ -882,14 +875,9 @@ const diagnosticCatalog = {
|
|
|
882
875
|
},
|
|
883
876
|
[diagnosticCode.adapterRequired]: {
|
|
884
877
|
title: "Adapter required",
|
|
885
|
-
cause: "An action needs an adapter
|
|
878
|
+
cause: "An action needs an adapter but none is configured.",
|
|
886
879
|
fix: "Set `adapter` in kubb.config.ts, for example `adapterOas()`."
|
|
887
880
|
},
|
|
888
|
-
[diagnosticCode.devtoolsInvalid]: {
|
|
889
|
-
title: "Invalid devtools config",
|
|
890
|
-
cause: "The `devtools` config is set to something other than an object.",
|
|
891
|
-
fix: "Set `devtools` to an options object, or remove it to disable Kubb Studio."
|
|
892
|
-
},
|
|
893
881
|
[diagnosticCode.pathTraversal]: {
|
|
894
882
|
title: "Path traversal",
|
|
895
883
|
cause: "A resolved output path escaped the output directory, which can stem from a path traversal in the spec or a misconfigured `group.name`.",
|
|
@@ -933,6 +921,52 @@ const diagnosticCatalog = {
|
|
|
933
921
|
var Diagnostics = class Diagnostics {
|
|
934
922
|
static #reporterStorage = new AsyncLocalStorage();
|
|
935
923
|
/**
|
|
924
|
+
* The diagnostic code catalog, exposed as `Diagnostics.code` (e.g. `Diagnostics.code.refNotFound`).
|
|
925
|
+
*/
|
|
926
|
+
static code = diagnosticCode;
|
|
927
|
+
/**
|
|
928
|
+
* Type guard for a build {@link ProblemDiagnostic}.
|
|
929
|
+
*/
|
|
930
|
+
static isProblem = isProblem;
|
|
931
|
+
/**
|
|
932
|
+
* Type guard for a version-update {@link UpdateDiagnostic}.
|
|
933
|
+
*/
|
|
934
|
+
static isUpdate = isUpdate;
|
|
935
|
+
/**
|
|
936
|
+
* Type guard for a per-plugin {@link PerformanceDiagnostic}.
|
|
937
|
+
*/
|
|
938
|
+
static isPerformance = isPerformance;
|
|
939
|
+
/**
|
|
940
|
+
* Narrows a {@link Diagnostic} to the variant for `code`, or `null` when it does not match.
|
|
941
|
+
*/
|
|
942
|
+
static narrow = narrow;
|
|
943
|
+
/**
|
|
944
|
+
* An `Error` that carries a {@link Diagnostic}, so structured problems can flow
|
|
945
|
+
* through the existing throw/catch paths while keeping their code and location.
|
|
946
|
+
*
|
|
947
|
+
* @example
|
|
948
|
+
* ```ts
|
|
949
|
+
* throw new Diagnostics.Error({ code: diagnosticCode.refNotFound, severity: 'error', message: `Could not find ${ref}`, location: { kind: 'schema', pointer: ref, ref } })
|
|
950
|
+
* ```
|
|
951
|
+
*/
|
|
952
|
+
static Error = class DiagnosticError extends Error {
|
|
953
|
+
diagnostic;
|
|
954
|
+
constructor(diagnostic) {
|
|
955
|
+
super(diagnostic.message, { cause: diagnostic.cause });
|
|
956
|
+
this.name = "DiagnosticError";
|
|
957
|
+
this.diagnostic = diagnostic;
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
/**
|
|
961
|
+
* Structural check for a {@link Diagnostics.Error}, including one thrown from a duplicated
|
|
962
|
+
* `@kubb/core` copy where `instanceof` fails. Matches on the `name` and a `diagnostic`
|
|
963
|
+
* that carries a `code`.
|
|
964
|
+
*/
|
|
965
|
+
static isError(error) {
|
|
966
|
+
if (error instanceof Diagnostics.Error) return true;
|
|
967
|
+
return error instanceof Error && error.name === "DiagnosticError" && "diagnostic" in error && typeof error.diagnostic === "object" && error.diagnostic !== null && typeof error.diagnostic?.code === "string";
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
936
970
|
* Runs `fn` with `sink` as the active diagnostic sink for the whole async
|
|
937
971
|
* subtree, so {@link Diagnostics.report} reaches it from anywhere inside.
|
|
938
972
|
*/
|
|
@@ -960,7 +994,7 @@ var Diagnostics = class Diagnostics {
|
|
|
960
994
|
await hooks.emit("kubb:diagnostic", { diagnostic });
|
|
961
995
|
}
|
|
962
996
|
/**
|
|
963
|
-
* Coerces any thrown value into a {@link ProblemDiagnostic}. A {@link
|
|
997
|
+
* Coerces any thrown value into a {@link ProblemDiagnostic}. A {@link Diagnostics.Error}
|
|
964
998
|
* keeps its structured data, and anything else becomes a `KUBB_UNKNOWN` error.
|
|
965
999
|
*/
|
|
966
1000
|
static from(error) {
|
|
@@ -968,7 +1002,7 @@ var Diagnostics = class Diagnostics {
|
|
|
968
1002
|
let current = error;
|
|
969
1003
|
let root;
|
|
970
1004
|
while (current instanceof Error && !seen.has(current)) {
|
|
971
|
-
if (
|
|
1005
|
+
if (Diagnostics.isError(current)) return current.diagnostic;
|
|
972
1006
|
seen.add(current);
|
|
973
1007
|
root = current;
|
|
974
1008
|
current = current.cause;
|
|
@@ -1031,7 +1065,7 @@ var Diagnostics = class Diagnostics {
|
|
|
1031
1065
|
let warnings = 0;
|
|
1032
1066
|
let infos = 0;
|
|
1033
1067
|
for (const diagnostic of diagnostics) {
|
|
1034
|
-
if (!
|
|
1068
|
+
if (!isProblem(diagnostic)) continue;
|
|
1035
1069
|
if (diagnostic.severity === "error") errors += 1;
|
|
1036
1070
|
else if (diagnostic.severity === "warning") warnings += 1;
|
|
1037
1071
|
else infos += 1;
|
|
@@ -1051,7 +1085,7 @@ var Diagnostics = class Diagnostics {
|
|
|
1051
1085
|
const seen = /* @__PURE__ */ new Set();
|
|
1052
1086
|
const result = [];
|
|
1053
1087
|
for (const diagnostic of diagnostics) {
|
|
1054
|
-
if (!
|
|
1088
|
+
if (!isProblem(diagnostic)) {
|
|
1055
1089
|
result.push(diagnostic);
|
|
1056
1090
|
continue;
|
|
1057
1091
|
}
|
|
@@ -1083,7 +1117,7 @@ var Diagnostics = class Diagnostics {
|
|
|
1083
1117
|
* fields are omitted rather than set to `undefined`.
|
|
1084
1118
|
*/
|
|
1085
1119
|
static serialize(diagnostic) {
|
|
1086
|
-
const problem =
|
|
1120
|
+
const problem = isProblem(diagnostic) ? diagnostic : void 0;
|
|
1087
1121
|
return {
|
|
1088
1122
|
code: diagnostic.code,
|
|
1089
1123
|
severity: diagnostic.severity,
|
|
@@ -1094,8 +1128,82 @@ var Diagnostics = class Diagnostics {
|
|
|
1094
1128
|
...diagnostic.code === diagnosticCode.unknown ? {} : { docsUrl: Diagnostics.docsUrl(diagnostic.code) }
|
|
1095
1129
|
};
|
|
1096
1130
|
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Renders a {@link Diagnostic} for terminal output as its parts: the colored severity `symbol`
|
|
1133
|
+
* (the gutter glyph), the `plugin(CODE): message` `headline`, and the `details` lines (optional
|
|
1134
|
+
* `at <pointer>`, `help:`, and `docs:`).
|
|
1135
|
+
*
|
|
1136
|
+
* Hosts compose these to fit their gutter: a clack logger passes `symbol` as its own gutter and
|
|
1137
|
+
* `[headline, ...details]` as the message, while plain text outputs use {@link Diagnostics.formatLines}.
|
|
1138
|
+
*/
|
|
1139
|
+
static format(diagnostic) {
|
|
1140
|
+
const { code, severity, message } = diagnostic;
|
|
1141
|
+
const { glyph, color } = severityStyle[severity];
|
|
1142
|
+
const problem = isProblem(diagnostic) ? diagnostic : void 0;
|
|
1143
|
+
const rule = styleText(color, styleText("bold", problem?.plugin ? `${problem.plugin}(${code})` : code));
|
|
1144
|
+
const details = [];
|
|
1145
|
+
if (problem?.location && "pointer" in problem.location) details.push(` ${styleText("dim", "at")} ${styleText("cyan", problem.location.pointer)}`);
|
|
1146
|
+
if (problem?.help) details.push(` ${styleText("cyan", "help:")} ${problem.help}`);
|
|
1147
|
+
if (code !== diagnosticCode.unknown) details.push(` ${styleText("dim", "docs:")} ${styleText("cyan", Diagnostics.docsUrl(code))}`);
|
|
1148
|
+
return {
|
|
1149
|
+
symbol: styleText(color, styleText("bold", glyph)),
|
|
1150
|
+
headline: `${rule}: ${message}`,
|
|
1151
|
+
details
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* The self-contained block form of {@link Diagnostics.format}: `${symbol} ${headline}` followed by
|
|
1156
|
+
* the detail lines. Used where there is no gutter to own the symbol (plain and file output).
|
|
1157
|
+
*/
|
|
1158
|
+
static formatLines(diagnostic) {
|
|
1159
|
+
const { symbol, headline, details } = Diagnostics.format(diagnostic);
|
|
1160
|
+
return [`${symbol} ${headline}`, ...details];
|
|
1161
|
+
}
|
|
1097
1162
|
};
|
|
1098
1163
|
//#endregion
|
|
1164
|
+
//#region src/createStorage.ts
|
|
1165
|
+
/**
|
|
1166
|
+
* Defines a custom storage backend. The builder receives user options and
|
|
1167
|
+
* returns a `Storage` implementation. Kubb ships with filesystem and
|
|
1168
|
+
* in-memory storages, reach for this when you need to write generated files
|
|
1169
|
+
* elsewhere (cloud storage, a database, a remote API).
|
|
1170
|
+
*
|
|
1171
|
+
* @example In-memory storage (the built-in implementation)
|
|
1172
|
+
* ```ts
|
|
1173
|
+
* import { createStorage } from '@kubb/core'
|
|
1174
|
+
*
|
|
1175
|
+
* export const memoryStorage = createStorage(() => {
|
|
1176
|
+
* const store = new Map<string, string>()
|
|
1177
|
+
*
|
|
1178
|
+
* return {
|
|
1179
|
+
* name: 'memory',
|
|
1180
|
+
* async hasItem(key) {
|
|
1181
|
+
* return store.has(key)
|
|
1182
|
+
* },
|
|
1183
|
+
* async getItem(key) {
|
|
1184
|
+
* return store.get(key) ?? null
|
|
1185
|
+
* },
|
|
1186
|
+
* async setItem(key, value) {
|
|
1187
|
+
* store.set(key, value)
|
|
1188
|
+
* },
|
|
1189
|
+
* async removeItem(key) {
|
|
1190
|
+
* store.delete(key)
|
|
1191
|
+
* },
|
|
1192
|
+
* async getKeys(base) {
|
|
1193
|
+
* const keys = [...store.keys()]
|
|
1194
|
+
* return base ? keys.filter((k) => k.startsWith(base)) : keys
|
|
1195
|
+
* },
|
|
1196
|
+
* async clear(base) {
|
|
1197
|
+
* if (!base) store.clear()
|
|
1198
|
+
* },
|
|
1199
|
+
* }
|
|
1200
|
+
* })
|
|
1201
|
+
* ```
|
|
1202
|
+
*/
|
|
1203
|
+
function createStorage(build) {
|
|
1204
|
+
return (options) => build(options ?? {});
|
|
1205
|
+
}
|
|
1206
|
+
//#endregion
|
|
1099
1207
|
//#region src/definePlugin.ts
|
|
1100
1208
|
/**
|
|
1101
1209
|
* Wraps a plugin factory and returns a function that accepts user options and
|
|
@@ -1323,8 +1431,8 @@ function defaultResolvePath({ baseName, pathMode, tag, path: groupPath }, { root
|
|
|
1323
1431
|
})();
|
|
1324
1432
|
const outputDir = path.resolve(root, output.path);
|
|
1325
1433
|
const outputDirWithSep = outputDir.endsWith(path.sep) ? outputDir : `${outputDir}${path.sep}`;
|
|
1326
|
-
if (result !== outputDir && !result.startsWith(outputDirWithSep)) throw new
|
|
1327
|
-
code:
|
|
1434
|
+
if (result !== outputDir && !result.startsWith(outputDirWithSep)) throw new Diagnostics.Error({
|
|
1435
|
+
code: Diagnostics.code.pathTraversal,
|
|
1328
1436
|
severity: "error",
|
|
1329
1437
|
message: `Resolved path "${result}" is outside the output directory "${outputDir}".`,
|
|
1330
1438
|
help: "This can stem from a path traversal in the OpenAPI specification or a misconfigured `group.name` function. Keep generated paths within the output directory.",
|
|
@@ -1548,47 +1656,6 @@ function defineResolver(build) {
|
|
|
1548
1656
|
return resolver;
|
|
1549
1657
|
}
|
|
1550
1658
|
//#endregion
|
|
1551
|
-
//#region src/devtools.ts
|
|
1552
|
-
/**
|
|
1553
|
-
* Encodes an `InputNode` as a compressed, URL-safe string.
|
|
1554
|
-
*
|
|
1555
|
-
* The JSON representation is deflate-compressed with {@link deflateSync} before
|
|
1556
|
-
* base64url encoding, which typically reduces payload size by 70, 80 % and
|
|
1557
|
-
* keeps URLs well within browser and server path-length limits.
|
|
1558
|
-
*/
|
|
1559
|
-
function encodeAst(input) {
|
|
1560
|
-
const compressed = deflateSync(new TextEncoder().encode(JSON.stringify(input)));
|
|
1561
|
-
return Buffer.from(compressed).toString("base64url");
|
|
1562
|
-
}
|
|
1563
|
-
/**
|
|
1564
|
-
* Constructs the Kubb Studio URL for the given `InputNode`.
|
|
1565
|
-
* When `options.ast` is `true`, navigates to the AST inspector (`/ast`).
|
|
1566
|
-
* The `input` is encoded and attached as the `?root=` query parameter so Studio
|
|
1567
|
-
* can decode and render it without a round-trip to any server.
|
|
1568
|
-
*/
|
|
1569
|
-
function getStudioUrl(input, studioUrl, options = {}) {
|
|
1570
|
-
return `${studioUrl.replace(/\/$/, "")}${options.ast ? "/ast" : ""}?root=${encodeAst(input)}`;
|
|
1571
|
-
}
|
|
1572
|
-
/**
|
|
1573
|
-
* Opens the Kubb Studio URL for the given `InputNode` in the default browser, *
|
|
1574
|
-
* Falls back to printing the URL if the browser cannot be launched.
|
|
1575
|
-
*/
|
|
1576
|
-
async function openInStudio(input, studioUrl, options = {}) {
|
|
1577
|
-
const url = getStudioUrl(input, studioUrl, options);
|
|
1578
|
-
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
1579
|
-
const args = process.platform === "win32" ? [
|
|
1580
|
-
"/c",
|
|
1581
|
-
"start",
|
|
1582
|
-
"",
|
|
1583
|
-
url
|
|
1584
|
-
] : [url];
|
|
1585
|
-
try {
|
|
1586
|
-
await x(cmd, args);
|
|
1587
|
-
} catch {
|
|
1588
|
-
console.log(`\n ${url}\n`);
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
//#endregion
|
|
1592
1659
|
//#region src/FileManager.ts
|
|
1593
1660
|
function mergeFile(a, b) {
|
|
1594
1661
|
return {
|
|
@@ -1994,19 +2061,10 @@ var KubbDriver = class KubbDriver {
|
|
|
1994
2061
|
inputNode = null;
|
|
1995
2062
|
adapter = null;
|
|
1996
2063
|
/**
|
|
1997
|
-
*
|
|
1998
|
-
*
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
* - `isOpen` prevents opening the studio more than once per build.
|
|
2002
|
-
* - `inputNode` caches the parse promise so `adapter.parse()` is called at most once
|
|
2003
|
-
* per studio session, even when `openInStudio()` is called multiple times.
|
|
2004
|
-
*/
|
|
2005
|
-
#studio = {
|
|
2006
|
-
source: null,
|
|
2007
|
-
isOpen: false,
|
|
2008
|
-
inputNode: null
|
|
2009
|
-
};
|
|
2064
|
+
* Raw adapter source so `adapter.parse()` / `adapter.stream()` can run lazily.
|
|
2065
|
+
* Intentionally outlives the build, cleared by `dispose()`.
|
|
2066
|
+
*/
|
|
2067
|
+
#adapterSource = null;
|
|
2010
2068
|
/**
|
|
2011
2069
|
* Central file store for all generated files.
|
|
2012
2070
|
* Plugins should use `this.addFile()` / `this.upsertFile()` (via their context) to
|
|
@@ -2051,7 +2109,7 @@ var KubbDriver = class KubbDriver {
|
|
|
2051
2109
|
this.plugins.set(plugin.name, plugin);
|
|
2052
2110
|
}
|
|
2053
2111
|
if (this.config.middleware) for (const middleware of this.config.middleware) for (const event of Object.keys(middleware.hooks)) this.#registerMiddleware(event, middleware.hooks);
|
|
2054
|
-
if (this.config.adapter) this.#
|
|
2112
|
+
if (this.config.adapter) this.#adapterSource = inputToAdapterSource(this.config);
|
|
2055
2113
|
}
|
|
2056
2114
|
get hooks() {
|
|
2057
2115
|
return this.options.hooks;
|
|
@@ -2077,14 +2135,14 @@ var KubbDriver = class KubbDriver {
|
|
|
2077
2135
|
}
|
|
2078
2136
|
/**
|
|
2079
2137
|
* Parses the adapter source into `this.inputNode`. Idempotent, so repeated calls from
|
|
2080
|
-
* `run`
|
|
2138
|
+
* `run` do not re-parse. Adapters with `stream()` are used directly.
|
|
2081
2139
|
* Adapters with only `parse()` are wrapped via `createStreamInput` so the dispatch loop
|
|
2082
2140
|
* stays stream-only.
|
|
2083
2141
|
*/
|
|
2084
2142
|
async #parseInput() {
|
|
2085
|
-
if (this.inputNode || !this.adapter || !this.#
|
|
2143
|
+
if (this.inputNode || !this.adapter || !this.#adapterSource) return;
|
|
2086
2144
|
const adapter = this.adapter;
|
|
2087
|
-
const source = this.#
|
|
2145
|
+
const source = this.#adapterSource;
|
|
2088
2146
|
if (adapter.stream) {
|
|
2089
2147
|
this.inputNode = await adapter.stream(source);
|
|
2090
2148
|
return;
|
|
@@ -2607,11 +2665,7 @@ var KubbDriver = class KubbDriver {
|
|
|
2607
2665
|
this.#defaultResolvers.clear();
|
|
2608
2666
|
this.fileManager.dispose();
|
|
2609
2667
|
this.inputNode = null;
|
|
2610
|
-
this.#
|
|
2611
|
-
source: null,
|
|
2612
|
-
isOpen: false,
|
|
2613
|
-
inputNode: null
|
|
2614
|
-
};
|
|
2668
|
+
this.#adapterSource = null;
|
|
2615
2669
|
}
|
|
2616
2670
|
[Symbol.dispose]() {
|
|
2617
2671
|
this.dispose();
|
|
@@ -2644,15 +2698,6 @@ var KubbDriver = class KubbDriver {
|
|
|
2644
2698
|
...diagnostic,
|
|
2645
2699
|
plugin: plugin.name
|
|
2646
2700
|
});
|
|
2647
|
-
if (diagnostic.severity === "error") {
|
|
2648
|
-
driver.hooks.emit("kubb:error", { error: diagnostic.cause ?? new Error(diagnostic.message) });
|
|
2649
|
-
return;
|
|
2650
|
-
}
|
|
2651
|
-
if (diagnostic.severity === "warning") {
|
|
2652
|
-
driver.hooks.emit("kubb:warn", { message: diagnostic.message });
|
|
2653
|
-
return;
|
|
2654
|
-
}
|
|
2655
|
-
driver.hooks.emit("kubb:info", { message: diagnostic.message });
|
|
2656
2701
|
};
|
|
2657
2702
|
return {
|
|
2658
2703
|
config: driver.config,
|
|
@@ -2691,7 +2736,7 @@ var KubbDriver = class KubbDriver {
|
|
|
2691
2736
|
},
|
|
2692
2737
|
warn(message) {
|
|
2693
2738
|
report({
|
|
2694
|
-
code:
|
|
2739
|
+
code: Diagnostics.code.pluginWarning,
|
|
2695
2740
|
severity: "warning",
|
|
2696
2741
|
message
|
|
2697
2742
|
});
|
|
@@ -2699,7 +2744,7 @@ var KubbDriver = class KubbDriver {
|
|
|
2699
2744
|
error(error) {
|
|
2700
2745
|
const cause = typeof error === "string" ? void 0 : error;
|
|
2701
2746
|
report({
|
|
2702
|
-
code:
|
|
2747
|
+
code: Diagnostics.code.pluginFailed,
|
|
2703
2748
|
severity: "error",
|
|
2704
2749
|
message: typeof error === "string" ? error : error.message,
|
|
2705
2750
|
cause
|
|
@@ -2707,31 +2752,10 @@ var KubbDriver = class KubbDriver {
|
|
|
2707
2752
|
},
|
|
2708
2753
|
info(message) {
|
|
2709
2754
|
report({
|
|
2710
|
-
code:
|
|
2755
|
+
code: Diagnostics.code.pluginInfo,
|
|
2711
2756
|
severity: "info",
|
|
2712
2757
|
message
|
|
2713
2758
|
});
|
|
2714
|
-
},
|
|
2715
|
-
async openInStudio(options) {
|
|
2716
|
-
if (!driver.config.devtools || driver.#studio.isOpen) return;
|
|
2717
|
-
if (typeof driver.config.devtools !== "object") throw new DiagnosticError({
|
|
2718
|
-
code: diagnosticCode.devtoolsInvalid,
|
|
2719
|
-
severity: "error",
|
|
2720
|
-
message: "The `devtools` config must be an object.",
|
|
2721
|
-
help: "Set `devtools` to an options object, or remove it to disable Kubb Studio.",
|
|
2722
|
-
location: { kind: "config" }
|
|
2723
|
-
});
|
|
2724
|
-
if (!driver.adapter || !driver.#studio.source) throw new DiagnosticError({
|
|
2725
|
-
code: diagnosticCode.adapterRequired,
|
|
2726
|
-
severity: "error",
|
|
2727
|
-
message: "An adapter is required to open Kubb Studio, but none is configured.",
|
|
2728
|
-
help: "Set `adapter` in kubb.config.ts (for example `adapterOas()`).",
|
|
2729
|
-
location: { kind: "config" }
|
|
2730
|
-
});
|
|
2731
|
-
driver.#studio.isOpen = true;
|
|
2732
|
-
const studioUrl = driver.config.devtools?.studioUrl ?? "https://kubb.studio";
|
|
2733
|
-
driver.#studio.inputNode ??= Promise.resolve(driver.adapter.parse(driver.#studio.source));
|
|
2734
|
-
return openInStudio(await driver.#studio.inputNode, studioUrl, options);
|
|
2735
2759
|
}
|
|
2736
2760
|
};
|
|
2737
2761
|
}
|
|
@@ -2740,8 +2764,8 @@ var KubbDriver = class KubbDriver {
|
|
|
2740
2764
|
}
|
|
2741
2765
|
requirePlugin(pluginName) {
|
|
2742
2766
|
const plugin = this.plugins.get(pluginName);
|
|
2743
|
-
if (!plugin) throw new
|
|
2744
|
-
code:
|
|
2767
|
+
if (!plugin) throw new Diagnostics.Error({
|
|
2768
|
+
code: Diagnostics.code.pluginNotFound,
|
|
2745
2769
|
severity: "error",
|
|
2746
2770
|
message: `Plugin "${pluginName}" is required but not found. Make sure it is included in your Kubb config.`,
|
|
2747
2771
|
help: `Add "${pluginName}" to the \`plugins\` array in kubb.config.ts, or remove the dependency on it.`,
|
|
@@ -2752,8 +2776,8 @@ var KubbDriver = class KubbDriver {
|
|
|
2752
2776
|
};
|
|
2753
2777
|
function inputToAdapterSource(config) {
|
|
2754
2778
|
const input = config.input;
|
|
2755
|
-
if (!input) throw new
|
|
2756
|
-
code:
|
|
2779
|
+
if (!input) throw new Diagnostics.Error({
|
|
2780
|
+
code: Diagnostics.code.inputRequired,
|
|
2757
2781
|
severity: "error",
|
|
2758
2782
|
message: "An adapter is configured without an input.",
|
|
2759
2783
|
help: "Provide `input.path` (a file or URL) or `input.data` (an inline spec) in your Kubb config.",
|
|
@@ -2773,6 +2797,56 @@ function inputToAdapterSource(config) {
|
|
|
2773
2797
|
};
|
|
2774
2798
|
}
|
|
2775
2799
|
//#endregion
|
|
2776
|
-
|
|
2800
|
+
//#region src/storages/memoryStorage.ts
|
|
2801
|
+
/**
|
|
2802
|
+
* In-memory storage driver. Useful for testing and dry-run scenarios where
|
|
2803
|
+
* generated output should be captured without touching the filesystem.
|
|
2804
|
+
*
|
|
2805
|
+
* All data lives in a `Map` scoped to the storage instance and is discarded
|
|
2806
|
+
* when the instance is garbage-collected.
|
|
2807
|
+
*
|
|
2808
|
+
* @example
|
|
2809
|
+
* ```ts
|
|
2810
|
+
* import { memoryStorage } from '@kubb/core'
|
|
2811
|
+
* import { defineConfig } from 'kubb'
|
|
2812
|
+
*
|
|
2813
|
+
* export default defineConfig({
|
|
2814
|
+
* input: { path: './petStore.yaml' },
|
|
2815
|
+
* output: { path: './src/gen' },
|
|
2816
|
+
* storage: memoryStorage(),
|
|
2817
|
+
* })
|
|
2818
|
+
* ```
|
|
2819
|
+
*/
|
|
2820
|
+
const memoryStorage = createStorage(() => {
|
|
2821
|
+
const store = /* @__PURE__ */ new Map();
|
|
2822
|
+
return {
|
|
2823
|
+
name: "memory",
|
|
2824
|
+
async hasItem(key) {
|
|
2825
|
+
return store.has(key);
|
|
2826
|
+
},
|
|
2827
|
+
async getItem(key) {
|
|
2828
|
+
return store.get(key) ?? null;
|
|
2829
|
+
},
|
|
2830
|
+
async setItem(key, value) {
|
|
2831
|
+
store.set(key, value);
|
|
2832
|
+
},
|
|
2833
|
+
async removeItem(key) {
|
|
2834
|
+
store.delete(key);
|
|
2835
|
+
},
|
|
2836
|
+
async getKeys(base) {
|
|
2837
|
+
const keys = [...store.keys()];
|
|
2838
|
+
return base ? keys.filter((k) => k.startsWith(base)) : keys;
|
|
2839
|
+
},
|
|
2840
|
+
async clear(base) {
|
|
2841
|
+
if (!base) {
|
|
2842
|
+
store.clear();
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
for (const key of store.keys()) if (key.startsWith(base)) store.delete(key);
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
});
|
|
2849
|
+
//#endregion
|
|
2850
|
+
export { FileManager as a, createStorage as c, formatMs as d, getElapsedMs as f, BuildError as h, FileProcessor as i, Diagnostics as l, AsyncEventEmitter as m, KubbDriver as n, defineResolver as o, camelCase as p, _usingCtx as r, definePlugin as s, memoryStorage as t, OTLP_ENDPOINT as u };
|
|
2777
2851
|
|
|
2778
|
-
//# sourceMappingURL=
|
|
2852
|
+
//# sourceMappingURL=memoryStorage-CNQTs-YG.js.map
|