@route-forge/core 2.2.1 → 3.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 +31 -23
- package/README_zh.md +30 -23
- package/dist/codegen.cjs +7 -8
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +2 -3
- package/dist/codegen.d.ts +2 -3
- package/dist/codegen.js +7 -8
- package/dist/codegen.js.map +1 -1
- package/dist/index.cjs +182 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -8
- package/dist/index.d.ts +27 -8
- package/dist/index.js +181 -83
- package/dist/index.js.map +1 -1
- package/dist/route-forge.global.js +182 -82
- package/dist/route-forge.global.js.map +1 -1
- package/dist/route-forge.global.min.js +2 -2
- package/dist/{types-CDKE8rw-.d.cts → types-B_vdcRqx.d.cts} +57 -18
- package/dist/{types-CDKE8rw-.d.ts → types-B_vdcRqx.d.ts} +57 -18
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -143,30 +143,56 @@ var ForgeError = class extends Error {
|
|
|
143
143
|
if (opts.cause !== void 0) this.cause = opts.cause;
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
|
+
function formatCandidates(candidates) {
|
|
147
|
+
if (!candidates || candidates.length === 0) return "";
|
|
148
|
+
const MAX = 5;
|
|
149
|
+
const shown = candidates.slice(0, MAX).join(", ");
|
|
150
|
+
const more = candidates.length > MAX ? ` (+${candidates.length - MAX} more)` : "";
|
|
151
|
+
return ` Available: ${shown}${more}`;
|
|
152
|
+
}
|
|
146
153
|
var UnknownRouteError = class extends ForgeError {
|
|
147
|
-
constructor(route, level) {
|
|
148
|
-
super(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
constructor(route, level, candidates) {
|
|
155
|
+
super(
|
|
156
|
+
`Route "${route}" not found${level ? ` in level "${level}"` : ""}.${formatCandidates(candidates)}`,
|
|
157
|
+
{
|
|
158
|
+
code: "RF_FE_001",
|
|
159
|
+
route,
|
|
160
|
+
level,
|
|
161
|
+
context: candidates && candidates.length > 0 ? { candidates } : void 0
|
|
162
|
+
}
|
|
163
|
+
);
|
|
153
164
|
}
|
|
154
165
|
};
|
|
155
166
|
var UnknownLevelError = class extends ForgeError {
|
|
156
|
-
constructor(level) {
|
|
157
|
-
super(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
constructor(level, candidates) {
|
|
168
|
+
super(
|
|
169
|
+
`Level "${level}" not declared in options.levels.${formatCandidates(candidates)}`,
|
|
170
|
+
{
|
|
171
|
+
code: "RF_FE_002",
|
|
172
|
+
level,
|
|
173
|
+
context: candidates && candidates.length > 0 ? { candidates } : void 0
|
|
174
|
+
}
|
|
175
|
+
);
|
|
161
176
|
}
|
|
162
177
|
};
|
|
163
178
|
var MissingRouteParamError = class extends ForgeError {
|
|
164
|
-
constructor(route, missingParams) {
|
|
165
|
-
super(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
179
|
+
constructor(route, missingParams, uri) {
|
|
180
|
+
super(
|
|
181
|
+
`Missing path parameter(s) ${missingParams.join(", ")} for route "${route}"${uri ? ` (${uri})` : ""}`,
|
|
182
|
+
{
|
|
183
|
+
code: "RF_FE_003",
|
|
184
|
+
route,
|
|
185
|
+
context: { missingParams, ...uri !== void 0 ? { uri } : {} }
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
var InvalidPathParamError = class extends ForgeError {
|
|
191
|
+
constructor(route, param, value) {
|
|
192
|
+
super(
|
|
193
|
+
`Path parameter "${param}" must be a primitive value (string, number, boolean), got ${typeof value}`,
|
|
194
|
+
{ code: "RF_FE_003", route, context: { param, value } }
|
|
195
|
+
);
|
|
170
196
|
}
|
|
171
197
|
};
|
|
172
198
|
var AdapterNotFoundError = class extends ForgeError {
|
|
@@ -199,6 +225,7 @@ var HTTPError = class extends ForgeError {
|
|
|
199
225
|
context: { status: opts.status, url: opts.url, method: opts.method },
|
|
200
226
|
cause: opts.cause
|
|
201
227
|
});
|
|
228
|
+
if (opts.response !== void 0) this.response = opts.response;
|
|
202
229
|
}
|
|
203
230
|
};
|
|
204
231
|
var RequestAbortedError = class extends ForgeError {
|
|
@@ -211,6 +238,14 @@ var RequestAbortedError = class extends ForgeError {
|
|
|
211
238
|
});
|
|
212
239
|
}
|
|
213
240
|
};
|
|
241
|
+
var DiscoveryNotReadyError = class extends ForgeError {
|
|
242
|
+
constructor() {
|
|
243
|
+
super(
|
|
244
|
+
"Route data not available. Auto-discovery has not completed. Use forge.ready() or forge.use(level) first, or await ready() before calling route()/hasRoute().",
|
|
245
|
+
{ code: "RF_FE_010" }
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
214
249
|
|
|
215
250
|
// src/interceptors.ts
|
|
216
251
|
var InterceptorManagerImpl = class {
|
|
@@ -277,6 +312,43 @@ async function runResponseInterceptors(manager, source) {
|
|
|
277
312
|
function createInterceptorManager() {
|
|
278
313
|
return new InterceptorManagerImpl();
|
|
279
314
|
}
|
|
315
|
+
function normalizeInterceptorDeclaration(value) {
|
|
316
|
+
if (value === null || value === void 0) return {};
|
|
317
|
+
let rawResolve;
|
|
318
|
+
let rawReject;
|
|
319
|
+
if (typeof value === "function") {
|
|
320
|
+
rawResolve = value;
|
|
321
|
+
} else if (Array.isArray(value)) {
|
|
322
|
+
rawResolve = value[0];
|
|
323
|
+
rawReject = value[1];
|
|
324
|
+
} else if (typeof value === "object") {
|
|
325
|
+
const obj = value;
|
|
326
|
+
rawResolve = obj.resolve;
|
|
327
|
+
rawReject = obj.reject;
|
|
328
|
+
} else {
|
|
329
|
+
throw new TypeError(
|
|
330
|
+
`Interceptor declaration must be a function, a [resolve, reject] tuple, or a { resolve, reject } object; received ${typeof value}.`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
const handler = {};
|
|
334
|
+
if (rawResolve !== void 0 && rawResolve !== null) {
|
|
335
|
+
if (typeof rawResolve !== "function") {
|
|
336
|
+
throw new TypeError(
|
|
337
|
+
`Interceptor "resolve" (onFulfilled) must be a function; received ${typeof rawResolve}.`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
handler.onFulfilled = rawResolve;
|
|
341
|
+
}
|
|
342
|
+
if (rawReject !== void 0 && rawReject !== null) {
|
|
343
|
+
if (typeof rawReject !== "function") {
|
|
344
|
+
throw new TypeError(
|
|
345
|
+
`Interceptor "reject" (onRejected) must be a function; received ${typeof rawReject}.`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
handler.onRejected = rawReject;
|
|
349
|
+
}
|
|
350
|
+
return handler;
|
|
351
|
+
}
|
|
280
352
|
|
|
281
353
|
// src/adapters/fetch-core.ts
|
|
282
354
|
function isAbortError(err, signal) {
|
|
@@ -377,7 +449,10 @@ async function rawFetch(config) {
|
|
|
377
449
|
level: config.level,
|
|
378
450
|
status: res.status,
|
|
379
451
|
url,
|
|
380
|
-
method: config.method
|
|
452
|
+
method: config.method,
|
|
453
|
+
// 完整 ResponseData 随错误逐段传递(响应拦截器 onRejected 链 → 最终 catch),
|
|
454
|
+
// 供调用方检查响应体,如 Laravel 422 校验错误 err.response.data.errors
|
|
455
|
+
response: responseData
|
|
381
456
|
}
|
|
382
457
|
);
|
|
383
458
|
}
|
|
@@ -589,7 +664,7 @@ function buildRequestUrl(meta, params, ctx) {
|
|
|
589
664
|
}
|
|
590
665
|
}
|
|
591
666
|
if (missingRequired.length > 0) {
|
|
592
|
-
throw new MissingRouteParamError(meta.name, missingRequired);
|
|
667
|
+
throw new MissingRouteParamError(meta.name, missingRequired, meta.uri);
|
|
593
668
|
}
|
|
594
669
|
let uri = meta.uri.replace(/\{([^{}]+)\}/g, (match, raw) => {
|
|
595
670
|
const optional = raw.endsWith("?");
|
|
@@ -597,10 +672,7 @@ function buildRequestUrl(meta, params, ctx) {
|
|
|
597
672
|
if (values[name] !== void 0) {
|
|
598
673
|
const val = values[name];
|
|
599
674
|
if (typeof val === "object") {
|
|
600
|
-
throw new
|
|
601
|
-
`Path parameter "${name}" must be a primitive value (string, number, boolean), got ${typeof val}`,
|
|
602
|
-
{ code: "RF_FE_003", route: meta.name, context: { param: name, value: val } }
|
|
603
|
-
);
|
|
675
|
+
throw new InvalidPathParamError(meta.name, name, val);
|
|
604
676
|
}
|
|
605
677
|
return encodeURIComponent(String(val));
|
|
606
678
|
}
|
|
@@ -673,24 +745,31 @@ function resolveApiParams(input) {
|
|
|
673
745
|
}
|
|
674
746
|
|
|
675
747
|
// src/auto-discovery.ts
|
|
748
|
+
var DEFAULT_ENDPOINT = "/_forge/routes";
|
|
676
749
|
async function fetchSummary(inputs, baseURL, fetchMeta) {
|
|
677
|
-
const { explicitLevels, explicitEndpoint } = inputs;
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
750
|
+
const { explicitLevels, explicitEndpoint, warnings } = inputs;
|
|
751
|
+
const endpoint = explicitEndpoint ?? DEFAULT_ENDPOINT;
|
|
752
|
+
const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
|
|
753
|
+
const ep = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
|
|
754
|
+
const url = `${base}${ep}`;
|
|
681
755
|
try {
|
|
682
|
-
const
|
|
683
|
-
const ep = explicitEndpoint.startsWith("/") ? explicitEndpoint : `/${explicitEndpoint}`;
|
|
684
|
-
const data = await fetchMeta("__forge__.summary", `${base}${ep}`);
|
|
756
|
+
const data = await fetchMeta("__forge__.summary", url);
|
|
685
757
|
return data;
|
|
686
758
|
} catch (e) {
|
|
687
759
|
if (explicitLevels && explicitLevels.length > 0) {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
760
|
+
if (warnings) {
|
|
761
|
+
console.warn(
|
|
762
|
+
`[route-forge] summary endpoint unreachable: ${e.message}; using explicit levels`
|
|
763
|
+
);
|
|
764
|
+
}
|
|
691
765
|
return null;
|
|
692
766
|
}
|
|
693
|
-
throw new
|
|
767
|
+
throw new NetworkError(
|
|
768
|
+
`Failed to fetch route summary from "${url}": ${e?.message ?? String(e)}; check the backend manifest endpoint or options.endpoint`,
|
|
769
|
+
void 0,
|
|
770
|
+
void 0,
|
|
771
|
+
e
|
|
772
|
+
);
|
|
694
773
|
}
|
|
695
774
|
}
|
|
696
775
|
function normalizeCacheTtl(raw) {
|
|
@@ -698,15 +777,15 @@ function normalizeCacheTtl(raw) {
|
|
|
698
777
|
return raw;
|
|
699
778
|
}
|
|
700
779
|
function applySummaryToState(summary, state, inputs) {
|
|
701
|
-
const { explicitLevels, explicitEager, explicitEndpoint } = inputs;
|
|
780
|
+
const { explicitLevels, explicitEager, explicitEndpoint, warnings } = inputs;
|
|
702
781
|
const schemeVersion = summary.schemeVersion ?? 1;
|
|
703
|
-
if (schemeVersion > 1) {
|
|
782
|
+
if (schemeVersion > 1 && warnings) {
|
|
704
783
|
console.warn(
|
|
705
784
|
`[route-forge] backend schemeVersion=${schemeVersion} > client supported 1; some features may be unavailable`
|
|
706
785
|
);
|
|
707
786
|
}
|
|
708
787
|
if (summary.config.endpoint_prefix && summary.config.endpoint_prefix !== explicitEndpoint) {
|
|
709
|
-
if (explicitEndpoint) {
|
|
788
|
+
if (explicitEndpoint && warnings) {
|
|
710
789
|
console.warn(
|
|
711
790
|
`[route-forge] backend endpoint_prefix "${summary.config.endpoint_prefix}" overrides frontend endpoint "${explicitEndpoint}"`
|
|
712
791
|
);
|
|
@@ -729,7 +808,7 @@ function applySummaryToState(summary, state, inputs) {
|
|
|
729
808
|
if (explicitLevels && explicitLevels.length > 0) {
|
|
730
809
|
const intersection = explicitLevels.filter((l) => backendLevels.includes(l));
|
|
731
810
|
const removed = explicitLevels.filter((l) => !backendLevels.includes(l));
|
|
732
|
-
if (removed.length > 0) {
|
|
811
|
+
if (removed.length > 0 && warnings) {
|
|
733
812
|
console.warn(
|
|
734
813
|
`[route-forge] levels not in backend summary and dropped: ${removed.join(", ")}`
|
|
735
814
|
);
|
|
@@ -781,7 +860,7 @@ var RouteStore = class {
|
|
|
781
860
|
}
|
|
782
861
|
assertLevelDeclared(level) {
|
|
783
862
|
if (!this.state.levels.includes(level)) {
|
|
784
|
-
throw new UnknownLevelError(level);
|
|
863
|
+
throw new UnknownLevelError(level, this.state.levels);
|
|
785
864
|
}
|
|
786
865
|
}
|
|
787
866
|
async fetchLevel(level) {
|
|
@@ -855,6 +934,7 @@ var RouteStore = class {
|
|
|
855
934
|
}
|
|
856
935
|
getRoutes(level) {
|
|
857
936
|
if (level !== void 0) {
|
|
937
|
+
this.assertLevelDeclared(level);
|
|
858
938
|
const entry = this.cache.get(level);
|
|
859
939
|
const routes = entry?.routes ?? {};
|
|
860
940
|
const result2 = {};
|
|
@@ -886,6 +966,7 @@ function createHttpRunner(deps) {
|
|
|
886
966
|
responseInterceptors,
|
|
887
967
|
load,
|
|
888
968
|
findRouteMeta,
|
|
969
|
+
getRouteNames,
|
|
889
970
|
baseURL,
|
|
890
971
|
state,
|
|
891
972
|
timeout,
|
|
@@ -937,7 +1018,10 @@ function createHttpRunner(deps) {
|
|
|
937
1018
|
level: resp.level,
|
|
938
1019
|
status: resp.status,
|
|
939
1020
|
url: resp.url,
|
|
940
|
-
method: resp.method
|
|
1021
|
+
method: resp.method,
|
|
1022
|
+
// 完整 ResponseData 随错误逐段传递(响应拦截器 onRejected 链 → 最终 catch),
|
|
1023
|
+
// 供调用方检查响应体,如 Laravel 422 校验错误 err.response.data.errors
|
|
1024
|
+
response: resp
|
|
941
1025
|
}
|
|
942
1026
|
);
|
|
943
1027
|
}
|
|
@@ -975,7 +1059,7 @@ function createHttpRunner(deps) {
|
|
|
975
1059
|
await load(level);
|
|
976
1060
|
const meta = findRouteMeta(level, name);
|
|
977
1061
|
if (!meta) {
|
|
978
|
-
throw new UnknownRouteError(name, level);
|
|
1062
|
+
throw new UnknownRouteError(name, level, getRouteNames(level));
|
|
979
1063
|
}
|
|
980
1064
|
return doApiCall(meta, params, ctrl.signal);
|
|
981
1065
|
})();
|
|
@@ -992,22 +1076,31 @@ function createHttpRunner(deps) {
|
|
|
992
1076
|
}
|
|
993
1077
|
|
|
994
1078
|
// src/resolveRouteName.ts
|
|
1079
|
+
function stripTrailingSeparator(prefix, separator) {
|
|
1080
|
+
let normalized = prefix;
|
|
1081
|
+
while (normalized.endsWith(separator)) {
|
|
1082
|
+
normalized = normalized.slice(0, -separator.length);
|
|
1083
|
+
}
|
|
1084
|
+
return normalized;
|
|
1085
|
+
}
|
|
995
1086
|
async function resolveRouteName(forge, level, prefix, suffix, separator = ".") {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1087
|
+
const normalized = stripTrailingSeparator(prefix, separator);
|
|
1088
|
+
if (!suffix) return normalized;
|
|
1089
|
+
const joined = `${normalized}${separator}${suffix}`;
|
|
1090
|
+
if (!suffix.startsWith(`${normalized}${separator}`)) return joined;
|
|
999
1091
|
await forge.load(level);
|
|
1000
1092
|
if (forge.hasRoute(level, joined)) return joined;
|
|
1001
1093
|
if (forge.hasRoute(level, suffix)) return suffix;
|
|
1002
|
-
throw new UnknownRouteError(joined, level);
|
|
1094
|
+
throw new UnknownRouteError(joined, level, forge.getRouteNames?.(level));
|
|
1003
1095
|
}
|
|
1004
1096
|
function resolveRouteNameSync(forge, level, prefix, suffix, separator = ".") {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1097
|
+
const normalized = stripTrailingSeparator(prefix, separator);
|
|
1098
|
+
if (!suffix) return normalized;
|
|
1099
|
+
const joined = `${normalized}${separator}${suffix}`;
|
|
1100
|
+
if (!suffix.startsWith(`${normalized}${separator}`)) return joined;
|
|
1008
1101
|
if (forge.hasRoute(level, joined)) return joined;
|
|
1009
1102
|
if (forge.hasRoute(level, suffix)) return suffix;
|
|
1010
|
-
throw new UnknownRouteError(joined, level);
|
|
1103
|
+
throw new UnknownRouteError(joined, level, forge.getRouteNames?.(level));
|
|
1011
1104
|
}
|
|
1012
1105
|
|
|
1013
1106
|
// src/defineImmutableProps.ts
|
|
@@ -1027,7 +1120,7 @@ function defineImmutableProps(target, props) {
|
|
|
1027
1120
|
// src/bound-forge.ts
|
|
1028
1121
|
function createBoundForgeFactory(deps) {
|
|
1029
1122
|
const { load, api, route, hasRoute, getRoutes, invalidate, isLoaded, loadingTracker } = deps;
|
|
1030
|
-
const resolver = { load, hasRoute };
|
|
1123
|
+
const resolver = { load, hasRoute, getRouteNames: (lvl) => Object.keys(getRoutes(lvl)) };
|
|
1031
1124
|
function createBoundForge(level, prefix) {
|
|
1032
1125
|
const levelLoadedPromise = load(level);
|
|
1033
1126
|
levelLoadedPromise.catch(() => {
|
|
@@ -1093,11 +1186,6 @@ var DEFAULT_TIMEOUT = 3e4;
|
|
|
1093
1186
|
var DEFAULT_CACHE_TTL = 3600;
|
|
1094
1187
|
function createRouteForge(options = {}) {
|
|
1095
1188
|
const bootstrapSummary = readEmbeddedSummary() ?? options.summary ?? null;
|
|
1096
|
-
if (!bootstrapSummary && !options.endpoint) {
|
|
1097
|
-
throw new TypeError(
|
|
1098
|
-
"createRouteForge: \u9700\u8981 options.endpoint\uFF0C\u6216 options.summary\uFF0C\u6216\u9875\u9762\u5185\u5D4C window.__ROUTE_FORGE__"
|
|
1099
|
-
);
|
|
1100
|
-
}
|
|
1101
1189
|
const {
|
|
1102
1190
|
adapter = "auto",
|
|
1103
1191
|
timeout = DEFAULT_TIMEOUT,
|
|
@@ -1105,6 +1193,7 @@ function createRouteForge(options = {}) {
|
|
|
1105
1193
|
interceptors: declarativeInterceptors,
|
|
1106
1194
|
cache: cacheOpts = {}
|
|
1107
1195
|
} = options;
|
|
1196
|
+
const warnings = options.warnings ?? true;
|
|
1108
1197
|
const loadingTracker = new LoadingTracker();
|
|
1109
1198
|
const explicitLevels = options.levels;
|
|
1110
1199
|
const explicitEager = options.eager;
|
|
@@ -1112,12 +1201,12 @@ function createRouteForge(options = {}) {
|
|
|
1112
1201
|
const discoveryState = {
|
|
1113
1202
|
levels: explicitLevels ?? [],
|
|
1114
1203
|
eager: explicitEager ?? [],
|
|
1115
|
-
endpoint: explicitEndpoint ?? bootstrapSummary?.config?.endpoint_prefix ??
|
|
1204
|
+
endpoint: explicitEndpoint ?? bootstrapSummary?.config?.endpoint_prefix ?? DEFAULT_ENDPOINT,
|
|
1116
1205
|
urlPrefix: "",
|
|
1117
1206
|
cacheTtl: void 0,
|
|
1118
1207
|
levelRoutes: {}
|
|
1119
1208
|
};
|
|
1120
|
-
const discoveryInputs = { explicitLevels, explicitEager, explicitEndpoint };
|
|
1209
|
+
const discoveryInputs = { explicitLevels, explicitEager, explicitEndpoint, warnings };
|
|
1121
1210
|
let autoDiscoveryCompleted = false;
|
|
1122
1211
|
let resolveReady;
|
|
1123
1212
|
let rejectReady;
|
|
@@ -1127,30 +1216,30 @@ function createRouteForge(options = {}) {
|
|
|
1127
1216
|
});
|
|
1128
1217
|
readyPromise.catch(() => {
|
|
1129
1218
|
});
|
|
1219
|
+
let readySettledOk = false;
|
|
1220
|
+
readyPromise.then(
|
|
1221
|
+
() => {
|
|
1222
|
+
readySettledOk = true;
|
|
1223
|
+
},
|
|
1224
|
+
() => {
|
|
1225
|
+
}
|
|
1226
|
+
);
|
|
1130
1227
|
const cacheTtl = cacheOpts.ttl ?? DEFAULT_CACHE_TTL;
|
|
1131
1228
|
const cacheStorage = cacheOpts.storage ?? "memory";
|
|
1132
1229
|
const cache = new RouteCache({ storage: cacheStorage, ttl: cacheTtl });
|
|
1133
1230
|
const requestInterceptors = new InterceptorManagerImpl();
|
|
1134
1231
|
const responseInterceptors = new InterceptorManagerImpl();
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
const [onFulfilled, onRejected] = entry;
|
|
1141
|
-
requestInterceptors.use(onFulfilled, onRejected);
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1232
|
+
const reqDecl = normalizeInterceptorDeclaration(
|
|
1233
|
+
declarativeInterceptors?.request
|
|
1234
|
+
);
|
|
1235
|
+
if (reqDecl.onFulfilled || reqDecl.onRejected) {
|
|
1236
|
+
requestInterceptors.use(reqDecl.onFulfilled, reqDecl.onRejected);
|
|
1144
1237
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
const [onFulfilled, onRejected] = entry;
|
|
1151
|
-
responseInterceptors.use(onFulfilled, onRejected);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1238
|
+
const resDecl = normalizeInterceptorDeclaration(
|
|
1239
|
+
declarativeInterceptors?.response
|
|
1240
|
+
);
|
|
1241
|
+
if (resDecl.onFulfilled || resDecl.onRejected) {
|
|
1242
|
+
responseInterceptors.use(resDecl.onFulfilled, resDecl.onRejected);
|
|
1154
1243
|
}
|
|
1155
1244
|
const adapterPromise = resolveAdapter({
|
|
1156
1245
|
adapter,
|
|
@@ -1162,6 +1251,11 @@ function createRouteForge(options = {}) {
|
|
|
1162
1251
|
if (!adapterResolved) {
|
|
1163
1252
|
adapterObj = await adapterPromise.catch((e) => {
|
|
1164
1253
|
if (e instanceof AdapterNotFoundError) throw e;
|
|
1254
|
+
if (warnings) {
|
|
1255
|
+
console.warn(
|
|
1256
|
+
`[route-forge] adapter initialization failed (${e?.message ?? String(e)}); falling back to builtin`
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1165
1259
|
return resolveAdapter({
|
|
1166
1260
|
adapter: "builtin",
|
|
1167
1261
|
forgeInterceptors: { request: requestInterceptors, response: responseInterceptors }
|
|
@@ -1173,10 +1267,7 @@ function createRouteForge(options = {}) {
|
|
|
1173
1267
|
}
|
|
1174
1268
|
function assertDiscoveryReady() {
|
|
1175
1269
|
if (!autoDiscoveryCompleted && !explicitLevels?.length) {
|
|
1176
|
-
throw new
|
|
1177
|
-
"Route data not available. Auto-discovery has not completed. Use forge.ready() or forge.use(level) first.",
|
|
1178
|
-
{ code: "RF_FE_010" }
|
|
1179
|
-
);
|
|
1270
|
+
throw new DiscoveryNotReadyError();
|
|
1180
1271
|
}
|
|
1181
1272
|
}
|
|
1182
1273
|
async function fetchMeta(routeTag, url, level = "") {
|
|
@@ -1201,8 +1292,8 @@ function createRouteForge(options = {}) {
|
|
|
1201
1292
|
const resp = await doRawRequest(config);
|
|
1202
1293
|
if (!resp || resp.status < 200 || resp.status >= 300) {
|
|
1203
1294
|
throw new HTTPError(
|
|
1204
|
-
`Failed to fetch "${routeTag}": HTTP ${resp?.status}`,
|
|
1205
|
-
{ level, status: resp?.status, url, method: "GET" }
|
|
1295
|
+
`Failed to fetch "${routeTag}" (${url}): HTTP ${resp?.status}`,
|
|
1296
|
+
{ level, status: resp?.status, url, method: "GET", response: resp ?? void 0 }
|
|
1206
1297
|
);
|
|
1207
1298
|
}
|
|
1208
1299
|
return resp.data;
|
|
@@ -1239,11 +1330,14 @@ function createRouteForge(options = {}) {
|
|
|
1239
1330
|
function getRoutes(level) {
|
|
1240
1331
|
return level === void 0 ? store.getRoutes() : store.getRoutes(level);
|
|
1241
1332
|
}
|
|
1333
|
+
function getLevels() {
|
|
1334
|
+
return [...discoveryState.levels];
|
|
1335
|
+
}
|
|
1242
1336
|
function route(level, name, params) {
|
|
1243
1337
|
assertDiscoveryReady();
|
|
1244
1338
|
const meta = findRouteMeta(level, name);
|
|
1245
1339
|
if (!meta) {
|
|
1246
|
-
throw new UnknownRouteError(name, level);
|
|
1340
|
+
throw new UnknownRouteError(name, level, Object.keys(store.getRoutes(level)));
|
|
1247
1341
|
}
|
|
1248
1342
|
return buildRequestUrl(meta, params ?? {}, { baseURL, urlPrefix: discoveryState.urlPrefix });
|
|
1249
1343
|
}
|
|
@@ -1253,6 +1347,7 @@ function createRouteForge(options = {}) {
|
|
|
1253
1347
|
responseInterceptors,
|
|
1254
1348
|
load,
|
|
1255
1349
|
findRouteMeta,
|
|
1350
|
+
getRouteNames: (lvl) => Object.keys(store.getRoutes(lvl)),
|
|
1256
1351
|
baseURL,
|
|
1257
1352
|
state: discoveryState,
|
|
1258
1353
|
timeout,
|
|
@@ -1308,7 +1403,10 @@ function createRouteForge(options = {}) {
|
|
|
1308
1403
|
isLoaded,
|
|
1309
1404
|
hasRoute,
|
|
1310
1405
|
getRoutes,
|
|
1406
|
+
getLevels,
|
|
1407
|
+
warnings,
|
|
1311
1408
|
isLoading: () => loadingTracker.isLoading(),
|
|
1409
|
+
isReady: () => readySettledOk,
|
|
1312
1410
|
onLoadingChange: (cb) => loadingTracker.subscribe(cb),
|
|
1313
1411
|
interceptors: {
|
|
1314
1412
|
request: requestInterceptors,
|
|
@@ -1324,10 +1422,12 @@ function createRouteForge(options = {}) {
|
|
|
1324
1422
|
}
|
|
1325
1423
|
|
|
1326
1424
|
exports.AdapterNotFoundError = AdapterNotFoundError;
|
|
1425
|
+
exports.DiscoveryNotReadyError = DiscoveryNotReadyError;
|
|
1327
1426
|
exports.ForgeError = ForgeError;
|
|
1328
1427
|
exports.HTTPError = HTTPError;
|
|
1329
1428
|
exports.InterceptorManagerImpl = InterceptorManagerImpl;
|
|
1330
1429
|
exports.InvalidInterceptorReturnError = InvalidInterceptorReturnError;
|
|
1430
|
+
exports.InvalidPathParamError = InvalidPathParamError;
|
|
1331
1431
|
exports.LoadingTracker = LoadingTracker;
|
|
1332
1432
|
exports.MissingRouteParamError = MissingRouteParamError;
|
|
1333
1433
|
exports.NetworkError = NetworkError;
|