@route-forge/core 2.0.0 → 2.1.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/dist/codegen.d.cts +1 -1
- package/dist/codegen.d.ts +1 -1
- package/dist/index.cjs +521 -436
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -47
- package/dist/index.d.ts +47 -47
- package/dist/index.js +521 -436
- package/dist/index.js.map +1 -1
- package/dist/route-forge.global.js +521 -436
- package/dist/route-forge.global.js.map +1 -1
- package/dist/route-forge.global.min.js +2 -2
- package/dist/{types-BvLdl02f.d.cts → types-C51rXaiY.d.cts} +1 -1
- package/dist/{types-BvLdl02f.d.ts → types-C51rXaiY.d.ts} +1 -1
- package/package.json +1 -1
|
@@ -491,39 +491,6 @@ var RouteForge = (function (exports) {
|
|
|
491
491
|
return createBuiltinHttp(opts.forgeInterceptors);
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
// src/resolveRouteName.ts
|
|
495
|
-
async function resolveRouteName(forge, level, prefix, suffix, separator = ".") {
|
|
496
|
-
if (!suffix) return prefix;
|
|
497
|
-
const joined = `${prefix}${separator}${suffix}`;
|
|
498
|
-
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
499
|
-
await forge.load(level);
|
|
500
|
-
if (forge.hasRoute(level, joined)) return joined;
|
|
501
|
-
if (forge.hasRoute(level, suffix)) return suffix;
|
|
502
|
-
throw new UnknownRouteError(joined, level);
|
|
503
|
-
}
|
|
504
|
-
function resolveRouteNameSync(forge, level, prefix, suffix, separator = ".") {
|
|
505
|
-
if (!suffix) return prefix;
|
|
506
|
-
const joined = `${prefix}${separator}${suffix}`;
|
|
507
|
-
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
508
|
-
if (forge.hasRoute(level, joined)) return joined;
|
|
509
|
-
if (forge.hasRoute(level, suffix)) return suffix;
|
|
510
|
-
throw new UnknownRouteError(joined, level);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// src/defineImmutableProps.ts
|
|
514
|
-
function defineImmutableProps(target, props) {
|
|
515
|
-
for (const key of Object.keys(props)) {
|
|
516
|
-
const val = props[key];
|
|
517
|
-
Object.defineProperty(target, key, {
|
|
518
|
-
value: val !== null && typeof val === "object" ? Object.freeze(val) : val,
|
|
519
|
-
writable: false,
|
|
520
|
-
enumerable: false,
|
|
521
|
-
configurable: false
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
return target;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
494
|
// src/loading.ts
|
|
528
495
|
var LoadingTracker = class {
|
|
529
496
|
constructor() {
|
|
@@ -583,328 +550,308 @@ var RouteForge = (function (exports) {
|
|
|
583
550
|
}
|
|
584
551
|
};
|
|
585
552
|
|
|
586
|
-
// src/
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const explicitLevels = options.levels;
|
|
601
|
-
const explicitEager = options.eager;
|
|
602
|
-
const explicitEndpoint = options.endpoint;
|
|
603
|
-
let effectiveLevels = explicitLevels ?? [];
|
|
604
|
-
let effectiveEager = explicitEager ?? [];
|
|
605
|
-
let effectiveEndpoint = explicitEndpoint;
|
|
606
|
-
let effectiveUrlPrefix = "";
|
|
607
|
-
let summaryUnassigned;
|
|
608
|
-
let backendHasUnassignedLevel = false;
|
|
609
|
-
const fetchSummary = async () => {
|
|
610
|
-
try {
|
|
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;
|
|
615
|
-
} catch (e) {
|
|
616
|
-
if (explicitLevels && explicitLevels.length > 0) {
|
|
617
|
-
console.warn(
|
|
618
|
-
`[route-forge] summary endpoint unreachable: ${e.message}; using explicit levels`
|
|
619
|
-
);
|
|
620
|
-
return null;
|
|
621
|
-
}
|
|
622
|
-
throw new UnknownLevelError("(auto-discovery)");
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
let summaryPromise;
|
|
626
|
-
const summaryWaiters = [];
|
|
627
|
-
const whenSummary = () => summaryPromise ?? new Promise((resolve) => summaryWaiters.push(resolve));
|
|
628
|
-
const autoDiscoveryPromise = whenSummary().then((summary) => {
|
|
629
|
-
if (summary === null) {
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
const schemaVersion = summary.schemaVersion ?? 1;
|
|
633
|
-
if (schemaVersion > 1) {
|
|
634
|
-
console.warn(
|
|
635
|
-
`[route-forge] backend schemaVersion=${schemaVersion} > client supported 1; some features may be unavailable`
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
|
-
if (summary.config.endpoint_prefix && summary.config.endpoint_prefix !== explicitEndpoint) {
|
|
639
|
-
console.warn(
|
|
640
|
-
`[route-forge] backend endpoint_prefix "${summary.config.endpoint_prefix}" overrides frontend endpoint "${explicitEndpoint}"`
|
|
641
|
-
);
|
|
642
|
-
effectiveEndpoint = summary.config.endpoint_prefix;
|
|
643
|
-
}
|
|
644
|
-
if (summary.config.url_prefix) {
|
|
645
|
-
effectiveUrlPrefix = summary.config.url_prefix.endsWith("/") ? summary.config.url_prefix.slice(0, -1) : summary.config.url_prefix;
|
|
646
|
-
}
|
|
647
|
-
const backendLevels = Object.keys(summary.levels);
|
|
648
|
-
backendHasUnassignedLevel = backendLevels.includes(UNASSIGNED_LEVEL);
|
|
649
|
-
if (Array.isArray(summary.unassigned) && summary.unassigned.length > 0 && !backendHasUnassignedLevel) {
|
|
650
|
-
summaryUnassigned = summary.unassigned;
|
|
651
|
-
}
|
|
652
|
-
const availableLevels = backendLevels.slice();
|
|
653
|
-
if (summaryUnassigned && !backendHasUnassignedLevel) {
|
|
654
|
-
availableLevels.push(UNASSIGNED_LEVEL);
|
|
553
|
+
// src/url-builder.ts
|
|
554
|
+
function buildUrl(level, ctx) {
|
|
555
|
+
const base = ctx.baseURL.endsWith("/") ? ctx.baseURL.slice(0, -1) : ctx.baseURL;
|
|
556
|
+
const ep = ctx.endpoint.startsWith("/") ? ctx.endpoint : `/${ctx.endpoint}`;
|
|
557
|
+
return `${base}${ep}/${encodeURIComponent(level)}`;
|
|
558
|
+
}
|
|
559
|
+
function buildRequestUrl(meta, params, ctx) {
|
|
560
|
+
const defaults = meta.parameter_defaults ?? {};
|
|
561
|
+
const missingRequired = [];
|
|
562
|
+
const values = {};
|
|
563
|
+
for (const p of meta.parameters) {
|
|
564
|
+
let v = params[p];
|
|
565
|
+
if ((v === void 0 || v === null) && p in defaults) {
|
|
566
|
+
v = defaults[p];
|
|
655
567
|
}
|
|
656
|
-
if (
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (removed.length > 0) {
|
|
660
|
-
console.warn(
|
|
661
|
-
`[route-forge] levels not in backend summary and dropped: ${removed.join(", ")}`
|
|
662
|
-
);
|
|
568
|
+
if (v === void 0 || v === null) {
|
|
569
|
+
if (!meta.uri.includes(`{${p}?}`)) {
|
|
570
|
+
missingRequired.push(p);
|
|
663
571
|
}
|
|
664
|
-
effectiveLevels = intersection;
|
|
665
572
|
} else {
|
|
666
|
-
|
|
573
|
+
values[p] = v;
|
|
667
574
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
575
|
+
}
|
|
576
|
+
if (missingRequired.length > 0) {
|
|
577
|
+
throw new MissingRouteParamError(meta.name, missingRequired);
|
|
578
|
+
}
|
|
579
|
+
let uri = meta.uri.replace(/\{([^{}]+)\}/g, (match, raw) => {
|
|
580
|
+
const optional = raw.endsWith("?");
|
|
581
|
+
const name = optional ? raw.slice(0, -1) : raw;
|
|
582
|
+
if (values[name] !== void 0) {
|
|
583
|
+
const val = values[name];
|
|
584
|
+
if (typeof val === "object") {
|
|
585
|
+
throw new ForgeError(
|
|
586
|
+
`Path parameter "${name}" must be a primitive value (string, number, boolean), got ${typeof val}`,
|
|
587
|
+
{ code: "RF_FE_003", route: meta.name, context: { param: name, value: val } }
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
return encodeURIComponent(String(val));
|
|
674
591
|
}
|
|
592
|
+
return optional ? "" : match;
|
|
675
593
|
});
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
const
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
594
|
+
uri = uri.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
595
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(ctx.urlPrefix)) {
|
|
596
|
+
const prefix2 = ctx.urlPrefix.endsWith("/") ? ctx.urlPrefix.slice(0, -1) : ctx.urlPrefix;
|
|
597
|
+
return uri.startsWith("/") ? `${prefix2}${uri}` : `${prefix2}/${uri}`;
|
|
598
|
+
}
|
|
599
|
+
const base = ctx.baseURL.endsWith("/") ? ctx.baseURL.slice(0, -1) : ctx.baseURL;
|
|
600
|
+
const prefix = ctx.urlPrefix;
|
|
601
|
+
return uri.startsWith("/") ? `${base}${prefix}${uri}` : `${base}${prefix}/${uri}`;
|
|
602
|
+
}
|
|
603
|
+
function pickMethod(meta) {
|
|
604
|
+
const m = meta.methods.find((x) => x.toUpperCase() !== "HEAD");
|
|
605
|
+
return (m ?? meta.methods[0] ?? "GET").toUpperCase();
|
|
606
|
+
}
|
|
607
|
+
function appendQuery(url, query) {
|
|
608
|
+
if (!query) return url;
|
|
609
|
+
const usp = new URLSearchParams();
|
|
610
|
+
for (const [k, v] of Object.entries(query)) {
|
|
611
|
+
if (v === void 0 || v === null) continue;
|
|
612
|
+
usp.append(k, String(v));
|
|
613
|
+
}
|
|
614
|
+
const qs = usp.toString();
|
|
615
|
+
if (!qs) return url;
|
|
616
|
+
return url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
|
|
617
|
+
}
|
|
618
|
+
function resolveApiParams(input) {
|
|
619
|
+
const {
|
|
620
|
+
params: explicitParams,
|
|
621
|
+
query: rawQuery,
|
|
622
|
+
body: rawBody,
|
|
623
|
+
headers: rawHeaders,
|
|
624
|
+
timeout: perCallTimeout,
|
|
625
|
+
...flatRest
|
|
626
|
+
} = input;
|
|
627
|
+
const pathParams = explicitParams ? { ...explicitParams } : {};
|
|
628
|
+
for (const [k, v] of Object.entries(flatRest)) {
|
|
629
|
+
if (!(k in pathParams)) {
|
|
630
|
+
pathParams[k] = v;
|
|
702
631
|
}
|
|
703
632
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
633
|
+
let query;
|
|
634
|
+
let body;
|
|
635
|
+
let headers;
|
|
636
|
+
if (rawQuery !== void 0) {
|
|
637
|
+
if (typeof rawQuery === "object" && rawQuery !== null) {
|
|
638
|
+
query = rawQuery;
|
|
639
|
+
} else if (!("query" in pathParams)) {
|
|
640
|
+
pathParams.query = rawQuery;
|
|
712
641
|
}
|
|
713
642
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
summaryPromise = fetchSummary();
|
|
720
|
-
for (const resolve of summaryWaiters) resolve(summaryPromise);
|
|
721
|
-
});
|
|
722
|
-
let adapterResolved = false;
|
|
723
|
-
let adapterObj = null;
|
|
724
|
-
async function ensureAdapter() {
|
|
725
|
-
if (!adapterResolved) {
|
|
726
|
-
adapterObj = await adapterPromise.catch((e) => {
|
|
727
|
-
if (e instanceof AdapterNotFoundError) throw e;
|
|
728
|
-
return resolveAdapter({
|
|
729
|
-
adapter: "builtin",
|
|
730
|
-
forgeInterceptors: { request: requestInterceptors, response: responseInterceptors }
|
|
731
|
-
});
|
|
732
|
-
});
|
|
733
|
-
adapterResolved = true;
|
|
643
|
+
if (rawBody !== void 0) {
|
|
644
|
+
if (typeof rawBody !== "string" && typeof rawBody !== "number") {
|
|
645
|
+
body = rawBody;
|
|
646
|
+
} else if (!("body" in pathParams)) {
|
|
647
|
+
pathParams.body = rawBody;
|
|
734
648
|
}
|
|
735
|
-
return adapterObj;
|
|
736
649
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
if (!
|
|
741
|
-
|
|
650
|
+
if (rawHeaders !== void 0) {
|
|
651
|
+
if (typeof rawHeaders === "object" && rawHeaders !== null) {
|
|
652
|
+
headers = rawHeaders;
|
|
653
|
+
} else if (!("headers" in pathParams)) {
|
|
654
|
+
pathParams.headers = rawHeaders;
|
|
742
655
|
}
|
|
743
656
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
657
|
+
return { pathParams, query, body, headers, timeout: perCallTimeout };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// src/auto-discovery.ts
|
|
661
|
+
var UNASSIGNED_LEVEL = "unassigned";
|
|
662
|
+
async function fetchSummary(inputs, baseURL, fetchMeta) {
|
|
663
|
+
const { explicitLevels, explicitEndpoint } = inputs;
|
|
664
|
+
try {
|
|
665
|
+
const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
|
|
666
|
+
const ep = explicitEndpoint.startsWith("/") ? explicitEndpoint : `/${explicitEndpoint}`;
|
|
667
|
+
const data = await fetchMeta("__forge__.summary", `${base}${ep}`);
|
|
668
|
+
return data;
|
|
669
|
+
} catch (e) {
|
|
670
|
+
if (explicitLevels && explicitLevels.length > 0) {
|
|
671
|
+
console.warn(
|
|
672
|
+
`[route-forge] summary endpoint unreachable: ${e.message}; using explicit levels`
|
|
749
673
|
);
|
|
674
|
+
return null;
|
|
750
675
|
}
|
|
676
|
+
throw new UnknownLevelError("(auto-discovery)");
|
|
751
677
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
678
|
+
}
|
|
679
|
+
function applySummaryToState(summary, state, inputs) {
|
|
680
|
+
const { explicitLevels, explicitEager, explicitEndpoint } = inputs;
|
|
681
|
+
const schemaVersion = summary.schemaVersion ?? 1;
|
|
682
|
+
if (schemaVersion > 1) {
|
|
683
|
+
console.warn(
|
|
684
|
+
`[route-forge] backend schemaVersion=${schemaVersion} > client supported 1; some features may be unavailable`
|
|
685
|
+
);
|
|
756
686
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
687
|
+
if (summary.config.endpoint_prefix && summary.config.endpoint_prefix !== explicitEndpoint) {
|
|
688
|
+
console.warn(
|
|
689
|
+
`[route-forge] backend endpoint_prefix "${summary.config.endpoint_prefix}" overrides frontend endpoint "${explicitEndpoint}"`
|
|
690
|
+
);
|
|
691
|
+
state.endpoint = summary.config.endpoint_prefix;
|
|
692
|
+
}
|
|
693
|
+
if (summary.config.url_prefix) {
|
|
694
|
+
state.urlPrefix = summary.config.url_prefix.endsWith("/") ? summary.config.url_prefix.slice(0, -1) : summary.config.url_prefix;
|
|
695
|
+
}
|
|
696
|
+
const backendLevels = Object.keys(summary.levels);
|
|
697
|
+
state.backendHasUnassignedLevel = backendLevels.includes(UNASSIGNED_LEVEL);
|
|
698
|
+
if (Array.isArray(summary.unassigned) && summary.unassigned.length > 0 && !state.backendHasUnassignedLevel) {
|
|
699
|
+
state.summaryUnassigned = summary.unassigned;
|
|
700
|
+
}
|
|
701
|
+
const availableLevels = backendLevels.slice();
|
|
702
|
+
if (state.summaryUnassigned && !state.backendHasUnassignedLevel) {
|
|
703
|
+
availableLevels.push(UNASSIGNED_LEVEL);
|
|
704
|
+
}
|
|
705
|
+
if (explicitLevels && explicitLevels.length > 0) {
|
|
706
|
+
const intersection = explicitLevels.filter((l) => availableLevels.includes(l));
|
|
707
|
+
const removed = explicitLevels.filter((l) => !availableLevels.includes(l));
|
|
708
|
+
if (removed.length > 0) {
|
|
709
|
+
console.warn(
|
|
710
|
+
`[route-forge] levels not in backend summary and dropped: ${removed.join(", ")}`
|
|
781
711
|
);
|
|
782
712
|
}
|
|
783
|
-
|
|
713
|
+
state.levels = intersection;
|
|
714
|
+
} else {
|
|
715
|
+
state.levels = availableLevels;
|
|
716
|
+
}
|
|
717
|
+
const backendEager = backendLevels.filter((lvl) => summary.levels[lvl]?.load === "eager");
|
|
718
|
+
if (!explicitEager) {
|
|
719
|
+
state.eager = backendEager;
|
|
720
|
+
} else {
|
|
721
|
+
const union = /* @__PURE__ */ new Set([...backendEager, ...explicitEager]);
|
|
722
|
+
state.eager = [...union];
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/route-store.ts
|
|
727
|
+
var RouteStore = class {
|
|
728
|
+
constructor(deps) {
|
|
729
|
+
this.inflight = /* @__PURE__ */ new Map();
|
|
730
|
+
/** 每个层级的失效代数,用于检测 loadOne 期间是否发生了 invalidate */
|
|
731
|
+
this.invalidationGens = /* @__PURE__ */ new Map();
|
|
732
|
+
this.cache = deps.cache;
|
|
733
|
+
this.state = deps.state;
|
|
734
|
+
this.baseURL = deps.baseURL;
|
|
735
|
+
this.fetchMeta = deps.fetchMeta;
|
|
736
|
+
this.autoDiscoveryPromise = deps.autoDiscoveryPromise;
|
|
737
|
+
this.getAutoDiscoveryError = deps.getAutoDiscoveryError;
|
|
738
|
+
}
|
|
739
|
+
assertLevelDeclared(level) {
|
|
740
|
+
if (!this.state.levels.includes(level)) {
|
|
741
|
+
throw new UnknownLevelError(level);
|
|
742
|
+
}
|
|
784
743
|
}
|
|
785
|
-
async
|
|
786
|
-
return await fetchMeta(
|
|
744
|
+
async fetchLevel(level) {
|
|
745
|
+
return await this.fetchMeta(
|
|
746
|
+
`__forge__.load.${level}`,
|
|
747
|
+
buildUrl(level, { baseURL: this.baseURL, endpoint: this.state.endpoint }),
|
|
748
|
+
level
|
|
749
|
+
);
|
|
787
750
|
}
|
|
788
|
-
async
|
|
751
|
+
async loadOne(level) {
|
|
752
|
+
const autoDiscoveryError = this.getAutoDiscoveryError();
|
|
789
753
|
if (autoDiscoveryError) throw autoDiscoveryError;
|
|
790
|
-
assertLevelDeclared(level);
|
|
791
|
-
if (cache.get(level)) return;
|
|
792
|
-
const existing = inflight.get(level);
|
|
754
|
+
this.assertLevelDeclared(level);
|
|
755
|
+
if (this.cache.get(level)) return;
|
|
756
|
+
const existing = this.inflight.get(level);
|
|
793
757
|
if (existing) return existing;
|
|
794
|
-
const gen = invalidationGens.get(level) ?? 0;
|
|
758
|
+
const gen = this.invalidationGens.get(level) ?? 0;
|
|
795
759
|
const p = (async () => {
|
|
796
760
|
try {
|
|
797
|
-
if (level === UNASSIGNED_LEVEL && !backendHasUnassignedLevel && summaryUnassigned) {
|
|
761
|
+
if (level === UNASSIGNED_LEVEL && !this.state.backendHasUnassignedLevel && this.state.summaryUnassigned) {
|
|
798
762
|
const routes = {};
|
|
799
|
-
for (const r of summaryUnassigned) {
|
|
763
|
+
for (const r of this.state.summaryUnassigned) {
|
|
800
764
|
routes[r.name] = { ...r, level: UNASSIGNED_LEVEL };
|
|
801
765
|
}
|
|
802
|
-
cache.set({ level: UNASSIGNED_LEVEL, routes, cache: null });
|
|
766
|
+
this.cache.set({ level: UNASSIGNED_LEVEL, routes, cache: null });
|
|
803
767
|
return;
|
|
804
768
|
}
|
|
805
|
-
const resp = await fetchLevel(level);
|
|
806
|
-
if ((invalidationGens.get(level) ?? 0) === gen) {
|
|
807
|
-
cache.set(resp);
|
|
769
|
+
const resp = await this.fetchLevel(level);
|
|
770
|
+
if ((this.invalidationGens.get(level) ?? 0) === gen) {
|
|
771
|
+
this.cache.set(resp);
|
|
808
772
|
}
|
|
809
773
|
} finally {
|
|
810
|
-
inflight.delete(level);
|
|
774
|
+
this.inflight.delete(level);
|
|
811
775
|
}
|
|
812
776
|
})();
|
|
813
|
-
inflight.set(level, p);
|
|
777
|
+
this.inflight.set(level, p);
|
|
814
778
|
return p;
|
|
815
779
|
}
|
|
816
|
-
async
|
|
817
|
-
await autoDiscoveryPromise;
|
|
780
|
+
async load(level) {
|
|
781
|
+
await this.autoDiscoveryPromise;
|
|
818
782
|
const list = Array.isArray(level) ? level : [level];
|
|
819
|
-
await Promise.all(list.map(loadOne));
|
|
783
|
+
await Promise.all(list.map((l) => this.loadOne(l)));
|
|
820
784
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
return buildRequestUrl(meta, params ?? {});
|
|
828
|
-
}
|
|
829
|
-
function buildRequestUrl(meta, params) {
|
|
830
|
-
const defaults = meta.parameter_defaults ?? {};
|
|
831
|
-
const missingRequired = [];
|
|
832
|
-
const values = {};
|
|
833
|
-
for (const p of meta.parameters) {
|
|
834
|
-
let v = params[p];
|
|
835
|
-
if ((v === void 0 || v === null) && p in defaults) {
|
|
836
|
-
v = defaults[p];
|
|
837
|
-
}
|
|
838
|
-
if (v === void 0 || v === null) {
|
|
839
|
-
if (!meta.uri.includes(`{${p}?}`)) {
|
|
840
|
-
missingRequired.push(p);
|
|
841
|
-
}
|
|
842
|
-
} else {
|
|
843
|
-
values[p] = v;
|
|
785
|
+
invalidate(level) {
|
|
786
|
+
if (level === void 0) {
|
|
787
|
+
this.cache.clear();
|
|
788
|
+
this.inflight.clear();
|
|
789
|
+
for (const lvl of this.state.levels) {
|
|
790
|
+
this.invalidationGens.set(lvl, (this.invalidationGens.get(lvl) ?? 0) + 1);
|
|
844
791
|
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const optional = raw.endsWith("?");
|
|
851
|
-
const name = optional ? raw.slice(0, -1) : raw;
|
|
852
|
-
if (values[name] !== void 0) {
|
|
853
|
-
const val = values[name];
|
|
854
|
-
if (typeof val === "object") {
|
|
855
|
-
throw new ForgeError(
|
|
856
|
-
`Path parameter "${name}" must be a primitive value (string, number, boolean), got ${typeof val}`,
|
|
857
|
-
{ code: "RF_FE_003", route: meta.name, context: { param: name, value: val } }
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
return encodeURIComponent(String(val));
|
|
792
|
+
} else if (Array.isArray(level)) {
|
|
793
|
+
for (const lvl of level) {
|
|
794
|
+
this.cache.del(lvl);
|
|
795
|
+
this.inflight.delete(lvl);
|
|
796
|
+
this.invalidationGens.set(lvl, (this.invalidationGens.get(lvl) ?? 0) + 1);
|
|
861
797
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
const prefix2 = effectiveUrlPrefix.endsWith("/") ? effectiveUrlPrefix.slice(0, -1) : effectiveUrlPrefix;
|
|
867
|
-
return uri.startsWith("/") ? `${prefix2}${uri}` : `${prefix2}/${uri}`;
|
|
798
|
+
} else {
|
|
799
|
+
this.cache.del(level);
|
|
800
|
+
this.inflight.delete(level);
|
|
801
|
+
this.invalidationGens.set(level, (this.invalidationGens.get(level) ?? 0) + 1);
|
|
868
802
|
}
|
|
869
|
-
const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
|
|
870
|
-
const prefix = effectiveUrlPrefix;
|
|
871
|
-
return uri.startsWith("/") ? `${base}${prefix}${uri}` : `${base}${prefix}/${uri}`;
|
|
872
803
|
}
|
|
873
|
-
|
|
874
|
-
|
|
804
|
+
isLoaded(level) {
|
|
805
|
+
if (level) return this.cache.get(level) !== void 0;
|
|
806
|
+
return this.state.levels.length > 0 && this.state.levels.every((lvl) => this.cache.get(lvl) !== void 0);
|
|
807
|
+
}
|
|
808
|
+
findRouteMeta(level, name) {
|
|
809
|
+
const entry = this.cache.get(level);
|
|
875
810
|
const meta = entry?.routes[name];
|
|
876
811
|
if (meta) {
|
|
877
812
|
return { ...meta, level };
|
|
878
813
|
}
|
|
879
814
|
return void 0;
|
|
880
815
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
ctrl.abort(abortReason);
|
|
889
|
-
}
|
|
890
|
-
await autoDiscoveryPromise;
|
|
891
|
-
await load(level);
|
|
892
|
-
const meta = findRouteMeta(level, name);
|
|
893
|
-
if (!meta) {
|
|
894
|
-
throw new UnknownRouteError(name, level);
|
|
816
|
+
getRoutes(level) {
|
|
817
|
+
if (level !== void 0) {
|
|
818
|
+
const entry = this.cache.get(level);
|
|
819
|
+
const routes = entry?.routes ?? {};
|
|
820
|
+
const result2 = {};
|
|
821
|
+
for (const [k, v] of Object.entries(routes)) {
|
|
822
|
+
result2[k] = JSON.parse(JSON.stringify(v));
|
|
895
823
|
}
|
|
896
|
-
return
|
|
897
|
-
}
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
824
|
+
return result2;
|
|
825
|
+
}
|
|
826
|
+
const result = {};
|
|
827
|
+
for (const lvl of this.state.levels) {
|
|
828
|
+
const entry = this.cache.get(lvl);
|
|
829
|
+
if (entry) {
|
|
830
|
+
const levelRoutes = {};
|
|
831
|
+
for (const [k, v] of Object.entries(entry.routes)) {
|
|
832
|
+
levelRoutes[k] = JSON.parse(JSON.stringify(v));
|
|
833
|
+
}
|
|
834
|
+
result[lvl] = levelRoutes;
|
|
904
835
|
}
|
|
905
|
-
}
|
|
906
|
-
return
|
|
836
|
+
}
|
|
837
|
+
return result;
|
|
907
838
|
}
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
// src/http-runner.ts
|
|
842
|
+
function createHttpRunner(deps) {
|
|
843
|
+
const {
|
|
844
|
+
ensureAdapter,
|
|
845
|
+
requestInterceptors,
|
|
846
|
+
responseInterceptors,
|
|
847
|
+
load,
|
|
848
|
+
findRouteMeta,
|
|
849
|
+
baseURL,
|
|
850
|
+
state,
|
|
851
|
+
timeout,
|
|
852
|
+
loadingTracker,
|
|
853
|
+
autoDiscoveryPromise
|
|
854
|
+
} = deps;
|
|
908
855
|
async function doApiCall(meta, params, signal) {
|
|
909
856
|
const {
|
|
910
857
|
pathParams,
|
|
@@ -917,7 +864,10 @@ var RouteForge = (function (exports) {
|
|
|
917
864
|
throw new RequestAbortedError(meta.name, meta.level, signal.reason);
|
|
918
865
|
}
|
|
919
866
|
const method = pickMethod(meta);
|
|
920
|
-
const urlWithQuery = appendQuery(
|
|
867
|
+
const urlWithQuery = appendQuery(
|
|
868
|
+
buildRequestUrl(meta, pathParams, { baseURL, urlPrefix: state.urlPrefix }),
|
|
869
|
+
query
|
|
870
|
+
);
|
|
921
871
|
const config = {
|
|
922
872
|
route: meta.name,
|
|
923
873
|
level: meta.level ?? "",
|
|
@@ -972,88 +922,77 @@ var RouteForge = (function (exports) {
|
|
|
972
922
|
loadingTracker.stop();
|
|
973
923
|
}
|
|
974
924
|
}
|
|
975
|
-
function
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
for (const lvl of level) {
|
|
984
|
-
cache.del(lvl);
|
|
985
|
-
inflight.delete(lvl);
|
|
986
|
-
invalidationGens.set(lvl, (invalidationGens.get(lvl) ?? 0) + 1);
|
|
925
|
+
return function api(level, name, params = {}) {
|
|
926
|
+
let ctrl;
|
|
927
|
+
let abortedBeforeInit = false;
|
|
928
|
+
let abortReason;
|
|
929
|
+
const work = (async () => {
|
|
930
|
+
ctrl = new AbortController();
|
|
931
|
+
if (abortedBeforeInit) {
|
|
932
|
+
ctrl.abort(abortReason);
|
|
987
933
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
}
|
|
994
|
-
function isLoaded(level) {
|
|
995
|
-
if (level) return cache.get(level) !== void 0;
|
|
996
|
-
return effectiveLevels.length > 0 && effectiveLevels.every((lvl) => cache.get(lvl) !== void 0);
|
|
997
|
-
}
|
|
998
|
-
function hasRoute(level, name) {
|
|
999
|
-
assertDiscoveryReady();
|
|
1000
|
-
return findRouteMeta(level, name) !== void 0;
|
|
1001
|
-
}
|
|
1002
|
-
function getRoutes(level) {
|
|
1003
|
-
if (level !== void 0) {
|
|
1004
|
-
const entry = cache.get(level);
|
|
1005
|
-
const routes = entry?.routes ?? {};
|
|
1006
|
-
const result2 = {};
|
|
1007
|
-
for (const [k, v] of Object.entries(routes)) {
|
|
1008
|
-
result2[k] = JSON.parse(JSON.stringify(v));
|
|
934
|
+
await autoDiscoveryPromise;
|
|
935
|
+
await load(level);
|
|
936
|
+
const meta = findRouteMeta(level, name);
|
|
937
|
+
if (!meta) {
|
|
938
|
+
throw new UnknownRouteError(name, level);
|
|
1009
939
|
}
|
|
1010
|
-
return
|
|
1011
|
-
}
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
levelRoutes[k] = JSON.parse(JSON.stringify(v));
|
|
1019
|
-
}
|
|
1020
|
-
result[lvl] = levelRoutes;
|
|
940
|
+
return doApiCall(meta, params, ctrl.signal);
|
|
941
|
+
})();
|
|
942
|
+
const request = work;
|
|
943
|
+
request.abort = () => {
|
|
944
|
+
if (ctrl) {
|
|
945
|
+
ctrl.abort();
|
|
946
|
+
} else {
|
|
947
|
+
abortedBeforeInit = true;
|
|
1021
948
|
}
|
|
1022
|
-
}
|
|
1023
|
-
return
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
949
|
+
};
|
|
950
|
+
return request;
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// src/resolveRouteName.ts
|
|
955
|
+
async function resolveRouteName(forge, level, prefix, suffix, separator = ".") {
|
|
956
|
+
if (!suffix) return prefix;
|
|
957
|
+
const joined = `${prefix}${separator}${suffix}`;
|
|
958
|
+
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
959
|
+
await forge.load(level);
|
|
960
|
+
if (forge.hasRoute(level, joined)) return joined;
|
|
961
|
+
if (forge.hasRoute(level, suffix)) return suffix;
|
|
962
|
+
throw new UnknownRouteError(joined, level);
|
|
963
|
+
}
|
|
964
|
+
function resolveRouteNameSync(forge, level, prefix, suffix, separator = ".") {
|
|
965
|
+
if (!suffix) return prefix;
|
|
966
|
+
const joined = `${prefix}${separator}${suffix}`;
|
|
967
|
+
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
968
|
+
if (forge.hasRoute(level, joined)) return joined;
|
|
969
|
+
if (forge.hasRoute(level, suffix)) return suffix;
|
|
970
|
+
throw new UnknownRouteError(joined, level);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// src/defineImmutableProps.ts
|
|
974
|
+
function defineImmutableProps(target, props) {
|
|
975
|
+
for (const key of Object.keys(props)) {
|
|
976
|
+
const val = props[key];
|
|
977
|
+
Object.defineProperty(target, key, {
|
|
978
|
+
value: val !== null && typeof val === "object" ? Object.freeze(val) : val,
|
|
979
|
+
writable: false,
|
|
980
|
+
enumerable: false,
|
|
981
|
+
configurable: false
|
|
982
|
+
});
|
|
1050
983
|
}
|
|
1051
|
-
|
|
984
|
+
return target;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/bound-forge.ts
|
|
988
|
+
function createBoundForgeFactory(deps) {
|
|
989
|
+
const { load, api, route, hasRoute, getRoutes, invalidate, isLoaded, loadingTracker } = deps;
|
|
990
|
+
const resolver = { load, hasRoute };
|
|
1052
991
|
function createBoundForge(level, prefix) {
|
|
1053
992
|
const levelLoadedPromise = load(level);
|
|
1054
993
|
levelLoadedPromise.catch(() => {
|
|
1055
994
|
});
|
|
1056
|
-
const apiFn = prefix ? (name, params) => resolveRouteName(
|
|
995
|
+
const apiFn = prefix ? (name, params) => resolveRouteName(resolver, level, prefix, name).then(
|
|
1057
996
|
(resolved) => api(level, resolved, params)
|
|
1058
997
|
) : (name, params) => api(level, name, params);
|
|
1059
998
|
const callable = apiFn;
|
|
@@ -1061,8 +1000,8 @@ var RouteForge = (function (exports) {
|
|
|
1061
1000
|
level,
|
|
1062
1001
|
...prefix !== void 0 ? { prefix } : {},
|
|
1063
1002
|
api: apiFn,
|
|
1064
|
-
route: prefix ? (name, params) => route(level, resolveRouteNameSync(
|
|
1065
|
-
url: prefix ? (name, params) => route(level, resolveRouteNameSync(
|
|
1003
|
+
route: prefix ? (name, params) => route(level, resolveRouteNameSync(resolver, level, prefix, name), params) : (name, params) => route(level, name, params),
|
|
1004
|
+
url: prefix ? (name, params) => route(level, resolveRouteNameSync(resolver, level, prefix, name), params) : (name, params) => route(level, name, params),
|
|
1066
1005
|
hasRoute: (name) => hasRoute(level, name),
|
|
1067
1006
|
getRoutes: () => getRoutes(level),
|
|
1068
1007
|
load: () => load(level),
|
|
@@ -1106,6 +1045,208 @@ var RouteForge = (function (exports) {
|
|
|
1106
1045
|
attachBoundMethods(bound, levelLoadedPromise, level);
|
|
1107
1046
|
return bound;
|
|
1108
1047
|
}
|
|
1048
|
+
return createBoundForgeWithMethods;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// src/forge.ts
|
|
1052
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
1053
|
+
var DEFAULT_CACHE_TTL = 3600;
|
|
1054
|
+
function createRouteForge(options) {
|
|
1055
|
+
if (!options.endpoint) throw new TypeError("options.endpoint is required");
|
|
1056
|
+
const {
|
|
1057
|
+
adapter = "auto",
|
|
1058
|
+
timeout = DEFAULT_TIMEOUT,
|
|
1059
|
+
baseURL = "",
|
|
1060
|
+
interceptors: declarativeInterceptors,
|
|
1061
|
+
cache: cacheOpts = {}
|
|
1062
|
+
} = options;
|
|
1063
|
+
const loadingTracker = new LoadingTracker();
|
|
1064
|
+
const explicitLevels = options.levels;
|
|
1065
|
+
const explicitEager = options.eager;
|
|
1066
|
+
const explicitEndpoint = options.endpoint;
|
|
1067
|
+
const discoveryState = {
|
|
1068
|
+
levels: explicitLevels ?? [],
|
|
1069
|
+
eager: explicitEager ?? [],
|
|
1070
|
+
endpoint: explicitEndpoint,
|
|
1071
|
+
urlPrefix: "",
|
|
1072
|
+
summaryUnassigned: void 0,
|
|
1073
|
+
backendHasUnassignedLevel: false
|
|
1074
|
+
};
|
|
1075
|
+
const discoveryInputs = { explicitLevels, explicitEager, explicitEndpoint };
|
|
1076
|
+
let autoDiscoveryCompleted = false;
|
|
1077
|
+
let resolveReady;
|
|
1078
|
+
let rejectReady;
|
|
1079
|
+
const readyPromise = new Promise((resolve, reject) => {
|
|
1080
|
+
resolveReady = resolve;
|
|
1081
|
+
rejectReady = reject;
|
|
1082
|
+
});
|
|
1083
|
+
readyPromise.catch(() => {
|
|
1084
|
+
});
|
|
1085
|
+
const cacheTtl = cacheOpts.ttl ?? DEFAULT_CACHE_TTL;
|
|
1086
|
+
const cacheStorage = cacheOpts.storage ?? "memory";
|
|
1087
|
+
const cache = new RouteCache({ storage: cacheStorage, ttl: cacheTtl });
|
|
1088
|
+
const requestInterceptors = new InterceptorManagerImpl();
|
|
1089
|
+
const responseInterceptors = new InterceptorManagerImpl();
|
|
1090
|
+
if (declarativeInterceptors?.request) {
|
|
1091
|
+
for (const entry of declarativeInterceptors.request) {
|
|
1092
|
+
if (typeof entry === "function") {
|
|
1093
|
+
requestInterceptors.use(entry);
|
|
1094
|
+
} else {
|
|
1095
|
+
const [onFulfilled, onRejected] = entry;
|
|
1096
|
+
requestInterceptors.use(onFulfilled, onRejected);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
if (declarativeInterceptors?.response) {
|
|
1101
|
+
for (const entry of declarativeInterceptors.response) {
|
|
1102
|
+
if (typeof entry === "function") {
|
|
1103
|
+
responseInterceptors.use(entry);
|
|
1104
|
+
} else {
|
|
1105
|
+
const [onFulfilled, onRejected] = entry;
|
|
1106
|
+
responseInterceptors.use(onFulfilled, onRejected);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const adapterPromise = resolveAdapter({
|
|
1111
|
+
adapter,
|
|
1112
|
+
forgeInterceptors: { request: requestInterceptors, response: responseInterceptors }
|
|
1113
|
+
});
|
|
1114
|
+
let adapterResolved = false;
|
|
1115
|
+
let adapterObj = null;
|
|
1116
|
+
async function ensureAdapter() {
|
|
1117
|
+
if (!adapterResolved) {
|
|
1118
|
+
adapterObj = await adapterPromise.catch((e) => {
|
|
1119
|
+
if (e instanceof AdapterNotFoundError) throw e;
|
|
1120
|
+
return resolveAdapter({
|
|
1121
|
+
adapter: "builtin",
|
|
1122
|
+
forgeInterceptors: { request: requestInterceptors, response: responseInterceptors }
|
|
1123
|
+
});
|
|
1124
|
+
});
|
|
1125
|
+
adapterResolved = true;
|
|
1126
|
+
}
|
|
1127
|
+
return adapterObj;
|
|
1128
|
+
}
|
|
1129
|
+
function assertDiscoveryReady() {
|
|
1130
|
+
if (!autoDiscoveryCompleted && !explicitLevels?.length) {
|
|
1131
|
+
throw new ForgeError(
|
|
1132
|
+
"Route data not available. Auto-discovery has not completed. Use forge.ready() or forge.use(level) first.",
|
|
1133
|
+
{ code: "RF_FE_010" }
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
async function fetchMeta(routeTag, url, level = "") {
|
|
1138
|
+
const adp = await ensureAdapter();
|
|
1139
|
+
const config = {
|
|
1140
|
+
route: routeTag,
|
|
1141
|
+
level,
|
|
1142
|
+
method: "GET",
|
|
1143
|
+
url,
|
|
1144
|
+
headers: { Accept: "application/json" },
|
|
1145
|
+
params: {},
|
|
1146
|
+
timeout,
|
|
1147
|
+
meta: {
|
|
1148
|
+
name: routeTag,
|
|
1149
|
+
uri: url,
|
|
1150
|
+
methods: ["GET"],
|
|
1151
|
+
parameters: [],
|
|
1152
|
+
level
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
const doRawRequest = adp.requestRaw ?? adp.request;
|
|
1156
|
+
const resp = await doRawRequest(config);
|
|
1157
|
+
if (!resp || resp.status < 200 || resp.status >= 300) {
|
|
1158
|
+
throw new HTTPError(
|
|
1159
|
+
`Failed to fetch "${routeTag}": HTTP ${resp?.status}`,
|
|
1160
|
+
{ level, status: resp?.status, url, method: "GET" }
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
return resp.data;
|
|
1164
|
+
}
|
|
1165
|
+
const autoDiscoveryPromise = fetchSummary(discoveryInputs, baseURL, fetchMeta).then((summary) => {
|
|
1166
|
+
if (summary === null) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
applySummaryToState(summary, discoveryState, discoveryInputs);
|
|
1170
|
+
});
|
|
1171
|
+
let autoDiscoveryError = null;
|
|
1172
|
+
autoDiscoveryPromise.catch((e) => {
|
|
1173
|
+
autoDiscoveryError = e;
|
|
1174
|
+
});
|
|
1175
|
+
const store = new RouteStore({
|
|
1176
|
+
cache,
|
|
1177
|
+
state: discoveryState,
|
|
1178
|
+
baseURL,
|
|
1179
|
+
fetchMeta,
|
|
1180
|
+
autoDiscoveryPromise,
|
|
1181
|
+
getAutoDiscoveryError: () => autoDiscoveryError
|
|
1182
|
+
});
|
|
1183
|
+
const load = (level) => store.load(level);
|
|
1184
|
+
const findRouteMeta = (level, name) => store.findRouteMeta(level, name);
|
|
1185
|
+
const invalidate = (level) => store.invalidate(level);
|
|
1186
|
+
const isLoaded = (level) => store.isLoaded(level);
|
|
1187
|
+
function getRoutes(level) {
|
|
1188
|
+
return level === void 0 ? store.getRoutes() : store.getRoutes(level);
|
|
1189
|
+
}
|
|
1190
|
+
function route(level, name, params) {
|
|
1191
|
+
assertDiscoveryReady();
|
|
1192
|
+
const meta = findRouteMeta(level, name);
|
|
1193
|
+
if (!meta) {
|
|
1194
|
+
throw new UnknownRouteError(name, level);
|
|
1195
|
+
}
|
|
1196
|
+
return buildRequestUrl(meta, params ?? {}, { baseURL, urlPrefix: discoveryState.urlPrefix });
|
|
1197
|
+
}
|
|
1198
|
+
const api = createHttpRunner({
|
|
1199
|
+
ensureAdapter,
|
|
1200
|
+
requestInterceptors,
|
|
1201
|
+
responseInterceptors,
|
|
1202
|
+
load,
|
|
1203
|
+
findRouteMeta,
|
|
1204
|
+
baseURL,
|
|
1205
|
+
state: discoveryState,
|
|
1206
|
+
timeout,
|
|
1207
|
+
loadingTracker,
|
|
1208
|
+
autoDiscoveryPromise
|
|
1209
|
+
});
|
|
1210
|
+
function hasRoute(level, name) {
|
|
1211
|
+
assertDiscoveryReady();
|
|
1212
|
+
return findRouteMeta(level, name) !== void 0;
|
|
1213
|
+
}
|
|
1214
|
+
void autoDiscoveryPromise.then(() => {
|
|
1215
|
+
autoDiscoveryCompleted = true;
|
|
1216
|
+
if (discoveryState.eager.length > 0) {
|
|
1217
|
+
return Promise.allSettled(discoveryState.eager.map((lvl) => load(lvl))).then((results) => {
|
|
1218
|
+
results.forEach((r, i) => {
|
|
1219
|
+
if (r.status === "rejected") {
|
|
1220
|
+
console.error(
|
|
1221
|
+
`[route-forge] eager load failed for level "${discoveryState.eager[i]}":`,
|
|
1222
|
+
r.reason
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
}).then(() => {
|
|
1229
|
+
resolveReady(forgeInstance);
|
|
1230
|
+
}).catch((e) => {
|
|
1231
|
+
rejectReady(e);
|
|
1232
|
+
});
|
|
1233
|
+
function ready(onFulfilled, onRejected) {
|
|
1234
|
+
if (onFulfilled) {
|
|
1235
|
+
const p = readyPromise.then(onFulfilled, onRejected);
|
|
1236
|
+
return p.then(() => forgeInstance);
|
|
1237
|
+
}
|
|
1238
|
+
return readyPromise;
|
|
1239
|
+
}
|
|
1240
|
+
const createBoundForgeWithMethods = createBoundForgeFactory({
|
|
1241
|
+
load,
|
|
1242
|
+
api,
|
|
1243
|
+
route,
|
|
1244
|
+
hasRoute,
|
|
1245
|
+
getRoutes,
|
|
1246
|
+
invalidate,
|
|
1247
|
+
isLoaded,
|
|
1248
|
+
loadingTracker
|
|
1249
|
+
});
|
|
1109
1250
|
const forgeInstance = {
|
|
1110
1251
|
api,
|
|
1111
1252
|
load,
|
|
@@ -1129,62 +1270,6 @@ var RouteForge = (function (exports) {
|
|
|
1129
1270
|
};
|
|
1130
1271
|
return forgeInstance;
|
|
1131
1272
|
}
|
|
1132
|
-
function pickMethod(meta) {
|
|
1133
|
-
const m = meta.methods.find((x) => x.toUpperCase() !== "HEAD");
|
|
1134
|
-
return (m ?? meta.methods[0] ?? "GET").toUpperCase();
|
|
1135
|
-
}
|
|
1136
|
-
function appendQuery(url, query) {
|
|
1137
|
-
if (!query) return url;
|
|
1138
|
-
const usp = new URLSearchParams();
|
|
1139
|
-
for (const [k, v] of Object.entries(query)) {
|
|
1140
|
-
if (v === void 0 || v === null) continue;
|
|
1141
|
-
usp.append(k, String(v));
|
|
1142
|
-
}
|
|
1143
|
-
const qs = usp.toString();
|
|
1144
|
-
if (!qs) return url;
|
|
1145
|
-
return url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
|
|
1146
|
-
}
|
|
1147
|
-
function resolveApiParams(input) {
|
|
1148
|
-
const {
|
|
1149
|
-
params: explicitParams,
|
|
1150
|
-
query: rawQuery,
|
|
1151
|
-
body: rawBody,
|
|
1152
|
-
headers: rawHeaders,
|
|
1153
|
-
timeout: perCallTimeout,
|
|
1154
|
-
...flatRest
|
|
1155
|
-
} = input;
|
|
1156
|
-
const pathParams = explicitParams ? { ...explicitParams } : {};
|
|
1157
|
-
for (const [k, v] of Object.entries(flatRest)) {
|
|
1158
|
-
if (!(k in pathParams)) {
|
|
1159
|
-
pathParams[k] = v;
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
let query;
|
|
1163
|
-
let body;
|
|
1164
|
-
let headers;
|
|
1165
|
-
if (rawQuery !== void 0) {
|
|
1166
|
-
if (typeof rawQuery === "object" && rawQuery !== null) {
|
|
1167
|
-
query = rawQuery;
|
|
1168
|
-
} else if (!("query" in pathParams)) {
|
|
1169
|
-
pathParams.query = rawQuery;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
if (rawBody !== void 0) {
|
|
1173
|
-
if (typeof rawBody !== "string" && typeof rawBody !== "number") {
|
|
1174
|
-
body = rawBody;
|
|
1175
|
-
} else if (!("body" in pathParams)) {
|
|
1176
|
-
pathParams.body = rawBody;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
if (rawHeaders !== void 0) {
|
|
1180
|
-
if (typeof rawHeaders === "object" && rawHeaders !== null) {
|
|
1181
|
-
headers = rawHeaders;
|
|
1182
|
-
} else if (!("headers" in pathParams)) {
|
|
1183
|
-
pathParams.headers = rawHeaders;
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
return { pathParams, query, body, headers, timeout: perCallTimeout };
|
|
1187
|
-
}
|
|
1188
1273
|
|
|
1189
1274
|
exports.AdapterNotFoundError = AdapterNotFoundError;
|
|
1190
1275
|
exports.ForgeError = ForgeError;
|