@route-forge/core 1.3.1 → 2.0.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/README.md +50 -6
- package/dist/codegen.d.cts +1 -1
- package/dist/codegen.d.ts +1 -1
- package/dist/index.cjs +152 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.js +152 -123
- package/dist/index.js.map +1 -1
- package/dist/route-forge.global.js +152 -123
- package/dist/route-forge.global.js.map +1 -1
- package/dist/route-forge.global.min.js +2 -2
- package/dist/{types-Cd1c1OdJ.d.cts → types-BvLdl02f.d.cts} +13 -17
- package/dist/{types-Cd1c1OdJ.d.ts → types-BvLdl02f.d.ts} +13 -17
- package/package.json +1 -1
|
@@ -17,9 +17,18 @@ var RouteForge = (function (exports) {
|
|
|
17
17
|
var RouteCache = class {
|
|
18
18
|
constructor(opts) {
|
|
19
19
|
this.memory = /* @__PURE__ */ new Map();
|
|
20
|
+
/** 其他 tab 修改 storage 时失效对应镜像(storage 事件:自己的写不触发,自己的写经 set/del 已同步) */
|
|
21
|
+
this.onStorageEvent = (e) => {
|
|
22
|
+
if (!e.key) return;
|
|
23
|
+
if (!e.key.startsWith(KEY_PREFIX)) return;
|
|
24
|
+
this.memory.delete(e.key.slice(KEY_PREFIX.length));
|
|
25
|
+
};
|
|
20
26
|
this.storage = opts.storage;
|
|
21
27
|
this.fallbackTtl = opts.ttl;
|
|
22
28
|
this.backend = pickStorage(opts.storage);
|
|
29
|
+
if (this.backend && typeof globalThis.addEventListener === "function") {
|
|
30
|
+
globalThis.addEventListener("storage", this.onStorageEvent);
|
|
31
|
+
}
|
|
23
32
|
}
|
|
24
33
|
key(level) {
|
|
25
34
|
return `${KEY_PREFIX}${level}`;
|
|
@@ -28,6 +37,8 @@ var RouteForge = (function (exports) {
|
|
|
28
37
|
if (this.storage === "memory" || !this.backend) {
|
|
29
38
|
return this.getFromMemory(level);
|
|
30
39
|
}
|
|
40
|
+
const mirrored = this.getFromMemory(level);
|
|
41
|
+
if (mirrored) return mirrored;
|
|
31
42
|
const raw = this.backend.getItem(this.key(level));
|
|
32
43
|
if (raw) {
|
|
33
44
|
try {
|
|
@@ -36,12 +47,13 @@ var RouteForge = (function (exports) {
|
|
|
36
47
|
this.del(level);
|
|
37
48
|
return void 0;
|
|
38
49
|
}
|
|
50
|
+
this.memory.set(level, entry);
|
|
39
51
|
return entry;
|
|
40
52
|
} catch {
|
|
41
53
|
return void 0;
|
|
42
54
|
}
|
|
43
55
|
}
|
|
44
|
-
return
|
|
56
|
+
return void 0;
|
|
45
57
|
}
|
|
46
58
|
getFromMemory(level) {
|
|
47
59
|
const entry = this.memory.get(level);
|
|
@@ -65,14 +77,13 @@ var RouteForge = (function (exports) {
|
|
|
65
77
|
ttl,
|
|
66
78
|
cachedAt: Date.now()
|
|
67
79
|
};
|
|
80
|
+
this.memory.set(resp.level, entry);
|
|
68
81
|
if (this.storage === "memory" || !this.backend) {
|
|
69
|
-
this.memory.set(resp.level, entry);
|
|
70
82
|
return;
|
|
71
83
|
}
|
|
72
84
|
try {
|
|
73
85
|
this.backend.setItem(this.key(resp.level), JSON.stringify(entry));
|
|
74
86
|
} catch {
|
|
75
|
-
this.memory.set(resp.level, entry);
|
|
76
87
|
}
|
|
77
88
|
}
|
|
78
89
|
del(level) {
|
|
@@ -252,14 +263,14 @@ var RouteForge = (function (exports) {
|
|
|
252
263
|
return new InterceptorManagerImpl();
|
|
253
264
|
}
|
|
254
265
|
|
|
255
|
-
// src/adapters/
|
|
256
|
-
function
|
|
257
|
-
if (
|
|
258
|
-
return typeof FormData !== "undefined" && body instanceof FormData || typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams || typeof Blob !== "undefined" && body instanceof Blob || typeof ArrayBuffer !== "undefined" && (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) || typeof ReadableStream !== "undefined" && body instanceof ReadableStream;
|
|
259
|
-
}
|
|
260
|
-
function isAbortError(err) {
|
|
266
|
+
// src/adapters/fetch-core.ts
|
|
267
|
+
function isAbortError(err, signal) {
|
|
268
|
+
if (signal?.aborted) return true;
|
|
261
269
|
const e = err;
|
|
262
|
-
|
|
270
|
+
if (!e) return false;
|
|
271
|
+
if (e.name === "AbortError") return true;
|
|
272
|
+
if (e.code === "ERR_CANCELED") return true;
|
|
273
|
+
return false;
|
|
263
274
|
}
|
|
264
275
|
function combineSignals(...signals) {
|
|
265
276
|
const valid = signals.filter((s) => !!s);
|
|
@@ -278,81 +289,92 @@ var RouteForge = (function (exports) {
|
|
|
278
289
|
}
|
|
279
290
|
return controller.signal;
|
|
280
291
|
}
|
|
281
|
-
function
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
292
|
+
function isPassthroughBody(body) {
|
|
293
|
+
if (body === null) return false;
|
|
294
|
+
return typeof FormData !== "undefined" && body instanceof FormData || typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams || typeof Blob !== "undefined" && body instanceof Blob || typeof ArrayBuffer !== "undefined" && (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) || typeof ReadableStream !== "undefined" && body instanceof ReadableStream;
|
|
295
|
+
}
|
|
296
|
+
async function rawFetch(config) {
|
|
297
|
+
const signal = combineSignals(
|
|
298
|
+
config.signal,
|
|
299
|
+
config.timeout && config.timeout > 0 ? AbortSignal.timeout(config.timeout) : void 0
|
|
300
|
+
);
|
|
301
|
+
let url = config.url;
|
|
302
|
+
if (config.paramsSerializer && config.params) {
|
|
303
|
+
const qs = config.paramsSerializer(config.params);
|
|
304
|
+
if (qs) {
|
|
305
|
+
url = url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
|
|
296
306
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
}
|
|
308
|
+
const headers = new Headers(config.headers);
|
|
309
|
+
const fetchInit = {
|
|
310
|
+
method: config.method,
|
|
311
|
+
headers
|
|
312
|
+
};
|
|
313
|
+
if (signal) fetchInit.signal = signal;
|
|
314
|
+
if (config.body !== void 0 && !["GET", "HEAD"].includes(config.method.toUpperCase())) {
|
|
315
|
+
if (typeof config.body === "string" || isPassthroughBody(config.body)) {
|
|
316
|
+
fetchInit.body = config.body;
|
|
317
|
+
} else {
|
|
318
|
+
fetchInit.body = JSON.stringify(config.body);
|
|
319
|
+
if (!headers.has("Content-Type")) {
|
|
320
|
+
headers.set("Content-Type", "application/json");
|
|
311
321
|
}
|
|
312
322
|
}
|
|
313
|
-
|
|
323
|
+
}
|
|
324
|
+
let res;
|
|
325
|
+
try {
|
|
326
|
+
res = await fetch(url, fetchInit);
|
|
327
|
+
} catch (e) {
|
|
328
|
+
if (isAbortError(e)) throw e;
|
|
329
|
+
throw new NetworkError(
|
|
330
|
+
e instanceof Error ? e.message : String(e),
|
|
331
|
+
config.route,
|
|
332
|
+
config.level,
|
|
333
|
+
e
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
const text = await res.text();
|
|
337
|
+
let data = text;
|
|
338
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
339
|
+
if (contentType.includes("application/json")) {
|
|
314
340
|
try {
|
|
315
|
-
|
|
316
|
-
} catch
|
|
317
|
-
if (isAbortError(e)) throw e;
|
|
318
|
-
throw new NetworkError(
|
|
319
|
-
e instanceof Error ? e.message : String(e),
|
|
320
|
-
finalConfig.route,
|
|
321
|
-
finalConfig.level,
|
|
322
|
-
e
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
const text = await res.text();
|
|
326
|
-
let data = text;
|
|
327
|
-
const contentType = res.headers.get("content-type") ?? "";
|
|
328
|
-
if (contentType.includes("application/json")) {
|
|
329
|
-
try {
|
|
330
|
-
data = JSON.parse(text);
|
|
331
|
-
} catch {
|
|
332
|
-
}
|
|
341
|
+
data = JSON.parse(text);
|
|
342
|
+
} catch {
|
|
333
343
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
344
|
+
}
|
|
345
|
+
const responseData = {
|
|
346
|
+
route: config.route,
|
|
347
|
+
level: config.level,
|
|
348
|
+
method: config.method,
|
|
349
|
+
url,
|
|
350
|
+
status: res.status,
|
|
351
|
+
headers: res.headers,
|
|
352
|
+
data,
|
|
353
|
+
config
|
|
354
|
+
};
|
|
355
|
+
if (res.status >= 200 && res.status < 300) {
|
|
356
|
+
return responseData;
|
|
357
|
+
}
|
|
358
|
+
throw new HTTPError(
|
|
359
|
+
`HTTP ${res.status} for route "${config.route}" (${config.method} ${url})`,
|
|
360
|
+
{
|
|
361
|
+
route: config.route,
|
|
362
|
+
level: config.level,
|
|
339
363
|
status: res.status,
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
)
|
|
355
|
-
);
|
|
364
|
+
url,
|
|
365
|
+
method: config.method
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/adapters/builtin-http.ts
|
|
371
|
+
function createBuiltinHttp(forgeInterceptors) {
|
|
372
|
+
const requestMgr = forgeInterceptors?.request ?? createInterceptorManager();
|
|
373
|
+
const responseMgr = forgeInterceptors?.response ?? createInterceptorManager();
|
|
374
|
+
const requestRaw = rawFetch;
|
|
375
|
+
async function request(config) {
|
|
376
|
+
const finalConfig = await runRequestInterceptors(requestMgr, config);
|
|
377
|
+
const source = Promise.resolve(finalConfig).then(requestRaw);
|
|
356
378
|
return runResponseInterceptors(
|
|
357
379
|
responseMgr,
|
|
358
380
|
source
|
|
@@ -365,6 +387,7 @@ var RouteForge = (function (exports) {
|
|
|
365
387
|
const del = (url, config) => request({ ...config, url, method: "DELETE" });
|
|
366
388
|
return {
|
|
367
389
|
request,
|
|
390
|
+
requestRaw,
|
|
368
391
|
interceptors: {
|
|
369
392
|
request: requestMgr,
|
|
370
393
|
response: responseMgr
|
|
@@ -576,7 +599,6 @@ var RouteForge = (function (exports) {
|
|
|
576
599
|
const loadingTracker = new LoadingTracker();
|
|
577
600
|
const explicitLevels = options.levels;
|
|
578
601
|
const explicitEager = options.eager;
|
|
579
|
-
const explicitStrict = options.strict ?? false;
|
|
580
602
|
const explicitEndpoint = options.endpoint;
|
|
581
603
|
let effectiveLevels = explicitLevels ?? [];
|
|
582
604
|
let effectiveEager = explicitEager ?? [];
|
|
@@ -584,17 +606,12 @@ var RouteForge = (function (exports) {
|
|
|
584
606
|
let effectiveUrlPrefix = "";
|
|
585
607
|
let summaryUnassigned;
|
|
586
608
|
let backendHasUnassignedLevel = false;
|
|
587
|
-
const
|
|
609
|
+
const fetchSummary = async () => {
|
|
588
610
|
try {
|
|
589
|
-
const
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
`[route-forge] summary endpoint ${summaryUrl} unreachable (HTTP ${resp.status}); falling back to explicit options`
|
|
594
|
-
);
|
|
595
|
-
return null;
|
|
596
|
-
}
|
|
597
|
-
return await resp.json();
|
|
611
|
+
const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
|
|
612
|
+
const ep = explicitEndpoint.startsWith("/") ? explicitEndpoint : `/${explicitEndpoint}`;
|
|
613
|
+
const data = await fetchMeta("__forge__.summary", `${base}${ep}`);
|
|
614
|
+
return data;
|
|
598
615
|
} catch (e) {
|
|
599
616
|
if (explicitLevels && explicitLevels.length > 0) {
|
|
600
617
|
console.warn(
|
|
@@ -604,8 +621,11 @@ var RouteForge = (function (exports) {
|
|
|
604
621
|
}
|
|
605
622
|
throw new UnknownLevelError("(auto-discovery)");
|
|
606
623
|
}
|
|
607
|
-
}
|
|
608
|
-
|
|
624
|
+
};
|
|
625
|
+
let summaryPromise;
|
|
626
|
+
const summaryWaiters = [];
|
|
627
|
+
const whenSummary = () => summaryPromise ?? new Promise((resolve) => summaryWaiters.push(resolve));
|
|
628
|
+
const autoDiscoveryPromise = whenSummary().then((summary) => {
|
|
609
629
|
if (summary === null) {
|
|
610
630
|
return;
|
|
611
631
|
}
|
|
@@ -624,11 +644,6 @@ var RouteForge = (function (exports) {
|
|
|
624
644
|
if (summary.config.url_prefix) {
|
|
625
645
|
effectiveUrlPrefix = summary.config.url_prefix.endsWith("/") ? summary.config.url_prefix.slice(0, -1) : summary.config.url_prefix;
|
|
626
646
|
}
|
|
627
|
-
if (summary.config.strict_mode && !explicitStrict) {
|
|
628
|
-
console.warn(
|
|
629
|
-
"[route-forge] backend strict_mode=true overrides frontend strict=false; forcing strict=true"
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
647
|
const backendLevels = Object.keys(summary.levels);
|
|
633
648
|
backendHasUnassignedLevel = backendLevels.includes(UNASSIGNED_LEVEL);
|
|
634
649
|
if (Array.isArray(summary.unassigned) && summary.unassigned.length > 0 && !backendHasUnassignedLevel) {
|
|
@@ -664,8 +679,12 @@ var RouteForge = (function (exports) {
|
|
|
664
679
|
});
|
|
665
680
|
let autoDiscoveryCompleted = false;
|
|
666
681
|
let resolveReady;
|
|
667
|
-
|
|
682
|
+
let rejectReady;
|
|
683
|
+
const readyPromise = new Promise((resolve, reject) => {
|
|
668
684
|
resolveReady = resolve;
|
|
685
|
+
rejectReady = reject;
|
|
686
|
+
});
|
|
687
|
+
readyPromise.catch(() => {
|
|
669
688
|
});
|
|
670
689
|
const cacheTtl = cacheOpts.ttl ?? DEFAULT_CACHE_TTL;
|
|
671
690
|
const cacheStorage = cacheOpts.storage ?? "memory";
|
|
@@ -696,13 +715,20 @@ var RouteForge = (function (exports) {
|
|
|
696
715
|
adapter,
|
|
697
716
|
forgeInterceptors: { request: requestInterceptors, response: responseInterceptors }
|
|
698
717
|
});
|
|
718
|
+
Promise.resolve().then(() => {
|
|
719
|
+
summaryPromise = fetchSummary();
|
|
720
|
+
for (const resolve of summaryWaiters) resolve(summaryPromise);
|
|
721
|
+
});
|
|
699
722
|
let adapterResolved = false;
|
|
700
723
|
let adapterObj = null;
|
|
701
724
|
async function ensureAdapter() {
|
|
702
725
|
if (!adapterResolved) {
|
|
703
726
|
adapterObj = await adapterPromise.catch((e) => {
|
|
704
727
|
if (e instanceof AdapterNotFoundError) throw e;
|
|
705
|
-
return resolveAdapter({
|
|
728
|
+
return resolveAdapter({
|
|
729
|
+
adapter: "builtin",
|
|
730
|
+
forgeInterceptors: { request: requestInterceptors, response: responseInterceptors }
|
|
731
|
+
});
|
|
706
732
|
});
|
|
707
733
|
adapterResolved = true;
|
|
708
734
|
}
|
|
@@ -728,33 +754,36 @@ var RouteForge = (function (exports) {
|
|
|
728
754
|
const ep = effectiveEndpoint.startsWith("/") ? effectiveEndpoint : `/${effectiveEndpoint}`;
|
|
729
755
|
return `${base}${ep}/${encodeURIComponent(level)}`;
|
|
730
756
|
}
|
|
731
|
-
async function
|
|
757
|
+
async function fetchMeta(routeTag, url, level = "") {
|
|
732
758
|
const adp = await ensureAdapter();
|
|
733
759
|
const config = {
|
|
734
|
-
route:
|
|
760
|
+
route: routeTag,
|
|
735
761
|
level,
|
|
736
762
|
method: "GET",
|
|
737
|
-
url
|
|
763
|
+
url,
|
|
738
764
|
headers: { Accept: "application/json" },
|
|
739
765
|
params: {},
|
|
740
766
|
timeout,
|
|
741
767
|
meta: {
|
|
742
|
-
name:
|
|
743
|
-
uri:
|
|
768
|
+
name: routeTag,
|
|
769
|
+
uri: url,
|
|
744
770
|
methods: ["GET"],
|
|
745
771
|
parameters: [],
|
|
746
772
|
level
|
|
747
773
|
}
|
|
748
774
|
};
|
|
749
|
-
const
|
|
775
|
+
const doRawRequest = adp.requestRaw ?? adp.request;
|
|
776
|
+
const resp = await doRawRequest(config);
|
|
750
777
|
if (!resp || resp.status < 200 || resp.status >= 300) {
|
|
751
778
|
throw new HTTPError(
|
|
752
|
-
`Failed to
|
|
753
|
-
{ level, status: resp?.status, url
|
|
779
|
+
`Failed to fetch "${routeTag}": HTTP ${resp?.status}`,
|
|
780
|
+
{ level, status: resp?.status, url, method: "GET" }
|
|
754
781
|
);
|
|
755
782
|
}
|
|
756
|
-
|
|
757
|
-
|
|
783
|
+
return resp.data;
|
|
784
|
+
}
|
|
785
|
+
async function fetchLevel(level) {
|
|
786
|
+
return await fetchMeta(`__forge__.load.${level}`, buildUrl(level), level);
|
|
758
787
|
}
|
|
759
788
|
async function loadOne(level) {
|
|
760
789
|
if (autoDiscoveryError) throw autoDiscoveryError;
|
|
@@ -926,7 +955,7 @@ var RouteForge = (function (exports) {
|
|
|
926
955
|
},
|
|
927
956
|
(err) => {
|
|
928
957
|
if (err instanceof ForgeError) throw err;
|
|
929
|
-
if (
|
|
958
|
+
if (isAbortError(err, signal)) {
|
|
930
959
|
throw new RequestAbortedError(meta.name, meta.level, err);
|
|
931
960
|
}
|
|
932
961
|
throw new NetworkError(
|
|
@@ -995,15 +1024,22 @@ var RouteForge = (function (exports) {
|
|
|
995
1024
|
}
|
|
996
1025
|
void autoDiscoveryPromise.then(() => {
|
|
997
1026
|
autoDiscoveryCompleted = true;
|
|
998
|
-
options.onSummaryReady?.();
|
|
999
1027
|
if (effectiveEager.length > 0) {
|
|
1000
|
-
return Promise.
|
|
1001
|
-
|
|
1028
|
+
return Promise.allSettled(effectiveEager.map((lvl) => load(lvl))).then((results) => {
|
|
1029
|
+
results.forEach((r, i) => {
|
|
1030
|
+
if (r.status === "rejected") {
|
|
1031
|
+
console.error(
|
|
1032
|
+
`[route-forge] eager load failed for level "${effectiveEager[i]}":`,
|
|
1033
|
+
r.reason
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1002
1037
|
});
|
|
1003
1038
|
}
|
|
1004
1039
|
}).then(() => {
|
|
1005
1040
|
resolveReady(forgeInstance);
|
|
1006
|
-
}).catch(() => {
|
|
1041
|
+
}).catch((e) => {
|
|
1042
|
+
rejectReady(e);
|
|
1007
1043
|
});
|
|
1008
1044
|
function ready(onFulfilled, onRejected) {
|
|
1009
1045
|
if (onFulfilled) {
|
|
@@ -1014,7 +1050,8 @@ var RouteForge = (function (exports) {
|
|
|
1014
1050
|
}
|
|
1015
1051
|
const forgeResolver = { load, hasRoute };
|
|
1016
1052
|
function createBoundForge(level, prefix) {
|
|
1017
|
-
const levelLoadedPromise = load(level)
|
|
1053
|
+
const levelLoadedPromise = load(level);
|
|
1054
|
+
levelLoadedPromise.catch(() => {
|
|
1018
1055
|
});
|
|
1019
1056
|
const apiFn = prefix ? (name, params) => resolveRouteName(forgeResolver, level, prefix, name).then(
|
|
1020
1057
|
(resolved) => api(level, resolved, params)
|
|
@@ -1148,14 +1185,6 @@ var RouteForge = (function (exports) {
|
|
|
1148
1185
|
}
|
|
1149
1186
|
return { pathParams, query, body, headers, timeout: perCallTimeout };
|
|
1150
1187
|
}
|
|
1151
|
-
function isAbortError2(err, signal) {
|
|
1152
|
-
if (signal?.aborted) return true;
|
|
1153
|
-
const e = err;
|
|
1154
|
-
if (!e) return false;
|
|
1155
|
-
if (e.name === "AbortError") return true;
|
|
1156
|
-
if (e.code === "ERR_CANCELED") return true;
|
|
1157
|
-
return false;
|
|
1158
|
-
}
|
|
1159
1188
|
|
|
1160
1189
|
exports.AdapterNotFoundError = AdapterNotFoundError;
|
|
1161
1190
|
exports.ForgeError = ForgeError;
|