@kubb/core 5.0.0-beta.41 → 5.0.0-beta.42
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-Ba-FcsPo.d.ts → diagnostics-DZGgDzSv.d.ts} +150 -7
- package/dist/index.cjs +267 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +16 -2
- package/dist/index.js +268 -19
- package/dist/index.js.map +1 -1
- package/dist/{memoryStorage-DA1bnMte.js → memoryStorage-BOnaknb7.js} +149 -10
- package/dist/memoryStorage-BOnaknb7.js.map +1 -0
- package/dist/{memoryStorage-DZqlEW7H.cjs → memoryStorage-Dldu8sRT.cjs} +148 -9
- package/dist/memoryStorage-Dldu8sRT.cjs.map +1 -0
- package/dist/mocks.cjs +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.js +1 -1
- package/package.json +4 -4
- package/src/Fingerprint.ts +98 -0
- package/src/KubbDriver.ts +68 -8
- package/src/Manifest.ts +85 -0
- package/src/Telemetry.ts +13 -1
- package/src/caches/fsCache.ts +103 -0
- package/src/createCache.ts +74 -0
- package/src/createKubb.ts +33 -4
- package/src/defineParser.ts +1 -1
- package/src/index.ts +2 -0
- package/src/storages/fsStorage.ts +15 -23
- package/src/types.ts +2 -0
- package/dist/memoryStorage-DA1bnMte.js.map +0 -1
- package/dist/memoryStorage-DZqlEW7H.cjs.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import "./chunk-C0LytTxp.js";
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import { styleText } from "node:util";
|
|
4
|
-
import
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import path, { basename, extname, join, relative, resolve } from "node:path";
|
|
5
7
|
import { collectUsedSchemaNames, createFile, createStreamInput, extractStringsFromNodes, isOperationNode, isSchemaNode, transform } from "@kubb/ast";
|
|
6
8
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
9
|
//#region ../../internals/utils/src/errors.ts
|
|
@@ -635,7 +637,7 @@ var URLPath = class {
|
|
|
635
637
|
};
|
|
636
638
|
//#endregion
|
|
637
639
|
//#region package.json
|
|
638
|
-
var version = "5.0.0-beta.
|
|
640
|
+
var version = "5.0.0-beta.42";
|
|
639
641
|
//#endregion
|
|
640
642
|
//#region src/constants.ts
|
|
641
643
|
/**
|
|
@@ -1204,6 +1206,89 @@ function createStorage(build) {
|
|
|
1204
1206
|
return (options) => build(options ?? {});
|
|
1205
1207
|
}
|
|
1206
1208
|
//#endregion
|
|
1209
|
+
//#region src/Fingerprint.ts
|
|
1210
|
+
/**
|
|
1211
|
+
* Computes the cache key for an incremental build. All methods are static, so call them as
|
|
1212
|
+
* `Fingerprint.compute(...)` and `Fingerprint.stringify(...)`. The key holds no absolute
|
|
1213
|
+
* paths or modification times, so it never depends on where the project lives on disk.
|
|
1214
|
+
*/
|
|
1215
|
+
var Fingerprint = class Fingerprint {
|
|
1216
|
+
/**
|
|
1217
|
+
* Bumped when the snapshot format or fingerprint inputs change in an incompatible way, so stale
|
|
1218
|
+
* cache entries from older Kubb builds are never reused.
|
|
1219
|
+
*/
|
|
1220
|
+
static version = 1;
|
|
1221
|
+
/**
|
|
1222
|
+
* Deterministically serializes a value to JSON: object keys are sorted recursively and
|
|
1223
|
+
* `undefined` values and functions are dropped. Two structurally equal configs produce the same
|
|
1224
|
+
* string regardless of key order, which keeps the fingerprint stable across machines.
|
|
1225
|
+
*/
|
|
1226
|
+
static stringify(value) {
|
|
1227
|
+
return JSON.stringify(Fingerprint.#normalize(value));
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Computes a cache key from everything that affects the generated output: the spec content, the
|
|
1231
|
+
* output-shaping config, each plugin's name and options, the middleware names, the running
|
|
1232
|
+
* `@kubb/core` version, and the cache format version. Returns `null` when the input can't be
|
|
1233
|
+
* fingerprinted (remote URL or no adapter source), which disables caching for that build.
|
|
1234
|
+
*/
|
|
1235
|
+
static async compute({ config, adapterSource, version }) {
|
|
1236
|
+
if (!adapterSource) return null;
|
|
1237
|
+
const spec = await Fingerprint.#readSpec(adapterSource, config.root);
|
|
1238
|
+
if (spec === null) return null;
|
|
1239
|
+
const input = {
|
|
1240
|
+
cacheVersion: Fingerprint.version,
|
|
1241
|
+
version,
|
|
1242
|
+
spec,
|
|
1243
|
+
name: config.name,
|
|
1244
|
+
output: config.output,
|
|
1245
|
+
adapter: config.adapter?.name,
|
|
1246
|
+
parsers: config.parsers.map((parser) => parser.name),
|
|
1247
|
+
plugins: config.plugins.map((plugin) => ({
|
|
1248
|
+
name: plugin.name,
|
|
1249
|
+
options: plugin.options
|
|
1250
|
+
})),
|
|
1251
|
+
middleware: (config.middleware ?? []).map((middleware) => middleware.name)
|
|
1252
|
+
};
|
|
1253
|
+
return createHash("sha256").update(Fingerprint.stringify(input)).digest("hex");
|
|
1254
|
+
}
|
|
1255
|
+
static #normalize(value) {
|
|
1256
|
+
if (value === null || typeof value !== "object") return typeof value === "function" ? void 0 : value;
|
|
1257
|
+
if (Array.isArray(value)) return value.map((item) => Fingerprint.#normalize(item));
|
|
1258
|
+
const source = value;
|
|
1259
|
+
const result = {};
|
|
1260
|
+
for (const key of Object.keys(source).sort()) {
|
|
1261
|
+
const normalized = Fingerprint.#normalize(source[key]);
|
|
1262
|
+
if (normalized !== void 0) result[key] = normalized;
|
|
1263
|
+
}
|
|
1264
|
+
return result;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Reads the spec content that feeds the fingerprint. Returns `null` for a remote URL source
|
|
1268
|
+
* (hashing remote content would mean fetching it on every run) or when a file can't be read, so a
|
|
1269
|
+
* missing or virtual spec disables caching instead of failing the build.
|
|
1270
|
+
*/
|
|
1271
|
+
static async #readSpec(source, root) {
|
|
1272
|
+
if (source.type === "data") return {
|
|
1273
|
+
kind: "data",
|
|
1274
|
+
data: typeof source.data === "string" ? source.data : Fingerprint.stringify(source.data)
|
|
1275
|
+
};
|
|
1276
|
+
const paths = source.type === "paths" ? source.paths : [source.path];
|
|
1277
|
+
if (paths.some((path) => new URLPath(path).isURL)) return null;
|
|
1278
|
+
try {
|
|
1279
|
+
return {
|
|
1280
|
+
kind: "path",
|
|
1281
|
+
contents: await Promise.all(paths.map(async (path) => ({
|
|
1282
|
+
path: relative(root, path),
|
|
1283
|
+
content: await readFile(path, "utf8")
|
|
1284
|
+
})))
|
|
1285
|
+
};
|
|
1286
|
+
} catch {
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
//#endregion
|
|
1207
1292
|
//#region src/definePlugin.ts
|
|
1208
1293
|
/**
|
|
1209
1294
|
* Wraps a plugin factory and returns a function that accepts user options and
|
|
@@ -2098,9 +2183,10 @@ var KubbDriver = class KubbDriver {
|
|
|
2098
2183
|
}
|
|
2099
2184
|
async setup() {
|
|
2100
2185
|
const normalized = this.config.plugins.map((rawPlugin) => this.#normalizePlugin(rawPlugin));
|
|
2186
|
+
const dependenciesByName = new Map(normalized.map((plugin) => [plugin.name, new Set(plugin.dependencies ?? [])]));
|
|
2101
2187
|
normalized.sort((a, b) => {
|
|
2102
|
-
if (b.
|
|
2103
|
-
if (a.
|
|
2188
|
+
if (dependenciesByName.get(b.name)?.has(a.name)) return -1;
|
|
2189
|
+
if (dependenciesByName.get(a.name)?.has(b.name)) return 1;
|
|
2104
2190
|
return enforceOrder(a.enforce) - enforceOrder(b.enforce);
|
|
2105
2191
|
});
|
|
2106
2192
|
for (const plugin of normalized) {
|
|
@@ -2325,8 +2411,10 @@ var KubbDriver = class KubbDriver {
|
|
|
2325
2411
|
await hooks.emit("kubb:files:processing:start", { files });
|
|
2326
2412
|
});
|
|
2327
2413
|
const updateBuffer = [];
|
|
2414
|
+
const snapshotSources = /* @__PURE__ */ new Map();
|
|
2328
2415
|
processor.hooks.on("update", (item) => {
|
|
2329
2416
|
updateBuffer.push(item);
|
|
2417
|
+
if (item.source !== void 0) snapshotSources.set(item.file.path, item.source);
|
|
2330
2418
|
});
|
|
2331
2419
|
processor.hooks.on("end", async (files) => {
|
|
2332
2420
|
await hooks.emit("kubb:files:processing:update", { files: updateBuffer.map((item) => ({
|
|
@@ -2342,6 +2430,19 @@ var KubbDriver = class KubbDriver {
|
|
|
2342
2430
|
this.fileManager.hooks.on("upsert", onFileUpsert);
|
|
2343
2431
|
return Diagnostics.scope((diagnostic) => diagnostics.push(diagnostic), async () => {
|
|
2344
2432
|
try {
|
|
2433
|
+
const cache = config.cache;
|
|
2434
|
+
const outputRoot = resolve(config.root, config.output.path);
|
|
2435
|
+
const cacheKey = cache ? await Fingerprint.compute({
|
|
2436
|
+
config,
|
|
2437
|
+
adapterSource: this.#adapterSource,
|
|
2438
|
+
version
|
|
2439
|
+
}) : null;
|
|
2440
|
+
if (cache && cacheKey && await this.#restoreSnapshot({
|
|
2441
|
+
cache,
|
|
2442
|
+
cacheKey,
|
|
2443
|
+
outputRoot,
|
|
2444
|
+
storage
|
|
2445
|
+
})) return { diagnostics: Diagnostics.dedupe(diagnostics) };
|
|
2345
2446
|
await this.#parseInput();
|
|
2346
2447
|
await this.emitSetupHooks();
|
|
2347
2448
|
if (this.adapter && this.inputNode) await hooks.emit("kubb:build:start", Object.assign({
|
|
@@ -2400,7 +2501,13 @@ var KubbDriver = class KubbDriver {
|
|
|
2400
2501
|
await hooks.emit("kubb:build:end", {
|
|
2401
2502
|
files: this.fileManager.files,
|
|
2402
2503
|
config,
|
|
2403
|
-
outputDir:
|
|
2504
|
+
outputDir: outputRoot
|
|
2505
|
+
});
|
|
2506
|
+
if (cache && cacheKey && !Diagnostics.hasError(diagnostics)) await this.#persistSnapshot({
|
|
2507
|
+
cache,
|
|
2508
|
+
cacheKey,
|
|
2509
|
+
outputRoot,
|
|
2510
|
+
sources: snapshotSources
|
|
2404
2511
|
});
|
|
2405
2512
|
return { diagnostics: Diagnostics.dedupe(diagnostics) };
|
|
2406
2513
|
} catch (caughtError) {
|
|
@@ -2411,6 +2518,40 @@ var KubbDriver = class KubbDriver {
|
|
|
2411
2518
|
}
|
|
2412
2519
|
});
|
|
2413
2520
|
}
|
|
2521
|
+
/**
|
|
2522
|
+
* Writes a restored snapshot straight to storage and emits `kubb:build:end`. Returns `true` on a
|
|
2523
|
+
* hit (the build is done), `false` on a miss so the caller falls through to a full build.
|
|
2524
|
+
*/
|
|
2525
|
+
async #restoreSnapshot({ cache, cacheKey, outputRoot, storage }) {
|
|
2526
|
+
const snapshot = await cache.restore({ key: cacheKey });
|
|
2527
|
+
if (!snapshot) return false;
|
|
2528
|
+
for (const [relativePath, source] of Object.entries(snapshot.files)) {
|
|
2529
|
+
const absolutePath = join(outputRoot, relativePath);
|
|
2530
|
+
this.fileManager.upsert(createFile({
|
|
2531
|
+
path: absolutePath,
|
|
2532
|
+
baseName: basename(relativePath)
|
|
2533
|
+
}));
|
|
2534
|
+
await storage.setItem(absolutePath, source);
|
|
2535
|
+
}
|
|
2536
|
+
await this.hooks.emit("kubb:build:end", {
|
|
2537
|
+
files: this.fileManager.files,
|
|
2538
|
+
config: this.config,
|
|
2539
|
+
outputDir: outputRoot
|
|
2540
|
+
});
|
|
2541
|
+
return true;
|
|
2542
|
+
}
|
|
2543
|
+
/**
|
|
2544
|
+
* Stores this run's rendered output, keyed by the input fingerprint, so the next unchanged build
|
|
2545
|
+
* restores it instead of regenerating. `sources` is keyed by absolute path and relativized here.
|
|
2546
|
+
*/
|
|
2547
|
+
async #persistSnapshot({ cache, cacheKey, outputRoot, sources }) {
|
|
2548
|
+
const files = {};
|
|
2549
|
+
for (const [absolutePath, source] of sources) files[relative(outputRoot, absolutePath)] = source;
|
|
2550
|
+
await cache.persist({
|
|
2551
|
+
key: cacheKey,
|
|
2552
|
+
snapshot: { files }
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2414
2555
|
#filesPayload() {
|
|
2415
2556
|
const driver = this;
|
|
2416
2557
|
return {
|
|
@@ -2487,10 +2628,8 @@ var KubbDriver = class KubbDriver {
|
|
|
2487
2628
|
const emitsSchemaHook = this.hooks.listenerCount("kubb:generate:schema") > 0;
|
|
2488
2629
|
const emitsOperationHook = this.hooks.listenerCount("kubb:generate:operation") > 0;
|
|
2489
2630
|
const emitsOperationsHook = this.hooks.listenerCount("kubb:generate:operations") > 0;
|
|
2490
|
-
const schemasBuffer =
|
|
2491
|
-
|
|
2492
|
-
const operationsBuffer = [];
|
|
2493
|
-
for await (const operation of operations) operationsBuffer.push(operation);
|
|
2631
|
+
const schemasBuffer = await Array.fromAsync(schemas);
|
|
2632
|
+
const operationsBuffer = await Array.fromAsync(operations);
|
|
2494
2633
|
const pruningStates = states.filter(({ plugin }) => {
|
|
2495
2634
|
const { include } = plugin.options;
|
|
2496
2635
|
return (include?.some(({ type }) => OPERATION_FILTER_TYPES.has(type)) ?? false) && !(include?.some(({ type }) => type === "schemaName") ?? false);
|
|
@@ -2857,4 +2996,4 @@ const memoryStorage = createStorage(() => {
|
|
|
2857
2996
|
//#endregion
|
|
2858
2997
|
export { FileManager as a, createStorage as c, URLPath as d, formatMs as f, BuildError as g, AsyncEventEmitter as h, FileProcessor as i, Diagnostics as l, camelCase as m, KubbDriver as n, defineResolver as o, getElapsedMs as p, _usingCtx as r, definePlugin as s, memoryStorage as t, OTLP_ENDPOINT as u };
|
|
2859
2998
|
|
|
2860
|
-
//# sourceMappingURL=memoryStorage-
|
|
2999
|
+
//# sourceMappingURL=memoryStorage-BOnaknb7.js.map
|