@route-forge/core 2.1.0 → 2.2.1
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 +282 -210
- package/README_zh.md +408 -0
- package/dist/codegen.cjs +2 -26
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.d.cts +2 -9
- package/dist/codegen.d.ts +2 -9
- package/dist/codegen.js +2 -26
- package/dist/codegen.js.map +1 -1
- package/dist/index.cjs +101 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +101 -48
- package/dist/index.js.map +1 -1
- package/dist/route-forge.global.js +101 -48
- package/dist/route-forge.global.js.map +1 -1
- package/dist/route-forge.global.min.js +2 -2
- package/dist/{types-C51rXaiY.d.cts → types-CDKE8rw-.d.cts} +41 -22
- package/dist/{types-C51rXaiY.d.ts → types-CDKE8rw-.d.ts} +41 -22
- package/package.json +20 -3
|
@@ -64,12 +64,28 @@ var RouteForge = (function (exports) {
|
|
|
64
64
|
}
|
|
65
65
|
return entry;
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
/**
|
|
68
|
+
* 写入某层级缓存。后端 TTL 唯一来源为摘要 config.cache_ttl(全局),经参数 backendTtl 传入:
|
|
69
|
+
* - null → 后端声明"不缓存":不写 storage,仅留内存镜像供 route() 同步读(load 层"每次重取"由调用方跳过 get 短路实现)
|
|
70
|
+
* - 0 → 永久缓存(内存 + storage)
|
|
71
|
+
* - undefined → 后端未下发该字段:用前端兜底 TTL(options.cache.ttl)
|
|
72
|
+
* - 正整数 → min(后端, 前端兜底):后端为上限,前端只能缩短不能延长(SPEC §5.3)
|
|
73
|
+
*/
|
|
74
|
+
set(resp, backendTtl) {
|
|
68
75
|
let ttl;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
let persist;
|
|
77
|
+
if (backendTtl === null) {
|
|
78
|
+
ttl = 0;
|
|
79
|
+
persist = false;
|
|
80
|
+
} else if (backendTtl === void 0) {
|
|
72
81
|
ttl = this.fallbackTtl;
|
|
82
|
+
persist = true;
|
|
83
|
+
} else if (backendTtl === 0) {
|
|
84
|
+
ttl = 0;
|
|
85
|
+
persist = true;
|
|
86
|
+
} else {
|
|
87
|
+
ttl = Math.min(backendTtl, this.fallbackTtl);
|
|
88
|
+
persist = true;
|
|
73
89
|
}
|
|
74
90
|
const entry = {
|
|
75
91
|
level: resp.level,
|
|
@@ -78,7 +94,7 @@ var RouteForge = (function (exports) {
|
|
|
78
94
|
cachedAt: Date.now()
|
|
79
95
|
};
|
|
80
96
|
this.memory.set(resp.level, entry);
|
|
81
|
-
if (this.storage === "memory" || !this.backend) {
|
|
97
|
+
if (!persist || this.storage === "memory" || !this.backend) {
|
|
82
98
|
return;
|
|
83
99
|
}
|
|
84
100
|
try {
|
|
@@ -658,9 +674,11 @@ var RouteForge = (function (exports) {
|
|
|
658
674
|
}
|
|
659
675
|
|
|
660
676
|
// src/auto-discovery.ts
|
|
661
|
-
var UNASSIGNED_LEVEL = "unassigned";
|
|
662
677
|
async function fetchSummary(inputs, baseURL, fetchMeta) {
|
|
663
678
|
const { explicitLevels, explicitEndpoint } = inputs;
|
|
679
|
+
if (!explicitEndpoint) {
|
|
680
|
+
throw new UnknownLevelError("(auto-discovery)");
|
|
681
|
+
}
|
|
664
682
|
try {
|
|
665
683
|
const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
|
|
666
684
|
const ep = explicitEndpoint.startsWith("/") ? explicitEndpoint : `/${explicitEndpoint}`;
|
|
@@ -676,35 +694,42 @@ var RouteForge = (function (exports) {
|
|
|
676
694
|
throw new UnknownLevelError("(auto-discovery)");
|
|
677
695
|
}
|
|
678
696
|
}
|
|
697
|
+
function normalizeCacheTtl(raw) {
|
|
698
|
+
if (typeof raw === "number" && raw < 0) return null;
|
|
699
|
+
return raw;
|
|
700
|
+
}
|
|
679
701
|
function applySummaryToState(summary, state, inputs) {
|
|
680
702
|
const { explicitLevels, explicitEager, explicitEndpoint } = inputs;
|
|
681
|
-
const
|
|
682
|
-
if (
|
|
703
|
+
const schemeVersion = summary.schemeVersion ?? 1;
|
|
704
|
+
if (schemeVersion > 1) {
|
|
683
705
|
console.warn(
|
|
684
|
-
`[route-forge] backend
|
|
706
|
+
`[route-forge] backend schemeVersion=${schemeVersion} > client supported 1; some features may be unavailable`
|
|
685
707
|
);
|
|
686
708
|
}
|
|
687
709
|
if (summary.config.endpoint_prefix && summary.config.endpoint_prefix !== explicitEndpoint) {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
710
|
+
if (explicitEndpoint) {
|
|
711
|
+
console.warn(
|
|
712
|
+
`[route-forge] backend endpoint_prefix "${summary.config.endpoint_prefix}" overrides frontend endpoint "${explicitEndpoint}"`
|
|
713
|
+
);
|
|
714
|
+
}
|
|
691
715
|
state.endpoint = summary.config.endpoint_prefix;
|
|
692
716
|
}
|
|
693
717
|
if (summary.config.url_prefix) {
|
|
694
718
|
state.urlPrefix = summary.config.url_prefix.endsWith("/") ? summary.config.url_prefix.slice(0, -1) : summary.config.url_prefix;
|
|
695
719
|
}
|
|
720
|
+
state.cacheTtl = normalizeCacheTtl(summary.config.cache_ttl);
|
|
696
721
|
const backendLevels = Object.keys(summary.levels);
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
availableLevels.push(UNASSIGNED_LEVEL);
|
|
722
|
+
const levelRoutes = {};
|
|
723
|
+
for (const lvl of backendLevels) {
|
|
724
|
+
const r = summary.levels[lvl]?.route;
|
|
725
|
+
if (r && typeof r.uri === "string") {
|
|
726
|
+
levelRoutes[lvl] = { uri: r.uri, methods: r.methods ?? ["GET", "HEAD"] };
|
|
727
|
+
}
|
|
704
728
|
}
|
|
729
|
+
state.levelRoutes = levelRoutes;
|
|
705
730
|
if (explicitLevels && explicitLevels.length > 0) {
|
|
706
|
-
const intersection = explicitLevels.filter((l) =>
|
|
707
|
-
const removed = explicitLevels.filter((l) => !
|
|
731
|
+
const intersection = explicitLevels.filter((l) => backendLevels.includes(l));
|
|
732
|
+
const removed = explicitLevels.filter((l) => !backendLevels.includes(l));
|
|
708
733
|
if (removed.length > 0) {
|
|
709
734
|
console.warn(
|
|
710
735
|
`[route-forge] levels not in backend summary and dropped: ${removed.join(", ")}`
|
|
@@ -712,7 +737,7 @@ var RouteForge = (function (exports) {
|
|
|
712
737
|
}
|
|
713
738
|
state.levels = intersection;
|
|
714
739
|
} else {
|
|
715
|
-
state.levels =
|
|
740
|
+
state.levels = backendLevels.slice();
|
|
716
741
|
}
|
|
717
742
|
const backendEager = backendLevels.filter((lvl) => summary.levels[lvl]?.load === "eager");
|
|
718
743
|
if (!explicitEager) {
|
|
@@ -723,6 +748,25 @@ var RouteForge = (function (exports) {
|
|
|
723
748
|
}
|
|
724
749
|
}
|
|
725
750
|
|
|
751
|
+
// src/embedded-summary.ts
|
|
752
|
+
var EMBEDDED_GLOBAL_KEY = "__ROUTE_FORGE__";
|
|
753
|
+
var memo;
|
|
754
|
+
function readEmbeddedSummary() {
|
|
755
|
+
if (memo !== void 0) return memo;
|
|
756
|
+
if (typeof window === "undefined") {
|
|
757
|
+
memo = null;
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
let raw;
|
|
761
|
+
try {
|
|
762
|
+
raw = window[EMBEDDED_GLOBAL_KEY];
|
|
763
|
+
} catch {
|
|
764
|
+
raw = void 0;
|
|
765
|
+
}
|
|
766
|
+
memo = raw !== null && typeof raw === "object" ? raw : null;
|
|
767
|
+
return memo;
|
|
768
|
+
}
|
|
769
|
+
|
|
726
770
|
// src/route-store.ts
|
|
727
771
|
var RouteStore = class {
|
|
728
772
|
constructor(deps) {
|
|
@@ -742,33 +786,30 @@ var RouteForge = (function (exports) {
|
|
|
742
786
|
}
|
|
743
787
|
}
|
|
744
788
|
async fetchLevel(level) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
789
|
+
const uri = this.state.levelRoutes[level]?.uri;
|
|
790
|
+
const url = uri ? this.joinBaseAndPath(this.baseURL, uri) : buildUrl(level, { baseURL: this.baseURL, endpoint: this.state.endpoint });
|
|
791
|
+
return await this.fetchMeta(`route-forge.${level}`, url, level);
|
|
792
|
+
}
|
|
793
|
+
/** baseURL 与后端下发的绝对 path 拼接(规范化斜杠),与 buildUrl 的 base 处理一致 */
|
|
794
|
+
joinBaseAndPath(baseURL, path) {
|
|
795
|
+
const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
|
|
796
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
797
|
+
return `${base}${p}`;
|
|
750
798
|
}
|
|
751
799
|
async loadOne(level) {
|
|
752
800
|
const autoDiscoveryError = this.getAutoDiscoveryError();
|
|
753
801
|
if (autoDiscoveryError) throw autoDiscoveryError;
|
|
754
802
|
this.assertLevelDeclared(level);
|
|
755
|
-
|
|
803
|
+
const noCache = this.state.cacheTtl === null;
|
|
804
|
+
if (!noCache && this.cache.get(level)) return;
|
|
756
805
|
const existing = this.inflight.get(level);
|
|
757
806
|
if (existing) return existing;
|
|
758
807
|
const gen = this.invalidationGens.get(level) ?? 0;
|
|
759
808
|
const p = (async () => {
|
|
760
809
|
try {
|
|
761
|
-
if (level === UNASSIGNED_LEVEL && !this.state.backendHasUnassignedLevel && this.state.summaryUnassigned) {
|
|
762
|
-
const routes = {};
|
|
763
|
-
for (const r of this.state.summaryUnassigned) {
|
|
764
|
-
routes[r.name] = { ...r, level: UNASSIGNED_LEVEL };
|
|
765
|
-
}
|
|
766
|
-
this.cache.set({ level: UNASSIGNED_LEVEL, routes, cache: null });
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
810
|
const resp = await this.fetchLevel(level);
|
|
770
811
|
if ((this.invalidationGens.get(level) ?? 0) === gen) {
|
|
771
|
-
this.cache.set(resp);
|
|
812
|
+
this.cache.set(resp, this.state.cacheTtl);
|
|
772
813
|
}
|
|
773
814
|
} finally {
|
|
774
815
|
this.inflight.delete(level);
|
|
@@ -1051,8 +1092,13 @@ var RouteForge = (function (exports) {
|
|
|
1051
1092
|
// src/forge.ts
|
|
1052
1093
|
var DEFAULT_TIMEOUT = 3e4;
|
|
1053
1094
|
var DEFAULT_CACHE_TTL = 3600;
|
|
1054
|
-
function createRouteForge(options) {
|
|
1055
|
-
|
|
1095
|
+
function createRouteForge(options = {}) {
|
|
1096
|
+
const bootstrapSummary = readEmbeddedSummary() ?? options.summary ?? null;
|
|
1097
|
+
if (!bootstrapSummary && !options.endpoint) {
|
|
1098
|
+
throw new TypeError(
|
|
1099
|
+
"createRouteForge: \u9700\u8981 options.endpoint\uFF0C\u6216 options.summary\uFF0C\u6216\u9875\u9762\u5185\u5D4C window.__ROUTE_FORGE__"
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1056
1102
|
const {
|
|
1057
1103
|
adapter = "auto",
|
|
1058
1104
|
timeout = DEFAULT_TIMEOUT,
|
|
@@ -1067,10 +1113,10 @@ var RouteForge = (function (exports) {
|
|
|
1067
1113
|
const discoveryState = {
|
|
1068
1114
|
levels: explicitLevels ?? [],
|
|
1069
1115
|
eager: explicitEager ?? [],
|
|
1070
|
-
endpoint: explicitEndpoint,
|
|
1116
|
+
endpoint: explicitEndpoint ?? bootstrapSummary?.config?.endpoint_prefix ?? "",
|
|
1071
1117
|
urlPrefix: "",
|
|
1072
|
-
|
|
1073
|
-
|
|
1118
|
+
cacheTtl: void 0,
|
|
1119
|
+
levelRoutes: {}
|
|
1074
1120
|
};
|
|
1075
1121
|
const discoveryInputs = { explicitLevels, explicitEager, explicitEndpoint };
|
|
1076
1122
|
let autoDiscoveryCompleted = false;
|
|
@@ -1162,12 +1208,19 @@ var RouteForge = (function (exports) {
|
|
|
1162
1208
|
}
|
|
1163
1209
|
return resp.data;
|
|
1164
1210
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1211
|
+
let autoDiscoveryPromise;
|
|
1212
|
+
if (bootstrapSummary) {
|
|
1213
|
+
applySummaryToState(bootstrapSummary, discoveryState, discoveryInputs);
|
|
1214
|
+
autoDiscoveryCompleted = true;
|
|
1215
|
+
autoDiscoveryPromise = Promise.resolve();
|
|
1216
|
+
} else {
|
|
1217
|
+
autoDiscoveryPromise = fetchSummary(discoveryInputs, baseURL, fetchMeta).then((summary) => {
|
|
1218
|
+
if (summary === null) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
applySummaryToState(summary, discoveryState, discoveryInputs);
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1171
1224
|
let autoDiscoveryError = null;
|
|
1172
1225
|
autoDiscoveryPromise.catch((e) => {
|
|
1173
1226
|
autoDiscoveryError = e;
|