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