@route-forge/core 1.2.2 → 1.3.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 +53 -10
- package/dist/codegen.d.cts +1 -1
- package/dist/codegen.d.ts +1 -1
- package/dist/index.cjs +106 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +106 -35
- package/dist/index.js.map +1 -1
- package/dist/route-forge.global.js +106 -35
- package/dist/route-forge.global.js.map +1 -1
- package/dist/route-forge.global.min.js +2 -2
- package/dist/{types-BcaNLA_l.d.cts → types-D4PMbR4-.d.cts} +43 -51
- package/dist/{types-BcaNLA_l.d.ts → types-D4PMbR4-.d.ts} +43 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -210,26 +210,69 @@ const forge = createRouteForge({
|
|
|
210
210
|
})
|
|
211
211
|
```
|
|
212
212
|
|
|
213
|
-
### `forge.ready`
|
|
213
|
+
### `forge.ready()` 方法
|
|
214
214
|
|
|
215
|
-
auto-discovery + eager load 完成后 resolve
|
|
215
|
+
auto-discovery + eager load 完成后 resolve,返回 `Promise<RouteForge>`(resolve 值为 forge 自身),适合 async/await 风格:
|
|
216
216
|
|
|
217
217
|
```ts
|
|
218
218
|
const forge = createRouteForge({ endpoint: '/_forge/routes' })
|
|
219
|
-
|
|
219
|
+
|
|
220
|
+
// 无参模式:直接 await
|
|
221
|
+
await forge.ready()
|
|
220
222
|
// 路由数据已就绪,可安全调用 route() / hasRoute()
|
|
223
|
+
|
|
224
|
+
// 回调模式:onFulfilled / onRejected
|
|
225
|
+
forge.ready(
|
|
226
|
+
(f) => { console.log('ready!', f) },
|
|
227
|
+
(err) => { console.error(err) }
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
// 链式调用:ready 返回 forge 自身
|
|
231
|
+
const bound = await forge.ready().then(f => f.use('admin'))
|
|
221
232
|
```
|
|
222
233
|
|
|
223
|
-
### `
|
|
234
|
+
### `forge.use(level?, prefix?)` 层级绑定
|
|
224
235
|
|
|
225
|
-
|
|
236
|
+
`forge.use()` 是 core 层唯一的 level 绑定入口,返回 `BoundForge` 对象:
|
|
226
237
|
|
|
227
238
|
```ts
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
})
|
|
231
|
-
//
|
|
232
|
-
|
|
239
|
+
// 绑定层级 — 自动触发 load,提供快捷方法
|
|
240
|
+
const bound = forge.use('admin')
|
|
241
|
+
bound('users.show', { user: 1 }) // 可直接调用(= bound.api())
|
|
242
|
+
bound.route('users.show') // URL 生成
|
|
243
|
+
bound.level // → 'admin'
|
|
244
|
+
bound.levelLoaded // Promise<void>
|
|
245
|
+
|
|
246
|
+
// 绑定层级 + 前缀 — 路由名自动拼接
|
|
247
|
+
const bound = forge.use('admin', 'users')
|
|
248
|
+
bound('show', { user: 1 }) // → forge.api('admin', 'users.show', ...)
|
|
249
|
+
|
|
250
|
+
// BoundForge 独有方法
|
|
251
|
+
await bound.onLevelLoaded() // 等待 level 加载完成
|
|
252
|
+
const prefixed = bound.useRoutePrefix('posts') // 追加前缀
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### IIFE 浏览器用法
|
|
256
|
+
|
|
257
|
+
通过 `<script>` 标签引入后,`RouteForge` 全局可用:
|
|
258
|
+
|
|
259
|
+
```html
|
|
260
|
+
<script src="https://unpkg.com/@route-forge/core"></script>
|
|
261
|
+
<script>
|
|
262
|
+
const forge = RouteForge.createRouteForge({
|
|
263
|
+
endpoint: '/_forge/routes',
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
// 等待就绪后绑定层级
|
|
267
|
+
forge.ready().then(function(f) {
|
|
268
|
+
const admin = f.use('admin')
|
|
269
|
+
return admin.onLevelLoaded()
|
|
270
|
+
}).then(function(bound) {
|
|
271
|
+
return bound('users.show', { user: 1 })
|
|
272
|
+
}).then(function(data) {
|
|
273
|
+
console.log(data)
|
|
274
|
+
})
|
|
275
|
+
</script>
|
|
233
276
|
```
|
|
234
277
|
|
|
235
278
|
### Auto-discovery 守卫
|
package/dist/codegen.d.cts
CHANGED
package/dist/codegen.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -467,6 +467,39 @@ async function resolveAdapter(opts) {
|
|
|
467
467
|
return createBuiltinHttp(opts.forgeInterceptors);
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
// src/resolveRouteName.ts
|
|
471
|
+
async function resolveRouteName(forge, level, prefix, suffix, separator = ".") {
|
|
472
|
+
if (!suffix) return prefix;
|
|
473
|
+
const joined = `${prefix}${separator}${suffix}`;
|
|
474
|
+
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
475
|
+
await forge.load(level);
|
|
476
|
+
if (forge.hasRoute(level, joined)) return joined;
|
|
477
|
+
if (forge.hasRoute(level, suffix)) return suffix;
|
|
478
|
+
throw new UnknownRouteError(joined, level);
|
|
479
|
+
}
|
|
480
|
+
function resolveRouteNameSync(forge, level, prefix, suffix, separator = ".") {
|
|
481
|
+
if (!suffix) return prefix;
|
|
482
|
+
const joined = `${prefix}${separator}${suffix}`;
|
|
483
|
+
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
484
|
+
if (forge.hasRoute(level, joined)) return joined;
|
|
485
|
+
if (forge.hasRoute(level, suffix)) return suffix;
|
|
486
|
+
throw new UnknownRouteError(joined, level);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// src/defineImmutableProps.ts
|
|
490
|
+
function defineImmutableProps(target, props) {
|
|
491
|
+
for (const key of Object.keys(props)) {
|
|
492
|
+
const val = props[key];
|
|
493
|
+
Object.defineProperty(target, key, {
|
|
494
|
+
value: val !== null && typeof val === "object" ? Object.freeze(val) : val,
|
|
495
|
+
writable: false,
|
|
496
|
+
enumerable: false,
|
|
497
|
+
configurable: false
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
return target;
|
|
501
|
+
}
|
|
502
|
+
|
|
470
503
|
// src/loading.ts
|
|
471
504
|
var LoadingTracker = class {
|
|
472
505
|
constructor() {
|
|
@@ -633,7 +666,6 @@ function createRouteForge(options) {
|
|
|
633
666
|
const readyPromise = new Promise((resolve) => {
|
|
634
667
|
resolveReady = resolve;
|
|
635
668
|
});
|
|
636
|
-
const levelLoadedListeners = /* @__PURE__ */ new Map();
|
|
637
669
|
const cacheTtl = cacheOpts.ttl ?? DEFAULT_CACHE_TTL;
|
|
638
670
|
const cacheStorage = cacheOpts.storage ?? "memory";
|
|
639
671
|
const cache = new RouteCache({ storage: cacheStorage, ttl: cacheTtl });
|
|
@@ -685,7 +717,7 @@ function createRouteForge(options) {
|
|
|
685
717
|
function assertDiscoveryReady() {
|
|
686
718
|
if (!autoDiscoveryCompleted && !explicitLevels?.length) {
|
|
687
719
|
throw new ForgeError(
|
|
688
|
-
"Route data not available. Auto-discovery has not completed. Use
|
|
720
|
+
"Route data not available. Auto-discovery has not completed. Use forge.ready() or forge.use(level) first.",
|
|
689
721
|
{ code: "RF_FE_010" }
|
|
690
722
|
);
|
|
691
723
|
}
|
|
@@ -738,15 +770,11 @@ function createRouteForge(options) {
|
|
|
738
770
|
routes[r.name] = { ...r, level: UNASSIGNED_LEVEL };
|
|
739
771
|
}
|
|
740
772
|
cache.set({ level: UNASSIGNED_LEVEL, routes, cache: null });
|
|
741
|
-
const listeners = levelLoadedListeners.get(level);
|
|
742
|
-
if (listeners) listeners.forEach((cb) => cb());
|
|
743
773
|
return;
|
|
744
774
|
}
|
|
745
775
|
const resp = await fetchLevel(level);
|
|
746
776
|
if ((invalidationGens.get(level) ?? 0) === gen) {
|
|
747
777
|
cache.set(resp);
|
|
748
|
-
const listeners = levelLoadedListeners.get(level);
|
|
749
|
-
if (listeners) listeners.forEach((cb) => cb());
|
|
750
778
|
}
|
|
751
779
|
} finally {
|
|
752
780
|
inflight.delete(level);
|
|
@@ -948,6 +976,7 @@ function createRouteForge(options) {
|
|
|
948
976
|
return result;
|
|
949
977
|
}
|
|
950
978
|
void autoDiscoveryPromise.then(() => {
|
|
979
|
+
autoDiscoveryCompleted = true;
|
|
951
980
|
options.onSummaryReady?.();
|
|
952
981
|
if (effectiveEager.length > 0) {
|
|
953
982
|
return Promise.all(effectiveEager.map((lvl) => load(lvl))).catch((e) => {
|
|
@@ -955,11 +984,74 @@ function createRouteForge(options) {
|
|
|
955
984
|
});
|
|
956
985
|
}
|
|
957
986
|
}).then(() => {
|
|
958
|
-
|
|
959
|
-
resolveReady();
|
|
987
|
+
resolveReady(forgeInstance);
|
|
960
988
|
}).catch(() => {
|
|
961
989
|
});
|
|
962
|
-
|
|
990
|
+
function ready(onFulfilled, onRejected) {
|
|
991
|
+
if (onFulfilled) {
|
|
992
|
+
const p = readyPromise.then(onFulfilled, onRejected);
|
|
993
|
+
return p.then(() => forgeInstance);
|
|
994
|
+
}
|
|
995
|
+
return readyPromise;
|
|
996
|
+
}
|
|
997
|
+
const forgeResolver = { load, hasRoute };
|
|
998
|
+
function createBoundForge(level, prefix) {
|
|
999
|
+
const levelLoadedPromise = load(level).catch(() => {
|
|
1000
|
+
});
|
|
1001
|
+
const apiFn = prefix ? (name, params) => resolveRouteName(forgeResolver, level, prefix, name).then(
|
|
1002
|
+
(resolved) => api(level, resolved, params)
|
|
1003
|
+
) : (name, params) => api(level, name, params);
|
|
1004
|
+
const callable = apiFn;
|
|
1005
|
+
defineImmutableProps(callable, {
|
|
1006
|
+
level,
|
|
1007
|
+
...prefix !== void 0 ? { prefix } : {},
|
|
1008
|
+
api: apiFn,
|
|
1009
|
+
route: prefix ? (name, params) => route(level, resolveRouteNameSync(forgeResolver, level, prefix, name), params) : (name, params) => route(level, name, params),
|
|
1010
|
+
url: prefix ? (name, params) => route(level, resolveRouteNameSync(forgeResolver, level, prefix, name), params) : (name, params) => route(level, name, params),
|
|
1011
|
+
hasRoute: (name) => hasRoute(level, name),
|
|
1012
|
+
getRoutes: () => getRoutes(level),
|
|
1013
|
+
load: () => load(level),
|
|
1014
|
+
invalidate: () => invalidate(level),
|
|
1015
|
+
isLoaded: () => isLoaded(level),
|
|
1016
|
+
isLoading: () => loadingTracker.isLoading(),
|
|
1017
|
+
onLoadingChange: (cb) => loadingTracker.subscribe(cb)
|
|
1018
|
+
});
|
|
1019
|
+
Object.defineProperty(callable, "levelLoaded", {
|
|
1020
|
+
value: levelLoadedPromise,
|
|
1021
|
+
writable: false,
|
|
1022
|
+
enumerable: true,
|
|
1023
|
+
configurable: true
|
|
1024
|
+
});
|
|
1025
|
+
return callable;
|
|
1026
|
+
}
|
|
1027
|
+
function boundOnLevelLoaded(bound, levelLoadedPromise, onFulfilled, onRejected) {
|
|
1028
|
+
const p = levelLoadedPromise.then(() => bound);
|
|
1029
|
+
if (onFulfilled) {
|
|
1030
|
+
return p.then(onFulfilled, onRejected).then(() => bound);
|
|
1031
|
+
}
|
|
1032
|
+
return p;
|
|
1033
|
+
}
|
|
1034
|
+
function attachBoundMethods(bound, levelLoadedPromise, level) {
|
|
1035
|
+
Object.defineProperty(bound, "onLevelLoaded", {
|
|
1036
|
+
value: (onFulfilled, onRejected) => boundOnLevelLoaded(bound, levelLoadedPromise, onFulfilled, onRejected),
|
|
1037
|
+
writable: false,
|
|
1038
|
+
enumerable: false,
|
|
1039
|
+
configurable: false
|
|
1040
|
+
});
|
|
1041
|
+
Object.defineProperty(bound, "useRoutePrefix", {
|
|
1042
|
+
value: (prefix) => createBoundForgeWithMethods(level, prefix),
|
|
1043
|
+
writable: false,
|
|
1044
|
+
enumerable: false,
|
|
1045
|
+
configurable: false
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
function createBoundForgeWithMethods(level, prefix) {
|
|
1049
|
+
const bound = createBoundForge(level, prefix);
|
|
1050
|
+
const levelLoadedPromise = bound.levelLoaded;
|
|
1051
|
+
attachBoundMethods(bound, levelLoadedPromise, level);
|
|
1052
|
+
return bound;
|
|
1053
|
+
}
|
|
1054
|
+
const forgeInstance = {
|
|
963
1055
|
api,
|
|
964
1056
|
load,
|
|
965
1057
|
route,
|
|
@@ -974,15 +1066,13 @@ function createRouteForge(options) {
|
|
|
974
1066
|
request: requestInterceptors,
|
|
975
1067
|
response: responseInterceptors
|
|
976
1068
|
},
|
|
977
|
-
ready
|
|
978
|
-
|
|
979
|
-
if (
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
levelLoadedListeners.get(level).add(cb);
|
|
983
|
-
return () => levelLoadedListeners.get(level)?.delete(cb);
|
|
1069
|
+
ready,
|
|
1070
|
+
use(level, prefix) {
|
|
1071
|
+
if (level === void 0) return forgeInstance;
|
|
1072
|
+
return createBoundForgeWithMethods(level, prefix);
|
|
984
1073
|
}
|
|
985
1074
|
};
|
|
1075
|
+
return forgeInstance;
|
|
986
1076
|
}
|
|
987
1077
|
function pickMethod(meta) {
|
|
988
1078
|
const m = meta.methods.find((x) => x.toUpperCase() !== "HEAD");
|
|
@@ -1050,25 +1140,6 @@ function isAbortError2(err, signal) {
|
|
|
1050
1140
|
return false;
|
|
1051
1141
|
}
|
|
1052
1142
|
|
|
1053
|
-
// src/resolveRouteName.ts
|
|
1054
|
-
async function resolveRouteName(forge, level, prefix, suffix, separator = ".") {
|
|
1055
|
-
if (!suffix) return prefix;
|
|
1056
|
-
const joined = `${prefix}${separator}${suffix}`;
|
|
1057
|
-
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
1058
|
-
await forge.load(level);
|
|
1059
|
-
if (forge.hasRoute(level, joined)) return joined;
|
|
1060
|
-
if (forge.hasRoute(level, suffix)) return suffix;
|
|
1061
|
-
throw new UnknownRouteError(joined, level);
|
|
1062
|
-
}
|
|
1063
|
-
function resolveRouteNameSync(forge, level, prefix, suffix, separator = ".") {
|
|
1064
|
-
if (!suffix) return prefix;
|
|
1065
|
-
const joined = `${prefix}${separator}${suffix}`;
|
|
1066
|
-
if (!suffix.startsWith(`${prefix}${separator}`)) return joined;
|
|
1067
|
-
if (forge.hasRoute(level, joined)) return joined;
|
|
1068
|
-
if (forge.hasRoute(level, suffix)) return suffix;
|
|
1069
|
-
throw new UnknownRouteError(joined, level);
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
1143
|
exports.AdapterNotFoundError = AdapterNotFoundError;
|
|
1073
1144
|
exports.ForgeError = ForgeError;
|
|
1074
1145
|
exports.HTTPError = HTTPError;
|