@finesoft/front 0.5.0 → 0.5.2

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.
Files changed (128) hide show
  1. package/README.md +4 -4
  2. package/dist/Outlet.svelte +39 -0
  3. package/dist/Outlet.svelte.d.ts +7 -0
  4. package/dist/browser-DIU6Sxl3.mjs +1237 -0
  5. package/dist/browser-kFMjlLGT.d.mts +262 -0
  6. package/dist/browser.d.mts +7 -2
  7. package/dist/browser.mjs +9 -1
  8. package/dist/controller-types-CgmJ6-le.d.mts +16 -0
  9. package/dist/cookies-Bpf9VayB.d.mts +779 -0
  10. package/dist/fetch-policy-BHT8RtrL.mjs +82 -0
  11. package/dist/host-guard-DDWxLpFL.mjs +222 -0
  12. package/dist/http-B6CJqDyf.d.mts +46 -0
  13. package/dist/http-CaxrMD1A.d.mts +1 -0
  14. package/dist/http-D70PL72H.mjs +257 -0
  15. package/dist/http.d.mts +3 -0
  16. package/dist/http.mjs +2 -0
  17. package/dist/index-node.d.mts +15 -0
  18. package/dist/index-node.mjs +15 -0
  19. package/dist/index.d.mts +48 -698
  20. package/dist/index.mjs +44 -261
  21. package/dist/load-node.d.mts +5 -0
  22. package/dist/load-node.mjs +10 -0
  23. package/dist/load-portable.d.mts +5 -0
  24. package/dist/load-portable.mjs +8 -0
  25. package/dist/lru-map-BKoUAySU.mjs +50 -0
  26. package/dist/messages-CAt2QdGr.mjs +140 -0
  27. package/dist/native-contract-DuR25hYB.d.mts +14 -0
  28. package/dist/native-contract.d.mts +2 -0
  29. package/dist/native-contract.mjs +1 -0
  30. package/dist/node-D9hB4dsz.d.mts +35 -0
  31. package/dist/node.d.mts +2 -0
  32. package/dist/node.mjs +59 -0
  33. package/dist/path-CGFl2w7D.mjs +113 -0
  34. package/dist/path-CXT6xGPO.d.mts +261 -0
  35. package/dist/portable-CaxrMD1A.d.mts +1 -0
  36. package/dist/portable.d.mts +11 -0
  37. package/dist/portable.mjs +12 -0
  38. package/dist/proxy-1SphZ7x7.mjs +436 -0
  39. package/dist/proxy-2dSWO-Xw.d.mts +53 -0
  40. package/dist/public-types-BcJM-AYc.mjs +835 -0
  41. package/dist/react-DhwBRw01.d.mts +16 -0
  42. package/dist/react.d.mts +3 -0
  43. package/dist/react.mjs +29 -0
  44. package/dist/rolldown-runtime-B4iAMlE-.mjs +35 -0
  45. package/dist/secure-fetch-Xlht2jd7.d.mts +30 -0
  46. package/dist/server-controller-proxy-BkVuVWVD.d.mts +38 -0
  47. package/dist/session-DnB4ZC3x.d.mts +1279 -0
  48. package/dist/src-Ftl_0rhu.mjs +28 -0
  49. package/dist/src-qwx7Vw8g.mjs +3807 -0
  50. package/dist/ssr-BEUNDvbj.d.mts +210 -0
  51. package/dist/ssr-C8xnYXoY.mjs +357 -0
  52. package/dist/ssr.d.mts +3 -0
  53. package/dist/ssr.mjs +3 -0
  54. package/dist/svelte-Dr5to3SE.d.mts +16 -0
  55. package/dist/svelte.d.mts +3 -0
  56. package/dist/svelte.mjs +13 -0
  57. package/dist/typegen-C-WeJCtf.d.mts +12 -0
  58. package/dist/typegen-cli.d.mts +1 -0
  59. package/dist/typegen-cli.mjs +11 -0
  60. package/dist/typegen.d.mts +3 -0
  61. package/dist/typegen.mjs +2 -0
  62. package/dist/types-BuaZHRG7.mjs +402 -0
  63. package/dist/undici-CPfL25Hr.mjs +22262 -0
  64. package/dist/vite-Cj4SPA8D.d.mts +277 -0
  65. package/dist/vite.d.mts +4 -0
  66. package/dist/vite.mjs +2354 -0
  67. package/dist/vue-DGmzuKho.d.mts +32 -0
  68. package/dist/vue.d.mts +3 -0
  69. package/dist/vue.mjs +56 -0
  70. package/dist/web.d.mts +6 -0
  71. package/dist/web.mjs +8 -0
  72. package/dist/worker.d.mts +2 -0
  73. package/dist/worker.mjs +2 -0
  74. package/docs/01-getting-started.md +67 -199
  75. package/docs/02-routing-and-controllers.md +163 -241
  76. package/docs/03-middleware.md +10 -212
  77. package/docs/04-rendering-and-hydration.md +6 -333
  78. package/docs/05-i18n.md +7 -237
  79. package/docs/06-http-client.md +20 -263
  80. package/docs/07-di-container.md +23 -257
  81. package/docs/08-observability.md +6 -286
  82. package/docs/09-server-and-deployment.md +59 -219
  83. package/docs/10-features-platform-pwa.md +7 -231
  84. package/docs/11-navigation.md +143 -288
  85. package/docs/12-session-restoration.md +6 -214
  86. package/docs/README.md +7 -7
  87. package/docs/advanced/custom-action-handler.md +29 -229
  88. package/docs/advanced/custom-adapter.md +7 -259
  89. package/docs/advanced/custom-event-recorder.md +7 -312
  90. package/docs/advanced/inline-proxy-codegen.md +6 -185
  91. package/docs/advanced/multi-tenant-scopes.md +10 -323
  92. package/docs/engineering/ci-release-flow.md +35 -222
  93. package/docs/engineering/project-structure.md +34 -277
  94. package/docs/engineering/testing.md +8 -310
  95. package/docs/pitfalls/container-scope-leak.md +2 -214
  96. package/docs/pitfalls/i18n-bundle-size.md +6 -176
  97. package/docs/pitfalls/proxy-binary-payloads.md +8 -12
  98. package/docs/pitfalls/ssr-hydration-mismatch.md +4 -160
  99. package/docs/pitfalls/ssr-vs-csr-globals.md +9 -170
  100. package/docs/zh/01-getting-started.md +67 -199
  101. package/docs/zh/02-routing-and-controllers.md +166 -244
  102. package/docs/zh/03-middleware.md +10 -212
  103. package/docs/zh/04-rendering-and-hydration.md +6 -333
  104. package/docs/zh/05-i18n.md +7 -237
  105. package/docs/zh/06-http-client.md +20 -263
  106. package/docs/zh/07-di-container.md +23 -257
  107. package/docs/zh/08-observability.md +6 -283
  108. package/docs/zh/09-server-and-deployment.md +59 -219
  109. package/docs/zh/10-features-platform-pwa.md +7 -231
  110. package/docs/zh/11-navigation.md +130 -290
  111. package/docs/zh/12-session-restoration.md +6 -214
  112. package/docs/zh/README.md +4 -4
  113. package/docs/zh/advanced/custom-action-handler.md +29 -229
  114. package/docs/zh/advanced/custom-adapter.md +7 -259
  115. package/docs/zh/advanced/custom-event-recorder.md +7 -312
  116. package/docs/zh/advanced/inline-proxy-codegen.md +6 -185
  117. package/docs/zh/advanced/multi-tenant-scopes.md +10 -323
  118. package/docs/zh/engineering/ci-release-flow.md +35 -222
  119. package/docs/zh/engineering/project-structure.md +34 -277
  120. package/docs/zh/engineering/testing.md +8 -310
  121. package/docs/zh/pitfalls/container-scope-leak.md +2 -214
  122. package/docs/zh/pitfalls/i18n-bundle-size.md +6 -176
  123. package/docs/zh/pitfalls/proxy-binary-payloads.md +8 -12
  124. package/docs/zh/pitfalls/ssr-hydration-mismatch.md +4 -160
  125. package/docs/zh/pitfalls/ssr-vs-csr-globals.md +9 -170
  126. package/package.json +118 -20
  127. package/dist/browser-BYZq9Jp7.mjs +0 -2
  128. package/dist/browser-JTs2jqVY.d.mts +0 -2811
@@ -1,2811 +0,0 @@
1
- //#region ../core/src/actions/types.d.ts
2
- /**
3
- * Action 类型定义
4
- *
5
- * FlowAction — SPA 内部导航
6
- * ExternalUrlAction — 打开外部链接
7
- * CompoundAction — 组合多个 Action
8
- */
9
- /** Action Kind 常量 */
10
- declare const ACTION_KINDS: {
11
- FLOW: "flow";
12
- EXTERNAL_URL: "externalUrl";
13
- COMPOUND: "compound";
14
- };
15
- /** FlowAction — SPA 导航 */
16
- interface FlowAction {
17
- kind: typeof ACTION_KINDS.FLOW;
18
- url: string;
19
- /** 展示方式: 默认 push,modal 弹窗 */
20
- presentationContext?: "default" | "modal";
21
- }
22
- /** ExternalUrlAction — 外部链接 */
23
- interface ExternalUrlAction {
24
- kind: typeof ACTION_KINDS.EXTERNAL_URL;
25
- url: string;
26
- }
27
- /** CompoundAction — 组合 Action */
28
- interface CompoundAction {
29
- kind: typeof ACTION_KINDS.COMPOUND;
30
- actions: Action[];
31
- }
32
- /** 所有 Action 的联合类型 */
33
- type Action = FlowAction | ExternalUrlAction | CompoundAction;
34
- declare function isFlowAction(action: Action): action is FlowAction;
35
- declare function isExternalUrlAction(action: Action): action is ExternalUrlAction;
36
- declare function isCompoundAction(action: Action): action is CompoundAction;
37
- declare function makeFlowAction(url: string, presentationContext?: FlowAction["presentationContext"]): FlowAction;
38
- declare function makeExternalUrlAction(url: string): ExternalUrlAction;
39
- //#endregion
40
- //#region ../core/src/actions/dispatcher.d.ts
41
- /** Action 处理器函数类型 */
42
- type ActionHandler<A extends Action = Action> = (action: A) => Promise<void> | void;
43
- declare class ActionDispatcher {
44
- private handlers;
45
- /**
46
- * 注册指定 kind 的 handler。
47
- *
48
- * 重复 kind 时保留第一个注册者并发出警告——这是有意设计:
49
- * framework 内部 handler 先注册,应用层意外覆盖会被记录而非静默生效。
50
- * 如需显式替换,先调用 removeAction(kind)。
51
- */
52
- onAction<A extends Action>(kind: string, handler: ActionHandler<A>): void;
53
- /** 移除指定 kind 的 handler(用于显式覆盖场景) */
54
- removeAction(kind: string): boolean;
55
- /** 执行一个 Action(CompoundAction 递归展开,有深度限制) */
56
- perform(action: Action, _depth?: number): Promise<void>;
57
- }
58
- //#endregion
59
- //#region ../core/src/dependencies/container.d.ts
60
- /**
61
- * Container — 通用的依赖注入容器
62
- */
63
- type Factory<T> = () => T;
64
- declare class Container {
65
- private registrations;
66
- private resolutionStack;
67
- private parent?;
68
- private children;
69
- /** 注册依赖(默认单例) */
70
- register<T>(key: string, factory: Factory<T>, singleton?: boolean): this;
71
- /** 解析依赖 — 当前容器未注册时回退到 parent */
72
- resolve<T>(key: string): T;
73
- /** 检查是否已注册(含 parent) */
74
- has(key: string): boolean;
75
- /**
76
- * 移除当前容器的注册(不影响 parent)。
77
- *
78
- * 用途:在 scope 内显式撤销之前覆写的依赖,避免用 `register(() => null)` 这种
79
- * 反语义的写法。被移除的 key 之后再 resolve 会回退到 parent 容器。
80
- *
81
- * 返回 true 表示当前层确实存在过这个注册并被移除,false 表示未注册(含「只在
82
- * parent 注册」的情况,本方法不向上递归删除 —— scope 不应能影响 parent 状态)。
83
- */
84
- unregister(key: string): boolean;
85
- /**
86
- * 创建子容器(请求级 scope)
87
- *
88
- * 子容器可覆写父容器的依赖(如每请求的 locale、user),
89
- * 未覆写的 key 自动回退到父容器解析。子容器会被父容器跟踪,
90
- * 父容器 dispose 时一并销毁所有未独立 dispose 的子容器。
91
- */
92
- createScope(): Container;
93
- /**
94
- * 销毁容器,清除所有缓存。
95
- *
96
- * - 递归 dispose 所有 createScope() 创建的未 dispose 子容器
97
- * - 自身被 dispose 后从父容器移除引用,允许 GC
98
- * - 重复 dispose 安全(幂等)
99
- */
100
- dispose(): void;
101
- }
102
- //#endregion
103
- //#region ../core/src/intents/types.d.ts
104
- /** Intent — 描述一个用户意图 */
105
- interface Intent<T = unknown> {
106
- /** Intent 标识符(用于匹配 Controller) */
107
- id: string;
108
- /** 意图参数(path/query 经 codec 转换后可能是 number/boolean 等) */
109
- params?: Record<string, unknown>;
110
- /** 预期返回的数据(仅用于类型推断) */
111
- _returnType?: T;
112
- }
113
- /** Intent Controller — 处理特定 intentId 的业务逻辑 */
114
- interface IntentController<T = unknown> {
115
- /** Controller 对应的 Intent ID */
116
- intentId: string;
117
- /** 执行意图,返回页面数据 */
118
- perform(intent: Intent<T>, container: Container): Promise<T> | T;
119
- }
120
- //#endregion
121
- //#region ../core/src/intents/dispatcher.d.ts
122
- declare class IntentDispatcher {
123
- private controllers;
124
- /** 注册一个 IntentController */
125
- register(controller: IntentController): void;
126
- /** 分发 Intent 到对应 Controller */
127
- dispatch<T>(intent: Intent<T>, container: Container): Promise<T>;
128
- /** 检查是否已注册某个 Intent */
129
- has(intentId: string): boolean;
130
- }
131
- //#endregion
132
- //#region ../core/src/http/secure-fetch.d.ts
133
- /**
134
- * secureFetch — `fetch` with SSRF defense baked in.
135
- *
136
- * Wraps an existing fetch implementation and refuses targets that resolve to
137
- * private / loopback / reserved ranges. Use when your controller fetches a
138
- * URL that the user can influence (image proxies, link previews, callbacks).
139
- *
140
- * Defaults to checking IP literals (sync) AND resolving hostnames via DNS
141
- * (Node only; silently skipped in browsers). Pass `allowInternalHosts: true`
142
- * to opt out — for example, when you have a legitimate server-to-server call
143
- * to 10.x or 127.0.0.1 and you trust the URL source.
144
- *
145
- * @example
146
- * ```ts
147
- * import { secureFetch, DEP_KEYS } from "@finesoft/front";
148
- *
149
- * class ShareController extends BaseController<{ next?: string }, Page> {
150
- * async execute(params, container) {
151
- * const baseFetch = container.resolve<typeof globalThis.fetch>(DEP_KEYS.FETCH);
152
- * const fetch = secureFetch(baseFetch);
153
- * const response = await fetch(params.next ?? "https://example.com");
154
- * ...
155
- * }
156
- * }
157
- * ```
158
- */
159
- interface SecureFetchOptions {
160
- /** Opt out of SSRF defense entirely (default false). */
161
- allowInternalHosts?: boolean;
162
- /** DNS-resolve hostnames and check each resolved IP (default true, Node only). */
163
- validateDns?: boolean;
164
- }
165
- /**
166
- * Return a `fetch`-shaped function that refuses requests to private hosts
167
- * before calling through to `baseFetch`.
168
- */
169
- declare function secureFetch(baseFetch: typeof globalThis.fetch, options?: SecureFetchOptions): typeof globalThis.fetch;
170
- //#endregion
171
- //#region ../core/src/logger/types.d.ts
172
- /** 日志级别 */
173
- type Level = "debug" | "info" | "warn" | "error";
174
- /**
175
- * Logger 接口
176
- *
177
- * 所有方法返回空字符串,允许在模板中内联使用而不渲染文本。
178
- */
179
- interface Logger {
180
- debug(...args: unknown[]): string;
181
- info(...args: unknown[]): string;
182
- warn(...args: unknown[]): string;
183
- error(...args: unknown[]): string;
184
- }
185
- interface LoggerFactory {
186
- loggerFor(category: string): Logger;
187
- }
188
- //#endregion
189
- //#region ../core/src/logger/base.d.ts
190
- declare abstract class BaseLogger implements Logger {
191
- protected category: string;
192
- constructor(category: string);
193
- abstract debug(...args: unknown[]): string;
194
- abstract info(...args: unknown[]): string;
195
- abstract warn(...args: unknown[]): string;
196
- abstract error(...args: unknown[]): string;
197
- }
198
- //#endregion
199
- //#region ../core/src/logger/reporting.d.ts
200
- /** 日志上报回调 */
201
- interface ReportCallback {
202
- (level: Level, category: string, args: unknown[]): void;
203
- }
204
- /** 配置 */
205
- interface ReportingLoggerOptions {
206
- /** 最低上报级别(默认 "warn") */
207
- minLevel?: Level;
208
- /** 上报回调 */
209
- report: ReportCallback;
210
- }
211
- declare class ReportingLogger extends BaseLogger {
212
- private readonly minPriority;
213
- private readonly report;
214
- constructor(category: string, options: ReportingLoggerOptions);
215
- debug(...args: unknown[]): string;
216
- info(...args: unknown[]): string;
217
- warn(...args: unknown[]): string;
218
- error(...args: unknown[]): string;
219
- private maybeReport;
220
- }
221
- declare class ReportingLoggerFactory implements LoggerFactory {
222
- private readonly options;
223
- constructor(options: ReportingLoggerOptions);
224
- loggerFor(category: string): Logger;
225
- }
226
- //#endregion
227
- //#region ../core/src/metrics/types.d.ts
228
- /**
229
- * Metrics — 类型定义
230
- *
231
- * 框架级埋点基础设施的核心接口。
232
- */
233
- /** 事件记录器 — 所有 metrics 后端实现此接口 */
234
- interface EventRecorder {
235
- /** 记录一条事件 */
236
- record(type: string, fields?: Record<string, unknown>): void;
237
- /** 刷新待发送的事件队列 */
238
- flush?(): Promise<void>;
239
- /** 销毁记录器,释放资源 */
240
- destroy?(): void;
241
- }
242
- /** 字段提供者 — 每次记录前自动注入公共字段 */
243
- interface MetricsFieldsProvider {
244
- /** 返回需要附加到每条事件的字段 */
245
- getFields(): Record<string, unknown>;
246
- }
247
- /** Impression 条目 */
248
- interface ImpressionEntry {
249
- /** 被追踪元素的唯一标识 */
250
- id: string;
251
- /** 元素进入视口的时间戳 */
252
- timestamp: number;
253
- /** 附加数据 */
254
- metadata?: Record<string, unknown>;
255
- }
256
- /** Impression 观察器 — 追踪元素可见性 */
257
- interface ImpressionObserver {
258
- /** 开始追踪一个元素 */
259
- observe(element: Element, id: string, metadata?: Record<string, unknown>): void;
260
- /** 停止追踪一个元素 */
261
- unobserve(element: Element): void;
262
- /** 获取已捕获的曝光并清空 */
263
- consume(): ImpressionEntry[];
264
- /** 销毁观察器 */
265
- destroy(): void;
266
- }
267
- //#endregion
268
- //#region ../core/src/utils/platform.d.ts
269
- /**
270
- * Platform — UA 解析与平台检测
271
- *
272
- * 提供统一的平台/浏览器/OS 检测,避免在各处手写 UA 判断。
273
- */
274
- interface PlatformInfo {
275
- /** 操作系统 */
276
- os: "ios" | "android" | "macos" | "windows" | "linux" | "unknown";
277
- /** 浏览器 */
278
- browser: "safari" | "chrome" | "firefox" | "edge" | "opera" | "samsung" | "unknown";
279
- /** 渲染引擎 */
280
- engine: "webkit" | "blink" | "gecko" | "unknown";
281
- /** 是否为移动设备 */
282
- isMobile: boolean;
283
- /** 是否为触摸设备 */
284
- isTouch: boolean;
285
- }
286
- /**
287
- * 从 User-Agent 字符串解析平台信息
288
- *
289
- * @param ua - User-Agent 字符串(默认取 navigator.userAgent)
290
- */
291
- declare function detectPlatform(ua?: string): PlatformInfo;
292
- //#endregion
293
- //#region ../core/src/i18n/messages.d.ts
294
- /**
295
- * i18n message helpers shared by SSR and browser startup.
296
- */
297
- /** Flat translation table: key -> localized text */
298
- type FlatMessages = Record<string, string>;
299
- /** Nested translation value: plain text or pluralized text map */
300
- type NestedMessageValue = string | Record<string, string>;
301
- /** Locale-grouped translation table */
302
- type LocaleMessages = Record<string, Record<string, NestedMessageValue>>;
303
- /**
304
- * Translation message formats supported by the framework:
305
- * - flat messages for a single locale
306
- * - locale-grouped messages with optional plural subkeys
307
- */
308
- type TranslationMessages = FlatMessages | LocaleMessages;
309
- interface MessagesLoaderContext {
310
- readonly runtime: "server" | "browser";
311
- readonly fetch: typeof globalThis.fetch;
312
- readonly url: string;
313
- readonly request?: Request;
314
- }
315
- type MessagesLoader = (locale: string, context: MessagesLoaderContext) => TranslationMessages | Promise<TranslationMessages | undefined> | undefined;
316
- interface ResolveConfiguredMessagesOptions {
317
- locale?: string;
318
- loadMessages?: MessagesLoader;
319
- context?: MessagesLoaderContext;
320
- }
321
- /**
322
- * Resolve the effective translation source for a locale.
323
- */
324
- declare function resolveConfiguredMessages(options: ResolveConfiguredMessagesOptions): Promise<TranslationMessages | undefined>;
325
- /**
326
- * Resolve `TranslationMessages` into the flat map consumed by
327
- * `SimpleTranslator`.
328
- */
329
- declare function resolveMessages(messages: TranslationMessages, locale: string): Record<string, string> | undefined;
330
- //#endregion
331
- //#region ../core/src/dependencies/make-dependencies.d.ts
332
- /** 网络请求层 */
333
- interface Net {
334
- fetch(url: string, options?: RequestInit): Promise<Response>;
335
- }
336
- /** 存储接口 */
337
- interface Storage {
338
- get(key: string): string | undefined;
339
- set(key: string, value: string): void;
340
- delete(key: string): void;
341
- }
342
- /** Feature Flags */
343
- interface FeatureFlags {
344
- isEnabled(key: string): boolean;
345
- getString(key: string): string | undefined;
346
- getNumber(key: string): number | undefined;
347
- }
348
- /** Feature Flags Provider — 用于从远程/外部源加载 flags */
349
- interface FeatureFlagsProvider {
350
- isEnabled(key: string): boolean;
351
- getString?(key: string): string | undefined;
352
- getNumber?(key: string): number | undefined;
353
- }
354
- /** Metrics 记录器 */
355
- interface MetricsRecorder {
356
- /** 记录一条事件(通用方法) */
357
- record(type: string, fields?: Record<string, unknown>): void;
358
- /** 记录页面访问(便捷方法) */
359
- recordPageView(page: string, fields?: Record<string, unknown>): void;
360
- /** 记录自定义事件(便捷方法) */
361
- recordEvent(name: string, fields?: Record<string, unknown>): void;
362
- /** 刷新待发送队列 */
363
- flush?(): Promise<void>;
364
- /** 销毁记录器 */
365
- destroy?(): void;
366
- }
367
- declare const DEP_KEYS: {
368
- readonly LOGGER: "logger";
369
- readonly LOGGER_FACTORY: "loggerFactory";
370
- readonly NET: "net";
371
- readonly STORAGE: "storage";
372
- readonly FEATURE_FLAGS: "featureFlags";
373
- readonly METRICS: "metrics";
374
- readonly FETCH: "fetch";
375
- /**
376
- * `fetch` 包了 SSRF 防护(拒绝 private / loopback / 保留 IP + DNS resolve 后逐 IP 校验)。
377
- * 当 controller 用用户可控的 URL 发起请求(图片代理、链接预览、回调等),
378
- * 优先 resolve 这个 key 而不是 `FETCH`。要 opt-out 可手动调 `secureFetch(baseFetch, { allowInternalHosts: true })`。
379
- */
380
- readonly SAFE_FETCH: "safeFetch";
381
- readonly EVENT_RECORDER: "eventRecorder";
382
- readonly LOCALE: "locale";
383
- readonly PLATFORM: "platform";
384
- readonly TRANSLATOR: "translator";
385
- };
386
- interface MakeDependenciesOptions {
387
- fetch?: typeof globalThis.fetch;
388
- featureFlags?: Record<string, boolean | string | number>;
389
- /** 外部 feature flags providers(远程配置、A/B 测试等) */
390
- featureFlagsProviders?: FeatureFlagsProvider[];
391
- /** 日志上报回调 — 提供后自动组合 ReportingLoggerFactory */
392
- reportCallback?: ReportCallback;
393
- /** 自定义 EventRecorder(默认 ConsoleEventRecorder) */
394
- eventRecorder?: EventRecorder;
395
- /** 语言代码(如 "zh-Hans"、"en-US"),用于注入 locale 信息 */
396
- locale?: string;
397
- /** 自定义 PlatformInfo(默认通过 UA 自动检测) */
398
- platform?: PlatformInfo;
399
- /**
400
- * 覆盖 `DEP_KEYS.SAFE_FETCH` 默认行为。比如服务正常需要打内网,可以传
401
- * `{ allowInternalHosts: true }` 整体放行;或自定义 DNS 校验策略。
402
- */
403
- safeFetch?: SecureFetchOptions;
404
- }
405
- declare function makeDependencies(container: Container, options?: MakeDependenciesOptions): void;
406
- //#endregion
407
- //#region ../core/src/dependencies/request-scoped-key.d.ts
408
- /** Anything that exposes the per-request DI container — covers NavigationContext, PostLoadContext, and raw Container. */
409
- type ContainerHolder = Container | {
410
- container: Container;
411
- };
412
- interface RequestScopedKey<T> {
413
- /** The underlying string key registered with the Container. */
414
- readonly key: string;
415
- /** Write the current request's value. Overwrites any prior registration. */
416
- set(target: ContainerHolder, value: T): void;
417
- /** Read the current request's value, or `undefined` if not set. */
418
- get(target: ContainerHolder): T | undefined;
419
- /**
420
- * Remove the registration from this request's container. Subsequent `get`
421
- * calls return `undefined`. Returns true if the key existed.
422
- */
423
- clear(target: ContainerHolder): boolean;
424
- }
425
- /**
426
- * Define a typed, request-scoped DI key.
427
- *
428
- * The returned object's `set`/`get`/`clear` work against the container belonging
429
- * to the request you hand in — typically `ctx.container` from a middleware or
430
- * the `container` argument inside `BaseController.execute`.
431
- */
432
- declare function defineRequestScopedKey<T>(key: string): RequestScopedKey<T>;
433
- //#endregion
434
- //#region ../core/src/models/page.d.ts
435
- /**
436
- * BasePage — 所有页面共享的基础属性
437
- *
438
- * 具体页面类型由应用层定义并扩展此接口。
439
- *
440
- * SSR prefetch 数据序列化时的可见性由 `FINESOFT_PUBLIC` symbol 控制 —— 见
441
- * `markPublic` / `isPublicMarked`。没有 marker 时 `serializeServerData` 仍按全字段
442
- * 序列化(向后兼容),但会在 dev 启动后打印一次告警,下一个 major 会改为只序列化
443
- * `BasePage` 的标准字段(id/pageType/title/description/url)。
444
- */
445
- interface BasePage {
446
- id: string;
447
- pageType: string;
448
- title: string;
449
- description?: string;
450
- url?: string;
451
- }
452
- /**
453
- * 显式声明 page 对象里哪些字段可以跨 SSR/CSR 边界进入 HTML。未列出的字段在
454
- * `serializeServerData` 时会被剥除,杜绝整 page 对象(含 apiToken、内部备注等)
455
- * 被无意中 hydrate 到客户端的失误。
456
- *
457
- * 用 symbol 而非普通字段名:避免和应用自己的字段冲突,且 `JSON.stringify` 自动
458
- * 忽略 symbol key,所以 marker 永远不会出现在序列化输出里。
459
- */
460
- declare const FINESOFT_PUBLIC: unique symbol;
461
- /** 标准 BasePage 字段 —— marker 缺失但显式标注时使用。 */
462
- declare const BASE_PAGE_FIELDS: readonly ["id", "pageType", "title", "description", "url"];
463
- /**
464
- * 把 `publicFields` 列表写到 page 上,供 `serializeServerData` 读取。
465
- * 推荐做法是不要直接渲染原始 page 对象,而是经过这个函数显式声明 contract:
466
- *
467
- * ```ts
468
- * return markPublic(
469
- * {
470
- * id: "profile",
471
- * pageType: "profile",
472
- * title: user.name,
473
- * email: user.email,
474
- * apiToken: user.apiToken, // 仍在对象上,给服务端别处用
475
- * },
476
- * ["id", "pageType", "title", "email"], // 但 prefetch 只 serialize 这些
477
- * );
478
- * ```
479
- *
480
- * 也可以传 `true` 表示「所有字段都安全」—— 当作 opt-out,等价于不调用本函数。
481
- */
482
- declare function markPublic<P extends BasePage>(page: P, publicFields: readonly (keyof P)[] | true): P;
483
- /** True 如果 page 用 `markPublic` 显式标过。 */
484
- declare function isPublicMarked(page: unknown): boolean;
485
- /** 取出 `markPublic` 写入的字段白名单;`true` 表示全开放,`null` 表示未标注。 */
486
- declare function getPublicFields(page: unknown): readonly string[] | true | null;
487
- //#endregion
488
- //#region ../core/src/middleware/types.d.ts
489
- /** 导航上下文(beforeLoad 阶段可用) */
490
- interface NavigationContext {
491
- /** 完整 URL(path + query) */
492
- readonly url: string;
493
- /** 仅路径部分 */
494
- readonly path: string;
495
- /** 路由参数 + 查询参数(codec 转换后可能是 number/boolean 等) */
496
- readonly params: Record<string, unknown>;
497
- /** 匹配的 Intent */
498
- readonly intent: Intent;
499
- /** 是否在服务端运行 */
500
- readonly isServer: boolean;
501
- /** DI 容器(可获取自定义服务) */
502
- readonly container: Container;
503
- /** 获取 Cookie 值(两端均可用) */
504
- getCookie(name: string): string | undefined;
505
- /** 获取请求头值(仅服务端有值,客户端始终返回 undefined) */
506
- getHeader(name: string): string | undefined;
507
- }
508
- /** 后置上下文(afterLoad 阶段,包含页面数据) */
509
- interface PostLoadContext extends NavigationContext {
510
- /** 控制器返回的页面数据 */
511
- readonly page: BasePage;
512
- }
513
- /** 继续执行下一个中间件 */
514
- interface NextResult {
515
- readonly kind: "next";
516
- }
517
- /** 重定向(服务端: HTTP 301/302,客户端: 触发新导航) */
518
- interface RedirectResult {
519
- readonly kind: "redirect";
520
- readonly url: string;
521
- readonly status: number;
522
- }
523
- /** URL 重写(服务端: HTTP 301,客户端: replaceState 仅更新地址栏) */
524
- interface RewriteResult {
525
- readonly kind: "rewrite";
526
- readonly url: string;
527
- }
528
- /** 拒绝访问 */
529
- interface DenyResult {
530
- readonly kind: "deny";
531
- readonly status: number;
532
- readonly message: string;
533
- }
534
- type MiddlewareResult = NextResult | RedirectResult | RewriteResult | DenyResult;
535
- /** 继续执行 */
536
- declare function next(): NextResult;
537
- /** 重定向到新 URL */
538
- declare function redirect(url: string, status?: 301 | 302): RedirectResult;
539
- /** URL 重写(不重新加载数据) */
540
- declare function rewrite(url: string): RewriteResult;
541
- /** 拒绝访问 */
542
- declare function deny(status?: number, message?: string): DenyResult;
543
- /** beforeLoad 守卫:路由匹配后、数据加载前 */
544
- type BeforeLoadGuard = (ctx: NavigationContext) => MiddlewareResult | Promise<MiddlewareResult>;
545
- /** afterLoad 守卫:数据加载后、渲染前 */
546
- type AfterLoadGuard = (ctx: PostLoadContext) => MiddlewareResult | Promise<MiddlewareResult>;
547
- //#endregion
548
- //#region ../core/src/router/params/standard.d.ts
549
- /**
550
- * Standard Schema v1 接口的最小本地声明 + 运行助手。
551
- * 纯 type-level 规范 + 运行时鸭子类型,不依赖 @standard-schema/spec 运行时包。
552
- * https://standardschema.dev
553
- */
554
- interface StandardSchemaV1<Input = unknown, Output = Input> {
555
- readonly "~standard": {
556
- readonly version: 1;
557
- readonly vendor: string;
558
- readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
559
- readonly types?: {
560
- readonly input: Input;
561
- readonly output: Output;
562
- };
563
- };
564
- }
565
- type StandardResult<Output> = {
566
- readonly value: Output;
567
- readonly issues?: undefined;
568
- } | {
569
- readonly issues: ReadonlyArray<StandardIssue>;
570
- };
571
- interface StandardIssue {
572
- readonly message: string;
573
- readonly path?: ReadonlyArray<PropertyKey | {
574
- readonly key: PropertyKey;
575
- }>;
576
- }
577
- /** 提取 Standard Schema 的输出类型 */
578
- type InferOutput<S extends StandardSchemaV1> = NonNullable<S["~standard"]["types"]>["output"];
579
- /** 路由参数 codec:输入恒为 string(缺失时 undefined),输出为目标类型 T */
580
- type ParamSchema<T = unknown> = StandardSchemaV1<string, T>;
581
- /** 工厂:从一个 validate 函数构造实现了 ~standard 的 codec */
582
- declare function makeSchema<T>(validate: (value: unknown) => StandardResult<T> | Promise<StandardResult<T>>): ParamSchema<T>;
583
- /** 统一执行任意 Standard Schema 的校验,吸收同步/异步差异(raw 可为单值或多值 query) */
584
- declare function runStandard(schema: StandardSchemaV1, raw: string | string[] | undefined): Promise<{
585
- ok: true;
586
- value: unknown;
587
- } | {
588
- ok: false;
589
- issues: readonly StandardIssue[];
590
- }>;
591
- //#endregion
592
- //#region ../core/src/router/params/multi.d.ts
593
- /** 多值(数组)query codec:输入为 string[],并带 `multi` 运行时标记。 */
594
- interface MultiValueSchema<Output> extends StandardSchemaV1<string[], Output> {
595
- readonly multi: true;
596
- }
597
- interface ListOptions {
598
- /** 最少元素数 */
599
- min?: number;
600
- /** 最多元素数 */
601
- max?: number;
602
- }
603
- /** 运行时判断一个 schema 是否为多值 codec(resolve 据此决定取全部值还是单值)。 */
604
- declare function isMultiValueSchema(schema: StandardSchemaV1): schema is MultiValueSchema<unknown>;
605
- /**
606
- * 多值 query 原语:用内部单值 codec 逐项校验/转换,产出数组。
607
- * 缺失(undefined)→ 空数组;任一项校验失败 → 整体失败(resolve 据此 fall-through)。
608
- */
609
- declare function list<T>(item: ParamSchema<T>, opts?: ListOptions): MultiValueSchema<T[]>;
610
- //#endregion
611
- //#region ../core/src/router/params/infer.d.ts
612
- /** 剥离可选参数尾随的 "?" */
613
- type StripOptional<S extends string> = S extends `${infer N}?` ? N : S;
614
- /** 从 path pattern 字面量提取参数名联合(处理 :param 与 :param?) */
615
- type ExtractParamNames<Path extends string> = Path extends `${infer _Head}:${infer Rest}` ? Rest extends `${infer Name}/${infer Tail}` ? StripOptional<Name> | ExtractParamNames<`/${Tail}`> : StripOptional<Rest> : never;
616
- /** path 参数 codec map 的形状:key 只能是 path 中出现的参数名(均可选声明) */
617
- type ParamsFor<Path extends string> = { [K in ExtractParamNames<Path>]?: ParamSchema };
618
- /** query 参数 codec map:key 自由开放;值可为单值 codec 或多值(list)codec */
619
- type QuerySchemaMap = Record<string, StandardSchemaV1<string, unknown> | MultiValueSchema<unknown>>;
620
- /** 把对象类型「拍平」为单层,便于阅读与类型相等比较 */
621
- type Prettify<T> = { [K in keyof T]: T[K] } & {};
622
- /** 输出类型含 undefined 的 key(由 optional() 产生)→ 渲染为可选属性 */
623
- type OptionalOutKeys<M extends Record<string, StandardSchemaV1>> = { [K in keyof M]: undefined extends InferOutput<M[K]> ? K : never }[keyof M];
624
- type RequiredOutKeys<M extends Record<string, StandardSchemaV1>> = { [K in keyof M]: undefined extends InferOutput<M[K]> ? never : K }[keyof M];
625
- /**
626
- * 从 codec map 推导运行期参数类型。
627
- * optional() 让输出含 undefined 的 key 渲染为可选属性(`tab?: T`,并剥掉冗余的 `| undefined`);
628
- * withDefault() 始终有值,key 保持必选。
629
- */
630
- type InferParams<P extends Record<string, ParamSchema>> = Prettify<{ [K in RequiredOutKeys<P>]: InferOutput<P[K]> } & { [K in OptionalOutKeys<P>]?: Exclude<InferOutput<P[K]>, undefined> }>;
631
- type InferQuery<Q extends QuerySchemaMap> = Prettify<{ [K in RequiredOutKeys<Q>]: InferOutput<Q[K]> } & { [K in OptionalOutKeys<Q>]?: Exclude<InferOutput<Q[K]>, undefined> }>;
632
- //#endregion
633
- //#region ../core/src/router/router.d.ts
634
- /** 路由匹配结果 */
635
- interface RouteMatch {
636
- intent: Intent;
637
- action: FlowAction;
638
- renderMode?: string;
639
- /** 该路由绑定的 beforeLoad 守卫 */
640
- beforeGuards?: BeforeLoadGuard[];
641
- /** 该路由绑定的 afterLoad 守卫 */
642
- afterGuards?: AfterLoadGuard[];
643
- }
644
- /** 路由添加选项 */
645
- interface RouteAddOptions {
646
- renderMode?: string;
647
- beforeGuards?: BeforeLoadGuard[];
648
- afterGuards?: AfterLoadGuard[];
649
- paramCodecs?: Record<string, ParamSchema>;
650
- queryCodecs?: QuerySchemaMap;
651
- }
652
- declare class Router {
653
- private readonly debug?;
654
- private routes;
655
- constructor(debug?: ((message: string) => void) | undefined);
656
- /** 添加路由规则 */
657
- add(pattern: string, intentId: string, renderModeOrOptions?: string | RouteAddOptions): this;
658
- /** 解析 URL → RouteMatch(含参数校验;校验失败则 fall-through 到下一条路由) */
659
- resolve(urlOrPath: string): Promise<RouteMatch | null>;
660
- /** 获取所有已注册的路由 */
661
- getRoutes(): string[];
662
- private parseUrl;
663
- }
664
- //#endregion
665
- //#region ../core/src/router/types.d.ts
666
- /**
667
- * Router 共享类型
668
- *
669
- * `RouteParams` 是路由参数(path + query 经 codec 转换后)的统一形状,
670
- * 同时也是 Intent.params、NavigationContext.params 与 LeafNode.params 的共同类型。
671
- */
672
- /** 路由参数:键为参数名,值为 codec 转换后的任意类型(string / number / boolean …)。 */
673
- type RouteParams = Record<string, unknown>;
674
- //#endregion
675
- //#region ../core/src/router/params/primitives.d.ts
676
- interface StrOptions {
677
- minLength?: number;
678
- maxLength?: number;
679
- pattern?: RegExp;
680
- }
681
- declare function str(opts?: StrOptions): ParamSchema<string>;
682
- interface NumOptions {
683
- min?: number;
684
- max?: number;
685
- }
686
- declare function int(opts?: NumOptions): ParamSchema<number>;
687
- declare function num(opts?: NumOptions): ParamSchema<number>;
688
- declare function bool(): ParamSchema<boolean>;
689
- declare function oneOf<const T extends readonly string[]>(values: T): ParamSchema<T[number]>;
690
- declare function uuid(): ParamSchema<string>;
691
- //#endregion
692
- //#region ../core/src/router/params/modifiers.d.ts
693
- /** 输入缺失(undefined)时跳过校验、产出 undefined;否则委托内部 codec(同步或异步均可)。 */
694
- declare function optional<T>(codec: ParamSchema<T>): ParamSchema<T | undefined>;
695
- /** 输入缺失时用 fallback;否则委托内部 codec。 */
696
- declare function withDefault<T>(codec: ParamSchema<T>, fallback: T): ParamSchema<T>;
697
- //#endregion
698
- //#region ../core/src/logger/composite.d.ts
699
- declare class CompositeLoggerFactory implements LoggerFactory {
700
- private readonly factories;
701
- constructor(factories: LoggerFactory[]);
702
- loggerFor(name: string): Logger;
703
- }
704
- declare class CompositeLogger implements Logger {
705
- private readonly loggers;
706
- constructor(loggers: Logger[]);
707
- debug(...args: unknown[]): string;
708
- info(...args: unknown[]): string;
709
- warn(...args: unknown[]): string;
710
- error(...args: unknown[]): string;
711
- private callAll;
712
- }
713
- //#endregion
714
- //#region ../core/src/logger/console.d.ts
715
- declare class ConsoleLogger extends BaseLogger {
716
- debug(...args: unknown[]): string;
717
- info(...args: unknown[]): string;
718
- warn(...args: unknown[]): string;
719
- error(...args: unknown[]): string;
720
- }
721
- declare class ConsoleLoggerFactory implements LoggerFactory {
722
- loggerFor(category: string): Logger;
723
- }
724
- //#endregion
725
- //#region ../core/src/logger/local-storage-filter.d.ts
726
- declare function shouldLog(name: string, level: Level): boolean;
727
- declare function resetFilterCache(): void;
728
- //#endregion
729
- //#region ../core/src/i18n/types.d.ts
730
- /**
731
- * i18n — 类型定义
732
- *
733
- * 框架级国际化基础设施。
734
- */
735
- /** 翻译函数 */
736
- interface Translator {
737
- /**
738
- * 翻译 key → 本地化字符串
739
- * @param key - 翻译 key
740
- * @param values - 插值参数
741
- */
742
- t(key: string, values?: Record<string, string | number>): string;
743
- /**
744
- * 复数形式翻译
745
- * @param key - 翻译 key 前缀
746
- * @param count - 数量
747
- * @param values - 附加插值
748
- */
749
- plural(key: string, count: number, values?: Record<string, string | number>): string;
750
- /** 当前 locale(如 "zh-Hans" / "en-US") */
751
- readonly locale: string;
752
- }
753
- /** 文本方向 */
754
- type TextDirection = "ltr" | "rtl";
755
- /** HTML 语言属性 */
756
- interface LocaleAttributes {
757
- /** BCP 47 语言标签 */
758
- lang: string;
759
- /** 文本方向 */
760
- dir: TextDirection;
761
- }
762
- /** Locale 信息 */
763
- interface LocaleInfo {
764
- /** 语言代码(如 "zh-Hans", "en") */
765
- language: string;
766
- /** 地区/Storefront 代码(如 "CN", "US") */
767
- region?: string;
768
- /** BCP 47 完整标签 */
769
- bcp47: string;
770
- /** 文本方向 */
771
- dir: TextDirection;
772
- }
773
- //#endregion
774
- //#region ../core/src/prefetched-intents/prefetched-intents.d.ts
775
- /** 预获取的 Intent-Data 对 */
776
- interface PrefetchedIntent {
777
- intent: Intent;
778
- data: unknown;
779
- }
780
- declare class PrefetchedIntents {
781
- private intents;
782
- private constructor();
783
- /** 从 PrefetchedIntent 数组创建缓存实例 */
784
- static fromArray(items: PrefetchedIntent[]): PrefetchedIntents;
785
- /** 创建空缓存实例 */
786
- static empty(): PrefetchedIntents;
787
- /**
788
- * 获取缓存的 Intent 结果(一次性使用)。
789
- * 命中后从缓存中删除。
790
- */
791
- get<T>(intent: Intent<T>): T | undefined;
792
- /** 检查缓存中是否有某个 Intent 的数据 */
793
- has(intent: Intent): boolean;
794
- /** 缓存中的条目数 */
795
- get size(): number;
796
- }
797
- //#endregion
798
- //#region ../core/src/framework.d.ts
799
- /** Framework 初始化配置 */
800
- interface FrameworkConfig extends MakeDependenciesOptions {
801
- setupRoutes?: (router: Router) => void;
802
- prefetchedIntents?: PrefetchedIntents;
803
- }
804
- declare class Framework {
805
- readonly container: Container;
806
- readonly intentDispatcher: IntentDispatcher;
807
- readonly actionDispatcher: ActionDispatcher;
808
- readonly router: Router;
809
- readonly prefetchedIntents: PrefetchedIntents;
810
- private readonly beforeGuards;
811
- private readonly afterGuards;
812
- private _logger?;
813
- private constructor();
814
- /** 创建并初始化 Framework 实例 */
815
- static create(config?: FrameworkConfig): Framework;
816
- private getLogger;
817
- /** 分发 Intent — 获取页面数据 */
818
- dispatch<T>(intent: Intent<T>): Promise<T>;
819
- /** 执行 Action — 处理用户交互 */
820
- perform(action: Action): Promise<void>;
821
- /** 路由 URL — 将 URL 解析为 Intent + Action */
822
- routeUrl(url: string): Promise<RouteMatch | null>;
823
- /** 记录页面访问事件 */
824
- didEnterPage(page: BasePage): void;
825
- /** 获取 locale 信息(如果已配置) */
826
- getLocale(): LocaleAttributes | undefined;
827
- /** 获取翻译器(如果当前 locale 已经初始化了翻译字典) */
828
- getTranslator(): Translator | undefined;
829
- /** 获取平台信息 */
830
- getPlatform(): PlatformInfo;
831
- /** 注册 Action 处理器 */
832
- onAction<A extends Action>(kind: string, handler: ActionHandler<A>): void;
833
- /** 注册 Intent Controller */
834
- registerIntent(controller: IntentController): void;
835
- /** 注册 beforeLoad 守卫(路由匹配后、数据加载前) */
836
- beforeLoad(guard: BeforeLoadGuard): void;
837
- /** 注册 afterLoad 守卫(数据加载后、渲染前) */
838
- afterLoad(guard: AfterLoadGuard): void;
839
- /** 执行所有 beforeLoad 守卫(全局 → 路由级) */
840
- runBeforeLoad(ctx: NavigationContext, routeGuards?: BeforeLoadGuard[]): Promise<MiddlewareResult>;
841
- /** 执行所有 afterLoad 守卫(全局 → 路由级) */
842
- runAfterLoad(ctx: PostLoadContext, routeGuards?: AfterLoadGuard[]): Promise<MiddlewareResult>;
843
- /** 销毁 Framework 实例 */
844
- dispose(): void;
845
- }
846
- //#endregion
847
- //#region ../core/src/models/safe-error-page.d.ts
848
- interface SafeErrorPageOptions {
849
- /** HTTP-style status code used in the page title (e.g. 404, 500). */
850
- status: number;
851
- /**
852
- * The message users / client code may safely see in production. Should
853
- * contain no stack, file paths, hostnames, or secrets.
854
- */
855
- publicMessage: string;
856
- /**
857
- * Optional error / debug payload to surface only in non-production. In
858
- * production this is dropped entirely; only `publicMessage` is exposed.
859
- */
860
- devError?: unknown;
861
- /**
862
- * Override the production detection. Pass `true` to force the prod-safe
863
- * variant (drops devError). Defaults to detecting `process.env.NODE_ENV`.
864
- */
865
- isProduction?: boolean;
866
- }
867
- /**
868
- * Build a BasePage for an error condition. In production, only `publicMessage`
869
- * makes it into the page. In dev, `devError` (if provided) is appended.
870
- */
871
- declare function safeErrorPage(options: SafeErrorPageOptions): BasePage;
872
- //#endregion
873
- //#region ../core/src/models/shelf.d.ts
874
- interface BaseShelf {
875
- id: string;
876
- shelfType: string;
877
- title?: string;
878
- subtitle?: string;
879
- seeAllAction?: Action;
880
- isHorizontal?: boolean;
881
- }
882
- interface BaseItem {
883
- id: string;
884
- itemType: string;
885
- clickAction?: Action;
886
- }
887
- //#endregion
888
- //#region ../core/src/prefetched-intents/stable-stringify.d.ts
889
- /**
890
- * stableStringify — 确定性 JSON 序列化(keys 按字母排序)
891
- *
892
- * 用作缓存 key:相同内容的对象始终产生相同字符串。
893
- */
894
- declare function stableStringify(obj: unknown): string;
895
- //#endregion
896
- //#region ../core/src/http/client.d.ts
897
- /**
898
- * HttpClient — 通用 HTTP 客户端基类
899
- *
900
- * 为 API Client 提供标准化的 HTTP 请求能力。
901
- * 子类继承后只需关注业务端点定义,不需要重复实现 fetch / JSON 解析 / 错误处理。
902
- *
903
- * 默认安全:拒绝向内网 / loopback / 保留地址发请求(SSRF 防御)。应用层
904
- * 显式 opt-out 用 `allowInternalHosts: true`。详见 host-guard.ts。
905
- */
906
- /** HTTP 请求错误 */
907
- declare class HttpError extends Error {
908
- readonly status: number;
909
- readonly statusText: string;
910
- readonly body?: string | undefined;
911
- constructor(status: number, statusText: string, body?: string | undefined);
912
- }
913
- /**
914
- * SSRF 防护拦截到不安全的目标地址时抛出。应用层可以 catch 它来给出业务友好的错误,
915
- * 不需要靠 message 字符串匹配。
916
- */
917
- declare class HostGuardError extends Error {
918
- readonly url: string;
919
- readonly reason: string;
920
- constructor(url: string, reason: string);
921
- }
922
- /** 请求拦截器 — 在发送前修改请求 */
923
- interface RequestInterceptor {
924
- (url: string, init: RequestInit): RequestInit | Promise<RequestInit>;
925
- }
926
- /** 响应拦截器 — 在解析前修改响应 */
927
- interface ResponseInterceptor {
928
- (response: Response, url: string): Response | Promise<Response>;
929
- }
930
- /** HttpClient 构造配置 */
931
- interface HttpClientConfig {
932
- /** API base URL(如 "/api" 或 "https://example.com/api") */
933
- baseUrl: string;
934
- /** 默认请求头 */
935
- defaultHeaders?: Record<string, string>;
936
- /** 自定义 fetch 实现(便于测试或 SSR) */
937
- fetch?: typeof globalThis.fetch;
938
- /** 请求拦截器(按注册顺序执行) */
939
- requestInterceptors?: RequestInterceptor[];
940
- /** 响应拦截器(按注册顺序执行) */
941
- responseInterceptors?: ResponseInterceptor[];
942
- /**
943
- * 是否允许向私有 / loopback / 保留 IP 段发请求。
944
- *
945
- * **默认 false** —— 阻止内网穿透(SSRF)。如果你的服务正常需要打内网(如
946
- * 微服务对内 API、127.0.0.1 上的开发依赖),把它设为 true 显式 opt-out,并
947
- * 自己做来源校验。
948
- */
949
- allowInternalHosts?: boolean;
950
- /**
951
- * 是否在请求前 DNS 解析 hostname 并对解析结果做 IP 段校验。
952
- *
953
- * **默认 true**(仅 Node 环境有效;浏览器静默跳过)。配合 `allowInternalHosts`
954
- * 防御 DNS rebinding:如果 hostname 不是 IP 字面量,框架会 resolve 它的 A/AAAA
955
- * 记录并按 IP 段校验。`false` 关闭只剩 IP 字面量同步校验。
956
- */
957
- validateDns?: boolean;
958
- }
959
- /**
960
- * 通用 HTTP 客户端基类
961
- *
962
- * 使用方式: 创建子类继承 HttpClient,定义业务方法调用 this.get() / this.post() 等。
963
- *
964
- * @example
965
- * ```ts
966
- * class MyApiClient extends HttpClient {
967
- * async getUser(id: string) {
968
- * return this.get<User>(`/users/${id}`);
969
- * }
970
- * }
971
- * ```
972
- */
973
- declare abstract class HttpClient {
974
- protected readonly baseUrl: string;
975
- protected readonly defaultHeaders: Record<string, string>;
976
- protected readonly fetchFn: typeof globalThis.fetch;
977
- private readonly requestInterceptors;
978
- private readonly responseInterceptors;
979
- private readonly allowInternalHosts;
980
- private readonly validateDns;
981
- constructor(config: HttpClientConfig);
982
- /** 动态添加请求拦截器 */
983
- useRequestInterceptor(interceptor: RequestInterceptor): this;
984
- /** 动态添加响应拦截器 */
985
- useResponseInterceptor(interceptor: ResponseInterceptor): this;
986
- /** GET 请求,返回解析后的 JSON */
987
- protected get<T>(path: string, params?: Record<string, string>): Promise<T>;
988
- /** POST 请求,自动序列化 body 为 JSON */
989
- protected post<T>(path: string, body?: unknown, params?: Record<string, string>): Promise<T>;
990
- /** PUT 请求 */
991
- protected put<T>(path: string, body?: unknown, params?: Record<string, string>): Promise<T>;
992
- /** DELETE 请求 */
993
- protected del<T>(path: string, params?: Record<string, string>): Promise<T>;
994
- /**
995
- * 底层请求方法 — 子类可覆写以自定义行为
996
- *
997
- * 自动处理:
998
- * - URL 拼接 (baseUrl + path + params)
999
- * - SSRF 防护(IP 字面量同步校验 + 可选 DNS 解析校验)
1000
- * - 默认 headers 合并
1001
- * - JSON body 序列化
1002
- * - 响应 JSON 解析
1003
- * - 非 2xx 状态码抛出 HttpError
1004
- */
1005
- protected request<T>(method: string, path: string, options?: {
1006
- params?: Record<string, string>;
1007
- body?: unknown;
1008
- headers?: Record<string, string>;
1009
- }): Promise<T>;
1010
- /** 构建完整 URL — 子类可覆写以自定义 URL 拼接逻辑 */
1011
- protected buildUrl(path: string, params?: Record<string, string>): string;
1012
- private enforceHostGuard;
1013
- }
1014
- //#endregion
1015
- //#region ../core/src/http/host-guard.d.ts
1016
- /**
1017
- * host-guard — refuse outbound requests to private / loopback / reserved hosts.
1018
- *
1019
- * Why this exists: `HttpClient` and the raw `fetch` injected via DI both used
1020
- * to accept any URL the application built. SSR controllers that take a
1021
- * user-controlled URL (image proxy, link preview, OAuth callback) could be
1022
- * tricked into fetching `http://127.0.0.1/admin` and embedding the response in
1023
- * the SSR HTML. Round 1 of the adversarial drill walked straight through this;
1024
- * round 2's hand-rolled IPv4 regex was bypassed in five minutes with
1025
- * `[::ffff:7f00:1]`. This module centralises the check so applications can stop
1026
- * re-implementing it (badly).
1027
- *
1028
- * Coverage (synchronous, IP-literal forms):
1029
- * - IPv4 dotted-decimal: `127.0.0.1`, `10.0.0.5`, `192.168.1.1`, `172.16.0.1`
1030
- * - IPv4 zero / link-local / multicast / reserved: `0.0.0.0`, `169.254.0.1`,
1031
- * `224.0.0.1`, `240.0.0.0/4`
1032
- * - IPv4 non-dotted forms accepted by some parsers: decimal `2130706433`,
1033
- * hex `0x7f000001`, octal `0177.0.0.1`
1034
- * - IPv6 loopback `::1`, link-local `fe80::/10`, ULA `fc00::/7`, unspecified `::`
1035
- * - IPv4-mapped IPv6: `::ffff:7f00:1`, `::ffff:127.0.0.1`
1036
- * - Names: `localhost`, `*.localhost`
1037
- *
1038
- * NOT covered here (callers can layer on top):
1039
- * - DNS resolution of arbitrary hostnames — see `validateUrlWithDns` in
1040
- * server-only callers. DNS rebinding is impossible to fix at this layer
1041
- * alone; the right pattern is "resolve once, then fetch by IP".
1042
- * - IDN / homograph attacks — Node's URL parser punycode-encodes hostnames
1043
- * already, so the hostname this code sees is the ASCII form.
1044
- */
1045
- type HostCheckResult = {
1046
- ok: true;
1047
- } | {
1048
- ok: false;
1049
- reason: string;
1050
- };
1051
- /**
1052
- * Inspect a hostname string (the `URL.hostname` value, without brackets, port,
1053
- * or userinfo). Returns `{ok: false}` for anything in a private/loopback/
1054
- * reserved range; `{ok: true}` if the literal looks like a public address or a
1055
- * non-IP name (the caller may then DNS-resolve and re-check).
1056
- */
1057
- declare function classifyHost(rawHost: string): HostCheckResult;
1058
- /**
1059
- * Convenience wrapper for callers holding a full URL string. Also rejects
1060
- * non-http(s) schemes (gopher, file, data, …).
1061
- */
1062
- declare function classifyUrl(rawUrl: string): HostCheckResult;
1063
- //#endregion
1064
- //#region ../core/src/intents/base-controller.d.ts
1065
- /**
1066
- * 抽象 Controller 基类
1067
- *
1068
- * 统一处理:
1069
- * - 类型安全的参数提取 (TParams)
1070
- * - 返回类型约束 (TResult)
1071
- * - try/catch 错误处理 + 可选 fallback
1072
- *
1073
- * @example
1074
- * ```ts
1075
- * class ProductController extends BaseController<{ productId: string }, ProductPage> {
1076
- * readonly intentId = "product-page";
1077
- *
1078
- * async execute(params: { productId: string }, container: Container) {
1079
- * const api = container.resolve<ApiClient>("api");
1080
- * return api.getProduct(params.productId);
1081
- * }
1082
- *
1083
- * fallback(params: { productId: string }, error: Error) {
1084
- * return getMockProduct(params.productId);
1085
- * }
1086
- * }
1087
- * ```
1088
- */
1089
- declare abstract class BaseController<TParams extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> implements IntentController<TResult> {
1090
- /** Controller 对应的 Intent ID */
1091
- abstract readonly intentId: string;
1092
- /**
1093
- * 执行业务逻辑 — 子类必须实现
1094
- *
1095
- * @param params - Intent 参数(已类型化)
1096
- * @param container - DI 容器
1097
- * @returns 页面数据
1098
- */
1099
- abstract execute(params: TParams, container: Container): Promise<TResult> | TResult;
1100
- /**
1101
- * 错误回退 — 子类可选覆写
1102
- *
1103
- * 当 execute() 抛出异常时调用。
1104
- * 默认行为: 重新抛出原始错误。
1105
- *
1106
- * @param params - Intent 参数
1107
- * @param error - execute() 抛出的错误
1108
- * @returns 回退数据
1109
- */
1110
- fallback(params: TParams, error: Error): Promise<TResult> | TResult;
1111
- /**
1112
- * IntentController.perform() 实现
1113
- *
1114
- * 自动 try/catch → fallback 模式。
1115
- */
1116
- perform(intent: Intent<TResult>, container: Container): Promise<TResult>;
1117
- }
1118
- //#endregion
1119
- //#region ../core/src/data/mapper.d.ts
1120
- /**
1121
- * Mapper 类型工具 — 标准化数据转换管线
1122
- *
1123
- * 提供类型约定和组合函数,让 API 响应 → 页面模型 的转换有统一的签名模式。
1124
- */
1125
- /** 同步映射函数 */
1126
- type Mapper<TInput, TOutput> = (input: TInput) => TOutput;
1127
- /** 异步映射函数 */
1128
- type AsyncMapper<TInput, TOutput> = (input: TInput) => TOutput | Promise<TOutput>;
1129
- /**
1130
- * 组合两个同步 Mapper: A → B → C
1131
- */
1132
- declare function pipe<A, B, C>(m1: Mapper<A, B>, m2: Mapper<B, C>): Mapper<A, C>;
1133
- /**
1134
- * 组合三个同步 Mapper: A → B → C → D
1135
- */
1136
- declare function pipe<A, B, C, D>(m1: Mapper<A, B>, m2: Mapper<B, C>, m3: Mapper<C, D>): Mapper<A, D>;
1137
- /**
1138
- * 组合四个同步 Mapper: A → B → C → D → E
1139
- */
1140
- declare function pipe<A, B, C, D, E>(m1: Mapper<A, B>, m2: Mapper<B, C>, m3: Mapper<C, D>, m4: Mapper<D, E>): Mapper<A, E>;
1141
- /**
1142
- * 组合任意数量的同步 Mapper
1143
- */
1144
- declare function pipe(...mappers: Mapper<unknown, unknown>[]): Mapper<unknown, unknown>;
1145
- /**
1146
- * 组合两个可能异步的 Mapper: A → B → C
1147
- */
1148
- declare function pipeAsync<A, B, C>(m1: AsyncMapper<A, B>, m2: AsyncMapper<B, C>): AsyncMapper<A, C>;
1149
- /**
1150
- * 组合三个可能异步的 Mapper
1151
- */
1152
- declare function pipeAsync<A, B, C, D>(m1: AsyncMapper<A, B>, m2: AsyncMapper<B, C>, m3: AsyncMapper<C, D>): AsyncMapper<A, D>;
1153
- /**
1154
- * 将一个 Mapper 应用到数组的每个元素
1155
- */
1156
- declare function mapEach<TInput, TOutput>(mapper: Mapper<TInput, TOutput>): Mapper<TInput[], TOutput[]>;
1157
- //#endregion
1158
- //#region ../core/src/navigation/types.d.ts
1159
- /**
1160
- * 导航树里 `Page` 的别名 —— 运行期 dispatch 始终产出 `BasePage`,
1161
- * 但导航层对内容无关,字段语义由应用决定。
1162
- */
1163
- type Page = BasePage;
1164
- /** 导航节点 Kind 常量 */
1165
- declare const NAVIGATION_NODE_KINDS: {
1166
- readonly LEAF: "leaf";
1167
- readonly STACK: "stack";
1168
- readonly TABS: "tabs";
1169
- readonly SPLIT: "split";
1170
- };
1171
- /** 所有导航节点 Kind 的联合类型 */
1172
- type NavigationNodeKind = (typeof NAVIGATION_NODE_KINDS)[keyof typeof NAVIGATION_NODE_KINDS];
1173
- /** 叶子:一个具体导航目标 */
1174
- interface LeafNode {
1175
- readonly kind: typeof NAVIGATION_NODE_KINDS.LEAF;
1176
- readonly intent: string;
1177
- readonly params: RouteParams;
1178
- }
1179
- /** 栈:有序路径,entries[0]=根,末尾=栈顶(可见) */
1180
- interface StackNode {
1181
- readonly kind: typeof NAVIGATION_NODE_KINDS.STACK;
1182
- readonly entries: readonly NavigationNode[];
1183
- }
1184
- /** Tabs:并列分支 + 当前激活键 + 稳定顺序;仅激活分支可见 */
1185
- interface TabsNode {
1186
- readonly kind: typeof NAVIGATION_NODE_KINDS.TABS;
1187
- readonly active: string;
1188
- readonly order: readonly string[];
1189
- readonly branches: Readonly<Record<string, NavigationNode>>;
1190
- }
1191
- /** Split 列:列 id + 该列内容(undefined = 尚未选择) */
1192
- interface SplitColumn {
1193
- readonly id: string;
1194
- readonly content: NavigationNode | undefined;
1195
- }
1196
- /**
1197
- * Split 列可见性,对标 SwiftUI `NavigationSplitViewVisibility`。
1198
- *
1199
- * 这是**可绑定 / 可序列化 / 可恢复的导航状态**(不是渲染样式):它决定哪些列算「可见」,
1200
- * 进而影响 `collectVisibleDestinations` 与 SSR 预取——例如深链到 `detailOnly` 时服务端只预取 detail 列。
1201
- *
1202
- * - `automatic`(缺省):框架不裁剪,所有有内容的列都可见(SSR 端无视口信息时的安全默认;客户端再按视口自适应)。
1203
- * - `all`:显式所有列可见(语义同 automatic 的全列)。
1204
- * - `doubleColumn`:仅首列 + 末列可见(三列时隐藏中间 content 列)。
1205
- * - `detailOnly`:仅末列(detail)可见。
1206
- *
1207
- * 注意:compact 视口塌缩成单栈(SwiftUI 的 `preferredCompactColumn`)是视口反应式的纯渲染决策,
1208
- * 框架不建模,交给应用按 `getPlatform()` / 视口自行处理。
1209
- */
1210
- declare const SPLIT_VISIBILITIES: {
1211
- readonly AUTOMATIC: "automatic";
1212
- readonly ALL: "all";
1213
- readonly DOUBLE_COLUMN: "doubleColumn";
1214
- readonly DETAIL_ONLY: "detailOnly";
1215
- };
1216
- /** Split 列可见性的联合类型 */
1217
- type SplitVisibility = (typeof SPLIT_VISIBILITIES)[keyof typeof SPLIT_VISIBILITIES];
1218
- /**
1219
- * Split:多列并存,列间通过 selectColumn 设置后续列内容。
1220
- * `visibility` 决定哪些列算可见(缺省 `automatic` = 全列),是可序列化的导航状态。
1221
- */
1222
- interface SplitNode {
1223
- readonly kind: typeof NAVIGATION_NODE_KINDS.SPLIT;
1224
- readonly columns: readonly SplitColumn[];
1225
- readonly visibility?: SplitVisibility;
1226
- }
1227
- /** 所有导航节点的联合类型 */
1228
- type NavigationNode = LeafNode | StackNode | TabsNode | SplitNode;
1229
- /** 指向树中某节点的路径(从根到目标)的一步 */
1230
- type NavigationPathStep = {
1231
- readonly kind: "stack-entry";
1232
- readonly index: number;
1233
- } | {
1234
- readonly kind: "tab";
1235
- readonly key: string;
1236
- } | {
1237
- readonly kind: "column";
1238
- readonly id: string;
1239
- };
1240
- /** 指向树中某节点的完整路径(从根到目标) */
1241
- type NavigationPath = readonly NavigationPathStep[];
1242
- /** 单个可见目标的解析结果 */
1243
- interface ResolvedDestination {
1244
- readonly intent: string;
1245
- readonly params: RouteParams;
1246
- readonly page: Page;
1247
- readonly status?: number;
1248
- }
1249
- /** 导航快照:当前树 + 所有可见目标解析结果(顺序与 collectVisibleDestinations 一致) */
1250
- interface NavigationSnapshot {
1251
- readonly tree: NavigationNode;
1252
- readonly destinations: readonly ResolvedDestination[];
1253
- }
1254
- /** 错误类型:序列化 / 路径 / 操作非法时抛出 */
1255
- declare class NavigationError extends Error {
1256
- constructor(message: string);
1257
- }
1258
- //#endregion
1259
- //#region ../core/src/navigation/nodes.d.ts
1260
- /** 构造叶子节点(一个具体导航目标)。 */
1261
- declare function leaf(intent: string, params?: RouteParams): LeafNode;
1262
- /**
1263
- * 构造栈节点。
1264
- * 接受单个根节点(栈仅含根)或一个 entries 数组(entries[0]=根,末尾=栈顶)。
1265
- *
1266
- * @example
1267
- * stack(leaf("home")) // 单根栈
1268
- * stack([leaf("home"), leaf("detail")]) // 根 + 栈顶
1269
- */
1270
- declare function stack(rootOrEntries: NavigationNode | readonly NavigationNode[]): StackNode;
1271
- /** tabs 构造选项 */
1272
- interface TabsInit {
1273
- /** 当前激活分支键 */
1274
- readonly active: string;
1275
- /** 分支映射(键 → 子节点) */
1276
- readonly branches: Readonly<Record<string, NavigationNode>>;
1277
- /** 稳定顺序;缺省时按 branches 的插入顺序推导 */
1278
- readonly order?: readonly string[];
1279
- }
1280
- /**
1281
- * 构造 Tabs 节点。
1282
- * 缺省 `order` 时按 `branches` 的插入顺序(`Object.keys`)推导稳定顺序。
1283
- */
1284
- declare function tabs(init: TabsInit): TabsNode;
1285
- /** split 列初始化(content 可缺省 = 尚未选择) */
1286
- interface SplitColumnInit {
1287
- readonly id: string;
1288
- readonly content?: NavigationNode;
1289
- }
1290
- /**
1291
- * 构造 Split 节点(多列并存)。
1292
- * `visibility` 缺省(不写字段)等价 `automatic` = 全列可见;
1293
- * 显式传入时纳入节点状态,影响 `collectVisibleDestinations` 与 SSR 预取。
1294
- *
1295
- * @example
1296
- * split([{ id: "sidebar", content: leaf("folders") }, { id: "detail" }])
1297
- * split([...], "detailOnly") // 深链:仅 detail 列可见
1298
- */
1299
- declare function split(columns: readonly SplitColumnInit[], visibility?: SplitVisibility): SplitNode;
1300
- declare function isLeafNode(node: NavigationNode): node is LeafNode;
1301
- declare function isStackNode(node: NavigationNode): node is StackNode;
1302
- declare function isTabsNode(node: NavigationNode): node is TabsNode;
1303
- declare function isSplitNode(node: NavigationNode): node is SplitNode;
1304
- //#endregion
1305
- //#region ../core/src/navigation/islands.d.ts
1306
- /** 交给挂载/渲染原语的单条目解析结果。 */
1307
- interface ResolvedEntry {
1308
- readonly intent: string;
1309
- readonly params: RouteParams;
1310
- readonly entryKey: string;
1311
- readonly page: BasePage;
1312
- /**
1313
- * SSR 水合提示:true = 该条目的容器已含服务端渲染标记,挂载原语应**水合**(如 Vue
1314
- * `createSSRApp().mount()`)而非新建(`createApp().mount()`)。缺省 false(新建)。
1315
- */
1316
- readonly hydrate?: boolean;
1317
- }
1318
- /**
1319
- * island 容器的标记属性 —— 客户端 orchestrator 与服务端 helper 的单一来源。
1320
- * `data-fs-entry`(标识容器)、`data-fs-intent`、`data-fs-key`(水合按它匹配)。
1321
- */
1322
- declare function islandContainerAttributes(intent: string, entryKey: string): Record<string, string>;
1323
- //#endregion
1324
- //#region ../core/src/navigation/operations.d.ts
1325
- /**
1326
- * 解析「激活路径」:从根沿可见分支一路向下,直到叶子或无法继续。
1327
- * - leaf:路径在此结束
1328
- * - stack:进入栈顶 entry
1329
- * - tabs:进入 active 分支
1330
- * - split:进入最后一个有内容的列(无任何内容则结束)
1331
- */
1332
- declare function resolveActivePath(tree: NavigationNode): NavigationPath;
1333
- /**
1334
- * 按路径定位节点;任一步无效(索引越界 / 键不存在 / 列为空 / kind 不匹配)返回 undefined。
1335
- */
1336
- declare function findNode(tree: NavigationNode, path: NavigationPath): NavigationNode | undefined;
1337
- /**
1338
- * 找到 target 处(默认激活路径)「at/under」最近的 StackNode 路径。
1339
- * 从 target 节点沿激活分支向下,返回第一个遇到的 StackNode 的完整路径;
1340
- * 找不到则返回 undefined。
1341
- */
1342
- declare function findNearestStack(tree: NavigationNode, path: NavigationPath): NavigationPath | undefined;
1343
- /**
1344
- * 收集所有可见的叶子目标(顺序即渲染/解析顺序)。
1345
- * - leaf → [leaf]
1346
- * - stack → 栈顶 entry 的可见目标
1347
- * - tabs → active 分支的可见目标
1348
- * - split → 每个有内容的列的可见目标,按列序拼接
1349
- */
1350
- declare function collectVisibleDestinations(tree: NavigationNode): readonly LeafNode[];
1351
- /**
1352
- * 收集树中**全部存在**的叶子(含不可见:栈非顶 entry、未激活 tab 分支、所有非空 split 列)。
1353
- * 区别于 `collectVisibleDestinations`(只沿可见分支)—— 保活 / 缓存 prune / 作用域保留需要
1354
- * 全部 present 条目。顺序:栈按序、tabs 按 `Object.values(branches)` 序、split 按列序。
1355
- */
1356
- declare function collectAllLeaves(tree: NavigationNode): readonly LeafNode[];
1357
- /**
1358
- * 按 `visibility` 求出一个 split 节点当前**可见**的列(不裁剪空内容列——空 content 由调用方处理)。
1359
- *
1360
- * - `automatic`(缺省)/ `all`:全部列。
1361
- * - `doubleColumn`:首列 + 末列(三列时隐藏中间 content 列;列数 ≤ 2 时等价全部)。
1362
- * - `detailOnly`:仅末列(detail)。
1363
- *
1364
- * 应用渲染时也可用它决定该画哪几列,无需自行重实现可见性映射。
1365
- */
1366
- declare function visibleSplitColumns(node: SplitNode): readonly SplitColumn[];
1367
- /** 在目标栈(默认激活栈,或 target「at/under」最近的 stack)顶部 push 一个节点。 */
1368
- declare function push(tree: NavigationNode, node: NavigationNode, target?: NavigationPath): NavigationNode;
1369
- /** 从 target 处最近的 stack 弹出 count 个 entry(默认 1);绝不弹到根 entry 之下。 */
1370
- declare function pop(tree: NavigationNode, count?: number, target?: NavigationPath): NavigationNode;
1371
- /** 把 target 处最近的 stack 弹回到根 entry。 */
1372
- declare function popToRoot(tree: NavigationNode, target?: NavigationPath): NavigationNode;
1373
- /** 把 target 处最近的 stack 弹回到指定 index(保留 [0..index])。 */
1374
- declare function popTo(tree: NavigationNode, index: number, target?: NavigationPath): NavigationNode;
1375
- /** 替换 target 处最近 stack 的栈顶 entry(栈为空时抛错)。 */
1376
- declare function replaceTop(tree: NavigationNode, node: NavigationNode, target?: NavigationPath): NavigationNode;
1377
- /**
1378
- * 切换 tabs 节点的激活分支。
1379
- * target 默认为「最近的激活 tabs 节点」;target 必须指向 TabsNode,且 key 必须是已知分支。
1380
- */
1381
- declare function selectTab(tree: NavigationNode, key: string, target?: NavigationPath): NavigationNode;
1382
- /**
1383
- * 设置 split 某列内容,并清空它之后的所有列(content 置 undefined)。
1384
- * target 默认为「最近的激活 split 节点」;columnId 必须是已知列。
1385
- */
1386
- declare function selectColumn(tree: NavigationNode, columnId: string, content: NavigationNode | undefined, target?: NavigationPath): NavigationNode;
1387
- /**
1388
- * 设置 split 节点的列可见性(对标 SwiftUI `NavigationSplitViewVisibility`)。
1389
- * target 默认为「最近的激活 split 节点」。改变 visibility 会影响 `collectVisibleDestinations`,
1390
- * 进而触发 controller 对「新变可见」的列做 dispatch / SSR 预取(如 detailOnly → all 时补预取 sidebar/content)。
1391
- */
1392
- declare function setVisibility(tree: NavigationNode, visibility: SplitVisibility, target?: NavigationPath): NavigationNode;
1393
- //#endregion
1394
- //#region ../core/src/navigation/serialization.d.ts
1395
- /** 序列化叶子 */
1396
- interface SerializedLeaf {
1397
- readonly kind: typeof NAVIGATION_NODE_KINDS.LEAF;
1398
- readonly intent: string;
1399
- readonly params: RouteParams;
1400
- }
1401
- /** 序列化栈 */
1402
- interface SerializedStack {
1403
- readonly kind: typeof NAVIGATION_NODE_KINDS.STACK;
1404
- readonly entries: readonly SerializedNavigation[];
1405
- }
1406
- /** 序列化 Tabs */
1407
- interface SerializedTabs {
1408
- readonly kind: typeof NAVIGATION_NODE_KINDS.TABS;
1409
- readonly active: string;
1410
- readonly order: readonly string[];
1411
- readonly branches: Readonly<Record<string, SerializedNavigation>>;
1412
- }
1413
- /** 序列化 Split 列(空内容用 null 表示,JSON 安全) */
1414
- interface SerializedSplitColumn {
1415
- readonly id: string;
1416
- readonly content: SerializedNavigation | null;
1417
- }
1418
- /** 序列化 Split(visibility 缺省时不写该字段,保持紧凑) */
1419
- interface SerializedSplit {
1420
- readonly kind: typeof NAVIGATION_NODE_KINDS.SPLIT;
1421
- readonly columns: readonly SerializedSplitColumn[];
1422
- readonly visibility?: SplitVisibility;
1423
- }
1424
- /** 序列化后的导航树(JSON 安全的可辨识联合) */
1425
- type SerializedNavigation = SerializedLeaf | SerializedStack | SerializedTabs | SerializedSplit;
1426
- /** 把导航树序列化为 JSON 安全的纯对象。 */
1427
- declare function serializeNavigation(tree: NavigationNode): SerializedNavigation;
1428
- /** 导航树的确定性字符串形式(keys 排序);用于稳定缓存键 / 紧凑编码。 */
1429
- declare function serializeNavigationStable(tree: NavigationNode): string;
1430
- /** 从 JSON 安全数据还原导航树;结构畸形抛 NavigationError。 */
1431
- declare function deserializeNavigation(data: unknown): NavigationNode;
1432
- //#endregion
1433
- //#region ../core/src/navigation/codec.d.ts
1434
- /**
1435
- * Router 的最小读取面 —— codec 只依赖这两个公共方法,避免与 Router 实现耦合。
1436
- * (`reverse` 为可选:若 Router 将来提供则优先使用。)
1437
- */
1438
- interface NavigationRouterLike {
1439
- /** 所有已注册路由的 `"pattern → intentId"` 摘要 */
1440
- getRoutes(): string[];
1441
- /** 可选:把 intentId + 参数反查为 URL(若实现则 encode 优先使用) */
1442
- reverse?(intentId: string, params: RouteParams): string | undefined;
1443
- }
1444
- /**
1445
- * 导航 URL 编解码器。
1446
- * - `encode`:把导航树映射为 URL。
1447
- * - `decode`:把 URL 还原为导航树;无法(或无需)从 URL 同步还原时返回 `undefined`。
1448
- */
1449
- interface NavigationCodec {
1450
- encode(tree: NavigationNode, router: NavigationRouterLike): string;
1451
- decode(url: string, router: NavigationRouterLike): NavigationNode | undefined;
1452
- }
1453
- /** 默认结构化覆盖参数名(full-state 编码所用的保留 query key)。 */
1454
- declare const DEFAULT_NAV_PARAM = "__nav";
1455
- /**
1456
- * 把整棵树编码为紧凑、URL 安全、确定性的字符串。
1457
- * 用 `serializeNavigationStable`(keys 排序)保证相同树产出相同串,再做 base64url。
1458
- */
1459
- declare function encodeNavigationTreeParam(tree: NavigationNode): string;
1460
- /**
1461
- * 还原 `encodeNavigationTreeParam` 的输出为导航树;畸形输入抛 NavigationError。
1462
- */
1463
- declare function decodeNavigationTreeParam(encoded: string): NavigationNode;
1464
- /**
1465
- * 默认 codec:
1466
- * - `encode`:把激活叶子反查为 URL(`Router.reverse` 或路由摘要反查);激活叶子无对应路由时回退 `"/"`。
1467
- * - `decode`:仅在 URL 带 `__nav` 结构化覆盖时同步还原整棵树;否则返回 `undefined`,
1468
- * 交由调用方走 `await router.resolve(url)` 异步重建单个 LeafNode(今天的行为)。
1469
- */
1470
- declare function createActiveLeafCodec(): NavigationCodec;
1471
- /** full-state codec 选项 */
1472
- interface FullStateCodecOptions {
1473
- /** 保留 query 参数名(整树编码所用);默认 `__nav`。 */
1474
- readonly param?: string;
1475
- }
1476
- /**
1477
- * 整树 codec:把整棵树编码进保留 query 参数(默认 `__nav`),支持完整深链。
1478
- * - `encode`:以激活叶子的 URL 作为基底路径(保留 app 可能依赖的 path/query),
1479
- * 再写入保留参数承载整棵树。激活叶子无对应路由时基底退化为 `"/"`。
1480
- * - `decode`:读取保留参数无损还原整棵树;缺失该参数时返回 `undefined`(交由调用方走默认路径)。
1481
- */
1482
- declare function createFullStateCodec(options?: FullStateCodecOptions): NavigationCodec;
1483
- /**
1484
- * 扁平栈 codec —— flat-islands 用:URL ↔ 单叶栈。
1485
- *
1486
- * - `decode(url, router)`:同步把 URL 匹配成单叶意图,返回 `stack([leaf(intent, params)])`;
1487
- * 不可路由时返回 `undefined`(调用方保留当前树,与 createActiveLeafCodec 约定一致)。
1488
- * - `encode(tree, router)`:取激活叶子(activeLeaf)的 intent + params 反查 URL;
1489
- * 无对应路由时回退 `"/"`。
1490
- *
1491
- * 全部复用本模块已有 helper:`parseRouteSummaries`、`parseUrlParts`、`reverseUrl`、`activeLeaf`。
1492
- */
1493
- declare function createFlatStackCodec(): NavigationCodec;
1494
- //#endregion
1495
- //#region ../core/src/navigation/controller.d.ts
1496
- /** 导航操作 Kind 常量 */
1497
- declare const NAVIGATION_OP_KINDS: {
1498
- readonly PUSH: "push";
1499
- readonly POP: "pop";
1500
- readonly POP_TO_ROOT: "popToRoot";
1501
- readonly POP_TO: "popTo";
1502
- readonly REPLACE_TOP: "replaceTop";
1503
- readonly SELECT_TAB: "selectTab";
1504
- readonly SELECT_COLUMN: "selectColumn";
1505
- readonly SET_VISIBILITY: "setVisibility";
1506
- readonly HYDRATE: "hydrate";
1507
- };
1508
- /** 所有导航操作 Kind 的联合类型 */
1509
- type NavigationOpKind = (typeof NAVIGATION_OP_KINDS)[keyof typeof NAVIGATION_OP_KINDS];
1510
- /** push:在目标栈顶压入一个新 leaf(intent + params)。 */
1511
- interface PushOperation {
1512
- readonly kind: typeof NAVIGATION_OP_KINDS.PUSH;
1513
- readonly intent: string;
1514
- readonly params?: RouteParams;
1515
- readonly target?: NavigationPath;
1516
- }
1517
- /** pop:从目标栈弹出 count 个 entry(默认 1)。 */
1518
- interface PopOperation {
1519
- readonly kind: typeof NAVIGATION_OP_KINDS.POP;
1520
- readonly count?: number;
1521
- readonly target?: NavigationPath;
1522
- }
1523
- /** popToRoot:把目标栈弹回根 entry。 */
1524
- interface PopToRootOperation {
1525
- readonly kind: typeof NAVIGATION_OP_KINDS.POP_TO_ROOT;
1526
- readonly target?: NavigationPath;
1527
- }
1528
- /** popTo:把目标栈弹回指定 index。 */
1529
- interface PopToOperation {
1530
- readonly kind: typeof NAVIGATION_OP_KINDS.POP_TO;
1531
- readonly index: number;
1532
- readonly target?: NavigationPath;
1533
- }
1534
- /** replaceTop:替换目标栈的栈顶为新 leaf。 */
1535
- interface ReplaceTopOperation {
1536
- readonly kind: typeof NAVIGATION_OP_KINDS.REPLACE_TOP;
1537
- readonly intent: string;
1538
- readonly params?: RouteParams;
1539
- readonly target?: NavigationPath;
1540
- }
1541
- /** selectTab:切换 tabs 节点的激活分支。 */
1542
- interface SelectTabOperation {
1543
- readonly kind: typeof NAVIGATION_OP_KINDS.SELECT_TAB;
1544
- readonly key: string;
1545
- readonly target?: NavigationPath;
1546
- }
1547
- /** selectColumn:设置 split 某列内容(intent 为 undefined 表示清空该列)。 */
1548
- interface SelectColumnOperation {
1549
- readonly kind: typeof NAVIGATION_OP_KINDS.SELECT_COLUMN;
1550
- readonly columnId: string;
1551
- readonly intent: string | undefined;
1552
- readonly params?: RouteParams;
1553
- readonly target?: NavigationPath;
1554
- }
1555
- /** setVisibility:设置 split 节点的列可见性(对标 NavigationSplitViewVisibility)。 */
1556
- interface SetVisibilityOperation {
1557
- readonly kind: typeof NAVIGATION_OP_KINDS.SET_VISIBILITY;
1558
- readonly visibility: SplitVisibility;
1559
- readonly target?: NavigationPath;
1560
- }
1561
- /** hydrate:用外部给定的整棵树替换当前树(来自 history/URL 还原)。 */
1562
- interface HydrateOperation {
1563
- readonly kind: typeof NAVIGATION_OP_KINDS.HYDRATE;
1564
- readonly tree: NavigationNode;
1565
- }
1566
- /** 所有导航操作的可辨识联合。 */
1567
- type NavigationOperation = PushOperation | PopOperation | PopToRootOperation | PopToOperation | ReplaceTopOperation | SelectTabOperation | SelectColumnOperation | SetVisibilityOperation | HydrateOperation;
1568
- /**
1569
- * 控制器解析单个目标时需要的「环境」——由应用提供。
1570
- *
1571
- * 仓库里没有契约所说的 `IntentContext`:dispatch 需要 `Container`,守卫需要
1572
- * `NavigationContext`(含 url/cookie/header)。所以 `createContext` 在此被建模为
1573
- * 「给定目标 intent/params,返回构建守卫上下文 + 派发所需的零件」:
1574
- * - `container`:派发 intent 用(`intentDispatcher.dispatch(intent, container)`)。
1575
- * - `navigation`:完整的 `NavigationContext`(应用按 SSR/CSR 用
1576
- * `createServerContext`/`createBrowserContext` 造好传入);缺省时控制器用一个不含
1577
- * cookie/header 的最小上下文兜底(含 url/path/params/intent/container/isServer,
1578
- * 其中 isServer 取 `NavigationControllerOptions.isServer`,缺省按运行环境推断)。
1579
- *
1580
- * `signal` 暂无消费方(现有 runner 也没有 AbortSignal 管线),仅透传保留。
1581
- */
1582
- interface NavigationContextInput {
1583
- readonly intent: string;
1584
- readonly params: RouteParams;
1585
- readonly signal?: AbortSignal;
1586
- }
1587
- /** `createContext` 的返回:派发用的 Container + 守卫用的 NavigationContext(可选)。 */
1588
- interface NavigationDispatchContext {
1589
- /** DI 容器 —— intent 派发的必备参数。 */
1590
- readonly container: Container;
1591
- /** 守卫上下文;缺省时控制器用最小上下文兜底。 */
1592
- readonly navigation?: NavigationContext;
1593
- /** 该目标对应的完整 URL(用于最小兜底上下文的 url/path)。 */
1594
- readonly url?: string;
1595
- }
1596
- /** NavigationController 构造选项。 */
1597
- interface NavigationControllerOptions {
1598
- /** Intent 派发器(派发可见目标的 intent → page)。 */
1599
- readonly intentDispatcher: IntentDispatcher;
1600
- /** 路由器(beforeLoad rewrite/redirect 时把 URL 重解析为 leaf)。 */
1601
- readonly router: Router;
1602
- /** 初始导航树(单 LeafNode = 今天的扁平单页)。 */
1603
- readonly initial: NavigationNode;
1604
- /** 应用提供的「目标 → 派发上下文」构建回调。 */
1605
- readonly createContext: (input: NavigationContextInput) => NavigationDispatchContext;
1606
- /**
1607
- * 是否运行在服务端——仅用于 `createContext` 未返回 `navigation` 时的最小兜底上下文,
1608
- * 决定该上下文的 `isServer` 字段。缺省时按运行环境推断(`typeof window === "undefined"`)。
1609
- * 应用若已通过 `createContext` 提供完整 `navigation`,此项不生效。
1610
- */
1611
- readonly isServer?: boolean;
1612
- /** 目标级 beforeLoad 守卫(在全局/路由守卫之外,由控制器对主目标执行)。 */
1613
- readonly beforeLoad?: readonly BeforeLoadGuard[];
1614
- /** 目标级 afterLoad 守卫。 */
1615
- readonly afterLoad?: readonly AfterLoadGuard[];
1616
- /** SSR 预取缓存(浏览器 hydration 时复用服务端解析结果)。 */
1617
- readonly prefetched?: PrefetchedIntents;
1618
- /**
1619
- * 兜底错误页工厂——dispatch 失败 / deny 时,用它产出该目标的 page。
1620
- * 缺省用一个最小的 BasePage(pageType="error")。复刻 runner 的 fallback 语义。
1621
- */
1622
- readonly getErrorPage?: (status: number, message: string) => Page;
1623
- /**
1624
- * redirect 处理器——beforeLoad/afterLoad 返回 redirect 时调用(SPA 内跳 / 外链)。
1625
- * 控制器不持有 history,把「怎么跳」交给应用(浏览器侧 → `framework.perform`)。
1626
- * 缺省为 no-op(该目标不 dispatch、不再跳,仅保留当前页/兜底页)。
1627
- */
1628
- readonly onRedirect?: (redirect: {
1629
- url: string;
1630
- status: number;
1631
- }) => void;
1632
- }
1633
- /** 导航控制器对外接口。 */
1634
- interface NavigationController {
1635
- /** 当前导航树。 */
1636
- getTree(): NavigationNode;
1637
- /** 当前快照(树 + 已解析的可见目标)。 */
1638
- getSnapshot(): NavigationSnapshot;
1639
- /** 应用一个声明式操作,重解析并提交,返回新快照。 */
1640
- apply(op: NavigationOperation): Promise<NavigationSnapshot>;
1641
- /** 便捷:在激活栈压入新目标。 */
1642
- push(intent: string, params?: RouteParams, options?: PushOptions): Promise<NavigationSnapshot>;
1643
- /** 便捷:从激活栈弹出。 */
1644
- pop(count?: number): Promise<NavigationSnapshot>;
1645
- /** 便捷:激活栈弹回根。 */
1646
- popToRoot(): Promise<NavigationSnapshot>;
1647
- /** 便捷:替换激活栈栈顶。 */
1648
- replaceTop(intent: string, params?: RouteParams): Promise<NavigationSnapshot>;
1649
- /** 便捷:切换 tabs 激活分支。 */
1650
- selectTab(key: string, target?: NavigationPath): Promise<NavigationSnapshot>;
1651
- /** 便捷:设置 split 列内容(intent=undefined 清空)。 */
1652
- selectColumn(columnId: string, intent: string | undefined, params?: RouteParams, target?: NavigationPath): Promise<NavigationSnapshot>;
1653
- /** 便捷:设置 split 列可见性(对标 NavigationSplitViewVisibility);改变可见集会触发新可见列的派发。 */
1654
- setVisibility(visibility: SplitVisibility, target?: NavigationPath): Promise<NavigationSnapshot>;
1655
- /** 用外部树替换当前树并重解析(history/URL 还原)。 */
1656
- hydrate(tree: NavigationNode): Promise<NavigationSnapshot>;
1657
- /**
1658
- * 清除页面缓存:给 `entryKey`(= `sessionEntryKey(intent, params)`)清单个,
1659
- * 不传清全部。仅清缓存、不触发重解析——该条目下次被解析时重新 dispatch。
1660
- */
1661
- invalidate(entryKey?: string): void;
1662
- /** 清当前激活叶子的缓存并重解析当前树(「下拉刷新」式:守卫跑、数据重 fetch)。 */
1663
- refresh(): Promise<NavigationSnapshot>;
1664
- /** 订阅快照变更;返回取消订阅函数。 */
1665
- subscribe(listener: (snapshot: NavigationSnapshot) => void): () => void;
1666
- /** 解析当前树(首屏 SSR/CSR),提交并返回快照。 */
1667
- resolve(): Promise<NavigationSnapshot>;
1668
- }
1669
- /** `push` 便捷方法的可选项。 */
1670
- interface PushOptions {
1671
- readonly target?: NavigationPath;
1672
- }
1673
- declare function createNavigationController(options: NavigationControllerOptions): NavigationController;
1674
- //#endregion
1675
- //#region ../core/src/bootstrap/define-navigation.d.ts
1676
- /**
1677
- * `initial` 既可是一棵静态初始树,也可是按 URL 产出树骨架的工厂。
1678
- *
1679
- * - 静态树:所有请求(CSR 首屏 / SSR 无深链回退)都以这棵树为初始结构。
1680
- * - 工厂 `(url) => NavigationNode | undefined`:按请求 URL 动态决定骨架;返回 `undefined`
1681
- * 表示「此 URL 无结构化骨架」,SSR 侧据此回退到「`Router.resolve` → 单 LeafNode」
1682
- * (今天的单页行为)。CSR 侧首屏对工厂传入当前 `window.location` 的 path+query。
1683
- */
1684
- type NavigationInitial = NavigationNode | ((url: string) => NavigationNode | undefined);
1685
- /**
1686
- * 浏览器 runner(`startBrowserApp`)期望的导航配置形态。
1687
- *
1688
- * 与 `@finesoft/browser` 的 `BrowserNavigationConfig` 结构等价(`initial` 为具体树);
1689
- * 在 core 中以结构化形状声明,避免 core → browser 的反向依赖。
1690
- */
1691
- interface NavigationBrowserConfig {
1692
- readonly initial: NavigationNode;
1693
- readonly codec?: NavigationCodec;
1694
- readonly beforeLoad?: readonly BeforeLoadGuard[];
1695
- readonly afterLoad?: readonly AfterLoadGuard[];
1696
- readonly getErrorPage?: (status: number, message: string) => BasePage;
1697
- }
1698
- /**
1699
- * SSR runner(`createSSRNavigationRender` / `ssrRenderNavigation`)期望的导航定义形态。
1700
- *
1701
- * 与 `@finesoft/ssr` 的 `SSRNavigationDefinition` 结构等价(`codec` 必填、`initial`
1702
- * 为骨架工厂);在 core 中以结构化形状声明,避免 core → ssr 的反向依赖。
1703
- */
1704
- interface NavigationSSRDefinition {
1705
- readonly codec: NavigationCodec;
1706
- readonly initial?: (url: string) => NavigationNode | undefined;
1707
- readonly beforeLoad?: readonly BeforeLoadGuard[];
1708
- readonly afterLoad?: readonly AfterLoadGuard[];
1709
- }
1710
- /** `defineNavigation` 的输入声明。 */
1711
- interface DefineNavigationOptions {
1712
- /**
1713
- * 初始导航结构:静态树或按 URL 产出树骨架的工厂。
1714
- * 单个 `leaf(...)` 树即为今天的扁平单页(向后兼容)。
1715
- */
1716
- readonly initial: NavigationInitial;
1717
- /**
1718
- * URL ⇄ 树 编解码器;缺省 `createActiveLeafCodec()`
1719
- * (URL 只反映激活叶子,整树通过 history/hydration 旁路)。
1720
- */
1721
- readonly codec?: NavigationCodec;
1722
- /** 导航级 beforeLoad 守卫(控制器对主目标执行,叠加在全局/路由守卫之外)。 */
1723
- readonly beforeLoad?: readonly BeforeLoadGuard[];
1724
- /** 导航级 afterLoad 守卫。 */
1725
- readonly afterLoad?: readonly AfterLoadGuard[];
1726
- /** dispatch 失败 / deny 时的兜底错误页工厂(仅 CSR runner 直接消费;SSR runner 用其自带的 getErrorPage)。 */
1727
- readonly getErrorPage?: (status: number, message: string) => BasePage;
1728
- }
1729
- /**
1730
- * `defineNavigation` 的产物:规范化后的导航定义。
1731
- *
1732
- * 既暴露规范化字段(应用可自取),也提供两个适配器把定义转成各 runner 需要的精确形态。
1733
- * 字段全部 `readonly`、不可变。
1734
- */
1735
- interface NavigationDefinition {
1736
- /** 规范化的初始结构(静态树或工厂)。 */
1737
- readonly initial: NavigationInitial;
1738
- /** 最终生效的 codec(已套用默认值)。 */
1739
- readonly codec: NavigationCodec;
1740
- /** 导航级 beforeLoad 守卫。 */
1741
- readonly beforeLoad?: readonly BeforeLoadGuard[];
1742
- /** 导航级 afterLoad 守卫。 */
1743
- readonly afterLoad?: readonly AfterLoadGuard[];
1744
- /** 兜底错误页工厂。 */
1745
- readonly getErrorPage?: (status: number, message: string) => BasePage;
1746
- /**
1747
- * 适配为浏览器 runner 配置(`initial` 收敛为具体树)。
1748
- * `initial` 是工厂时,对 `url`(缺省当前 `window.location`)求值;返回 `undefined`
1749
- * 时回退到一个最小的占位 leaf(`@finesoft/navigation-root`),保证 bridge 能挂载——
1750
- * 浏览器首屏随后会用 SSR 注入的真实树 hydrate(见 navigation-bridge)。
1751
- */
1752
- toBrowserConfig(url?: string): NavigationBrowserConfig;
1753
- /** 适配为 SSR runner 定义(`initial` 收敛为骨架工厂、`codec` 必填)。 */
1754
- toSSRDefinition(): NavigationSSRDefinition;
1755
- }
1756
- /**
1757
- * 声明结构化导航。
1758
- *
1759
- * 在 `bootstrap(framework)` 里与 `defineRoutes` 并列调用,返回一个 `NavigationDefinition`,
1760
- * 由应用分别交给 CSR / SSR runner:
1761
- *
1762
- * @example
1763
- * ```ts
1764
- * const nav = defineNavigation({
1765
- * initial: tabs({
1766
- * active: "home",
1767
- * branches: { home: stack(leaf("home")), me: stack(leaf("me")) },
1768
- * }),
1769
- * beforeLoad: [authGuard],
1770
- * });
1771
- *
1772
- * // CSR
1773
- * startBrowserApp({ bootstrap, mount, callbacks, navigation: nav.toBrowserConfig() });
1774
- * // SSR
1775
- * createSSRNavigationRender({ bootstrap, getErrorPage, renderApp, navigation: nav.toSSRDefinition() });
1776
- * ```
1777
- */
1778
- declare function defineNavigation(options: DefineNavigationOptions): NavigationDefinition;
1779
- //#endregion
1780
- //#region ../core/src/bootstrap/define-routes.d.ts
1781
- /** 渲染模式 */
1782
- type RenderMode = "ssr" | "csr" | "prerender";
1783
- /** 单条路由定义 */
1784
- interface RouteDefinition<Path extends string = string, P extends ParamsFor<Path> = ParamsFor<Path>, Q extends QuerySchemaMap = QuerySchemaMap> {
1785
- /** URL pattern (如 "/product/:id") */
1786
- path: Path;
1787
- /** Intent ID */
1788
- intentId: string;
1789
- /** Controller 实例(可选)。同一 intentId 的多条路由只需在第一条提供。 */
1790
- controller?: IntentController;
1791
- /** path 参数 codec;key 必须是 path 中出现的 :param 名 */
1792
- params?: P;
1793
- /** query 参数 codec;key 自由 */
1794
- query?: Q;
1795
- /** 渲染模式(可选,默认 "ssr") */
1796
- renderMode?: RenderMode;
1797
- /** 路由级 beforeLoad 守卫 */
1798
- beforeLoad?: BeforeLoadGuard[];
1799
- /** 路由级 afterLoad 守卫 */
1800
- afterLoad?: AfterLoadGuard[];
1801
- }
1802
- /**
1803
- * 构造一条强类型路由定义。
1804
- * `params` 的 key 受 `path` 字面量约束——写入 path 中不存在的参数名会编译期报错。
1805
- *
1806
- * @example
1807
- * route("/product/:id", { intentId: "product", controller, params: { id: int() } })
1808
- */
1809
- declare function route<const Path extends string, P extends ParamsFor<Path> = ParamsFor<Path>, Q extends QuerySchemaMap = QuerySchemaMap>(path: Path, def: {
1810
- intentId: string;
1811
- controller?: IntentController;
1812
- params?: P;
1813
- query?: Q;
1814
- renderMode?: RenderMode;
1815
- beforeLoad?: BeforeLoadGuard[];
1816
- afterLoad?: AfterLoadGuard[];
1817
- }): RouteDefinition;
1818
- /** defineRoute 的 handler 入参类型:path 参数 + query 经 codec 推导后合并(免手写)。 */
1819
- type HandlerParams<P, Q> = InferParams<{ [K in keyof P]: P[K] extends ParamSchema ? P[K] : never }> & InferQuery<{ [K in keyof Q]: Q[K] extends QuerySchemaMap[string] ? Q[K] : never }>;
1820
- /**
1821
- * 构造一条强类型路由定义,**handler 的入参类型从 `path` + codec 自动推导**——免手写
1822
- * `BaseController<InferParams<typeof ...> & InferQuery<typeof ...>>`。
1823
- *
1824
- * 与 `route()`(类 controller)互补:用 handler 函数代替 `BaseController` 子类,框架据
1825
- * `params`/`query` codec 自动算出 handler 入参类型;内部把 handler 合成为 `IntentController`,
1826
- * 复刻 `BaseController` 的 try/catch → fallback 行为。`params` 的 key 同样受 `path` 字面量约束。
1827
- *
1828
- * @example
1829
- * route 同款的强类型,但无需声明 controller 类:
1830
- * ```ts
1831
- * defineRoute("/product/:id", {
1832
- * intentId: "product",
1833
- * params: { id: int() },
1834
- * query: { page: withDefault(int(), 1) },
1835
- * handler: (params) => ({ ... }), // params: { id: number; page: number },自动推导
1836
- * })
1837
- * ```
1838
- */
1839
- declare function defineRoute<const Path extends string, P extends ParamsFor<Path> = ParamsFor<Path>, Q extends QuerySchemaMap = QuerySchemaMap, TResult = unknown>(path: Path, def: {
1840
- intentId: string;
1841
- params?: P;
1842
- query?: Q;
1843
- handler: (params: HandlerParams<P, Q>, container: Container) => TResult | Promise<TResult>;
1844
- fallback?: (params: HandlerParams<P, Q>, error: Error) => TResult | Promise<TResult>;
1845
- renderMode?: RenderMode;
1846
- beforeLoad?: BeforeLoadGuard[];
1847
- afterLoad?: AfterLoadGuard[];
1848
- }): RouteDefinition;
1849
- /** defineRoutes 选项 */
1850
- interface DefineRoutesOptions {
1851
- /**
1852
- * 支持的 locale 列表。
1853
- * 提供后,每条路由会额外注册 `/:locale/path` 版本,
1854
- * `:locale` 参数自动出现在 `intent.params.locale` 中。
1855
- * 原始无前缀路径保留作为备选路由。
1856
- *
1857
- * @example
1858
- * ```ts
1859
- * defineRoutes(framework, routes, { locales: ["zh", "en", "ja"] });
1860
- * // "/about" → 注册 /about + /zh/about + /en/about + /ja/about
1861
- * ```
1862
- */
1863
- locales?: string[];
1864
- }
1865
- /**
1866
- * 数组形态 6c 一致性的载体:把一条路由的 `params` 按其自身 `path` 字面量重新约束 key。
1867
- *
1868
- * 不在 path 参数名集合内的 key 显式映射为 `never`——结构化赋值不会对非 fresh 对象做
1869
- * excess-property 检查(`route()` 单数 helper 靠 fresh 字面量 + `ParamsFor` 拿到 excess
1870
- * 检查,但数组元素流经泛型推断后不再是 fresh,故改用 never 映射),使其值(`ParamSchema`)
1871
- * 不可赋给 `never` 而编译期报错。
1872
- */
1873
- type ValidateRouteParams<Path extends string, Params> = { [K in keyof Params]: K extends ExtractParamNames<Path> ? Params[K] : never };
1874
- /**
1875
- * 仅当元素是「带 params 的对象字面量」时重写其 params 约束;无 params 的路由与 `route()`
1876
- * 输出(`params` 可选、Path 已擦除为 string)走 false 分支,原样透传、不被过度约束。
1877
- */
1878
- type ValidateRouteDef<R> = R extends {
1879
- path: infer P extends string;
1880
- params: infer Params;
1881
- } ? Omit<R, "params"> & {
1882
- params: ValidateRouteParams<P, Params>;
1883
- } : R;
1884
- /**
1885
- * 声明式注册路由和 Controller
1886
- *
1887
- * - 自动去重: 同一 intentId 的 controller 只注册一次
1888
- * - 路由和 controller 在同一个配置数组中,方便检查一致性
1889
- * - **数组形态 6c**: 每条对象字面量路由的 `params` key 受其自身 `path` 字面量约束,
1890
- * 写入 path 中不存在的参数名会编译期报错(与 `route()` 单数 helper 同等保证)。
1891
- *
1892
- * @example
1893
- * ```ts
1894
- * defineRoutes(framework, [
1895
- * { path: "/", intentId: "home", controller: new HomeController() },
1896
- * { path: "/product/:id", intentId: "product", controller: new ProductController() },
1897
- * { path: "/search", intentId: "search", controller: new SearchController() },
1898
- * { path: "/charts/:type", intentId: "charts", controller: new ChartsController() },
1899
- * { path: "/charts", intentId: "charts" }, // 同 intentId,不需要重复 controller
1900
- * ]);
1901
- * ```
1902
- */
1903
- declare function defineRoutes<const T extends readonly RouteDefinition[]>(framework: Framework, definitions: { [I in keyof T]: ValidateRouteDef<T[I]> }, options?: DefineRoutesOptions): void;
1904
- //#endregion
1905
- //#region ../core/src/session/types.d.ts
1906
- /** 会话快照在 Storage 中的默认键。 */
1907
- declare const SESSION_DEFAULT_KEY = "__finesoft_session__";
1908
- /** 会话快照的默认版本号;解码时不匹配即整份丢弃。 */
1909
- declare const SESSION_DEFAULT_VERSION = 1;
1910
- /** 扁平单页的导航位置:一个 URL(区别于结构化树的 `SerializedNavigation`)。 */
1911
- interface SessionUrlLocation {
1912
- readonly url: string;
1913
- }
1914
- /**
1915
- * 会话快照:用户「当时在干什么」的可序列化捕获。
1916
- *
1917
- * `navigation` 用一个轻判别区分两种导航形态:`SerializedNavigation` 自带 `kind`
1918
- * (leaf/stack/tabs/split),`SessionUrlLocation` 用独有的 `url` 字段(见 `isUrlLocation`)。
1919
- */
1920
- interface SessionSnapshot {
1921
- /** 快照版本;解码时与期望版本不符即丢弃。 */
1922
- readonly version: number;
1923
- /** 导航位置:结构化 → `SerializedNavigation`;扁平 → `SessionUrlLocation`;缺省 → 不恢复导航。 */
1924
- readonly navigation?: SerializedNavigation | SessionUrlLocation;
1925
- /**
1926
- * 该快照导航位置的可比 URL(捕获时刻与 history 同步的浏览器 URL),供恢复门控做精确匹配。
1927
- *
1928
- * 扁平与结构化适配器均可在 `capture` 时记录(见 `SessionNavigationAdapter.captureUrl`)。
1929
- * 缺省时(旧快照 / 适配器不提供)门控回退到旧策略:扁平比 `nav.url`、结构化只在根放行。
1930
- * 它让结构化导航也能像扁平一样「重载同深链即恢复、改去别的深链则跳过」(对称)。
1931
- */
1932
- readonly url?: string;
1933
- /** 全局切片(app-wide):`provider.key` → 该 provider `capture()` 的 JSON 值。 */
1934
- readonly slices: Readonly<Record<string, unknown>>;
1935
- /** 导航作用域状态:`entryKey` → 该导航条目的状态袋;条目离树即被 prune 丢弃。 */
1936
- readonly scoped: Readonly<Record<string, unknown>>;
1937
- /** 捕获时刻(epoch ms);用于 `maxAgeMs` 过期判断。 */
1938
- readonly capturedAt: number;
1939
- }
1940
- /**
1941
- * 判别 `navigation` 是否为扁平 URL 位置。
1942
- *
1943
- * `SerializedNavigation` 始终带 `kind`、从不带 `url` 字段,故 `url` 是无歧义判别位。
1944
- */
1945
- declare function isUrlLocation(nav: SessionSnapshot["navigation"]): nav is SessionUrlLocation;
1946
- /**
1947
- * 全局状态切片 Provider。
1948
- *
1949
- * 同步、JSON 安全。框架不解释切片内容 —— 它只搬运。应用控制捕获什么
1950
- * (敏感字段在 `capture()` 中自行排除)。
1951
- */
1952
- interface SessionStateProvider<T = unknown> {
1953
- /** 切片唯一键(快照里 `slices` 的 key)。 */
1954
- readonly key: string;
1955
- /** 捕获当前切片状态,必须返回 JSON 安全的同步值。 */
1956
- capture(): T;
1957
- /** 用持久化的切片数据恢复(应用自行 setState / 填表单 / 滚动)。 */
1958
- restore(data: T): void;
1959
- }
1960
- /**
1961
- * 导航作用域状态:`entryKey` → 状态袋;条目离树由框架 prune 丢弃(见 `scoped-state.ts`)。
1962
- */
1963
- interface NavigationScopedState {
1964
- /** 读取某条目的状态袋(不存在返回 `undefined`,`unknown` 已含此情形)。 */
1965
- get(entryKey: string): unknown;
1966
- /** 写入某条目的状态袋。 */
1967
- set(entryKey: string, data: unknown): void;
1968
- /** 删除某条目的状态袋。 */
1969
- delete(entryKey: string): void;
1970
- /** 仅保留 `presentKeys` 中的键,丢弃其余(导航提交后由 bridge 调用)。 */
1971
- prune(presentKeys: Iterable<string>): void;
1972
- /** 当前持有状态的全部条目键。 */
1973
- keys(): readonly string[];
1974
- }
1975
- /**
1976
- * 导航适配器:SessionStore 与具体导航机制(结构化 controller / 扁平 URL)解耦的接缝。
1977
- *
1978
- * SessionStore 不直接依赖 `NavigationController`,core 不产生 nav → session 的反向耦合;
1979
- * 扁平与结构化导航经此同一套机制覆盖(见 `navigation-adapter.ts`)。
1980
- */
1981
- interface SessionNavigationAdapter {
1982
- /** 捕获当前导航位置。 */
1983
- capture(): SessionSnapshot["navigation"] | undefined;
1984
- /** 应用恢复的导航位置。 */
1985
- apply(navigation: SessionSnapshot["navigation"]): void | Promise<void>;
1986
- /**
1987
- * 可选:计算当前导航位置的可比 URL,写入 `SessionSnapshot.url` 供恢复门控精确匹配。
1988
- *
1989
- * 浏览器侧适配器返回当时的 `location`(pushState 后与导航树同步);返回 `undefined`
1990
- * 或不实现 = 快照不带 `url`,门控回退旧策略(见 `defaultShouldRestore`)。
1991
- */
1992
- captureUrl?(): string | undefined;
1993
- /** 树中**存在**的全部条目身份键(用于 scoped prune;「存在」非「可见」)。 */
1994
- presentKeys(): Iterable<string>;
1995
- }
1996
- /** 会话错误上下文:标记出错所处阶段,供 `onError` 上报。 */
1997
- interface SessionErrorContext {
1998
- readonly phase: "capture" | "restore" | "persist" | "load";
1999
- readonly key?: string;
2000
- }
2001
- /** `createSessionStore` 选项。 */
2002
- interface SessionStoreOptions {
2003
- /** 持久化存储(`DEP_KEYS.STORAGE`)。 */
2004
- readonly storage: Storage;
2005
- /** 快照键;默认 `SESSION_DEFAULT_KEY`。 */
2006
- readonly key?: string;
2007
- /** 快照版本;默认 `SESSION_DEFAULT_VERSION`,不符即丢弃。 */
2008
- readonly version?: number;
2009
- /** 快照最大存活时长(ms);省略 = 不过期。 */
2010
- readonly maxAgeMs?: number;
2011
- /** 导航适配器;省略 = 不恢复导航。 */
2012
- readonly navigation?: SessionNavigationAdapter;
2013
- /** 注入时钟(测试 / SSR 安全);默认 `() => Date.now()`。 */
2014
- readonly now?: () => number;
2015
- /** 错误回调;默认 no-op,应用可接 EventRecorder。 */
2016
- readonly onError?: (error: unknown, ctx: SessionErrorContext) => void;
2017
- }
2018
- /** 会话编排器:组装 / 落盘 / 读取 / 恢复快照,并持有导航作用域状态。 */
2019
- interface SessionStore {
2020
- /** 注册全局切片 provider;返回反注册函数。 */
2021
- register(provider: SessionStateProvider): () => void;
2022
- /** 导航作用域状态读写 + prune。 */
2023
- readonly scope: NavigationScopedState;
2024
- /** 组装当前快照(nav + slices + scoped),不落盘。 */
2025
- capture(): SessionSnapshot;
2026
- /** 落盘(省略参数则先 `capture`)。 */
2027
- persist(snapshot?: SessionSnapshot): void;
2028
- /** 从 Storage 读取并校验(version / maxAge / 畸形 → `undefined`)。 */
2029
- load(): SessionSnapshot | undefined;
2030
- /** 恢复:应用 nav + 回填 scoped + 派发各 slice 给对应 provider(省略则先 `load`)。 */
2031
- restore(snapshot?: SessionSnapshot): void | Promise<void>;
2032
- /** 清除持久化快照。 */
2033
- clear(): void;
2034
- /** 手动逃生口 = `capture` + `persist`。 */
2035
- save(): void;
2036
- }
2037
- /** 会话错误:序列化 / 编排过程中需要显式标识的错误类型。 */
2038
- declare class SessionError extends Error {
2039
- constructor(message: string);
2040
- }
2041
- //#endregion
2042
- //#region ../core/src/session/snapshot.d.ts
2043
- /** 把快照编码为确定性字符串(keys 排序),用作 `storage.set` 的值。 */
2044
- declare function encodeSnapshot(snapshot: SessionSnapshot): string;
2045
- /**
2046
- * 解码并校验快照。
2047
- *
2048
- * 校验:`version === expectedVersion`、`slices`/`scoped` 为对象、`capturedAt` 为数值;
2049
- * `navigation` 可缺省。任一不符或解析失败 → `undefined`(永不抛)。
2050
- */
2051
- declare function decodeSnapshot(raw: string | undefined, expectedVersion: number): SessionSnapshot | undefined;
2052
- //#endregion
2053
- //#region ../core/src/session/scoped-state.d.ts
2054
- /**
2055
- * 导航条目身份键:`intent + " " + stableStringify(params)`。
2056
- *
2057
- * 与 controller 的目标键同源、跨重载稳定(`stableStringify` 对 params 键排序,
2058
- * 故 `{a,b}` 与 `{b,a}` 产出同一键)。
2059
- */
2060
- declare function sessionEntryKey(intent: string, params: RouteParams): string;
2061
- /**
2062
- * 收集导航树中**全部 leaf** 的身份键(含不可见 / 未激活分支 / 各 split 列)。
2063
- * 委派给 `collectAllLeaves`(同一「全部存在」遍历),映射成 entryKey。
2064
- * 「全部存在」而非「可见」,用于 scoped 状态保留。
2065
- */
2066
- declare function collectLeafKeys(tree: NavigationNode): string[];
2067
- /**
2068
- * 创建导航作用域状态容器。
2069
- *
2070
- * 内部持一个 `Map`;`prune` 由 `presentKeys` 构造 `Set`,删除不在其中的全部键。
2071
- * `initial` 在构造时浅拷贝进 `Map`,构造后改动源对象不会泄漏进容器。
2072
- */
2073
- declare function createNavigationScopedState(initial?: Record<string, unknown>): NavigationScopedState;
2074
- //#endregion
2075
- //#region ../core/src/session/session-store.d.ts
2076
- /**
2077
- * 创建会话编排器。
2078
- *
2079
- * `scope` 是一个 `createNavigationScopedState()` 实例,由 store 持有;`restore` 用快照
2080
- * 的 `scoped` 重建其内容。时钟 `now` 注入(默认 `() => Date.now()`),`capturedAt` 由它产出。
2081
- */
2082
- declare function createSessionStore(options: SessionStoreOptions): SessionStore;
2083
- //#endregion
2084
- //#region ../core/src/session/navigation-adapter.d.ts
2085
- /**
2086
- * 结构化导航适配器:把 `NavigationController` 接到会话编排器。
2087
- *
2088
- * `apply` 仅处理 `SerializedNavigation`;`SessionUrlLocation` 与 `undefined` 一律 no-op
2089
- * (结构化应用始终捕获一棵树,不会落到 URL 形态)。
2090
- *
2091
- * `currentUrl`(可选):捕获时刻读取的浏览器 URL(pushState 后与导航树同步),写入快照
2092
- * `url` 字段供恢复门控精确匹配 —— 让结构化导航也能像扁平一样「重载同深链即恢复、改去别的
2093
- * 深链则跳过」。省略时快照不带 `url`,门控回退到「只在根放行」的旧策略。
2094
- */
2095
- declare function createNavigationSessionAdapter(controller: NavigationController, currentUrl?: () => string): SessionNavigationAdapter;
2096
- /** `createUrlSessionAdapter` 选项(扁平单页)。 */
2097
- interface UrlAdapterOptions {
2098
- /** 读取当前 URL(如 `() => location.pathname + location.search`)。 */
2099
- readonly currentUrl: () => string;
2100
- /** 应用恢复的 URL(应用提供,如 `framework.perform(makeFlowAction(url))`)。 */
2101
- readonly navigate: (url: string) => void | Promise<void>;
2102
- /** 可选:当前屏的 intent + params,用于 `presentKeys` 产出稳定身份键。 */
2103
- readonly currentIntent?: () => {
2104
- intent: string;
2105
- params: RouteParams;
2106
- };
2107
- }
2108
- /**
2109
- * 扁平 URL 适配器:把单页 URL 接到会话编排器。
2110
- *
2111
- * `apply` 仅处理 `SessionUrlLocation`;`SerializedNavigation` 与 `undefined` 一律 no-op
2112
- * (扁平只认 URL 形态)。`presentKeys` 恒为单条目:有 `currentIntent` 时用
2113
- * `sessionEntryKey(intent, params)`,否则退化为当前 URL 字符串。
2114
- */
2115
- declare function createUrlSessionAdapter(opts: UrlAdapterOptions): SessionNavigationAdapter;
2116
- //#endregion
2117
- //#region ../core/src/utils/lru-map.d.ts
2118
- /**
2119
- * LruMap — 固定容量的 LRU 缓存
2120
- */
2121
- declare class LruMap<K, V> {
2122
- private map;
2123
- private readonly capacity;
2124
- constructor(capacity: number);
2125
- get(key: K): V | undefined;
2126
- set(key: K, value: V): void;
2127
- has(key: K): boolean;
2128
- delete(key: K): boolean;
2129
- get size(): number;
2130
- clear(): void;
2131
- }
2132
- //#endregion
2133
- //#region ../core/src/utils/optional.d.ts
2134
- /**
2135
- * Optional 类型工具
2136
- */
2137
- type None = null | undefined;
2138
- type Optional<T> = T | None;
2139
- declare function isSome<T>(value: Optional<T>): value is T;
2140
- declare function isNone<T>(value: Optional<T>): value is None;
2141
- //#endregion
2142
- //#region ../core/src/utils/pwa.d.ts
2143
- /**
2144
- * PWA Display Mode 检测
2145
- *
2146
- * 检测当前应用是否以 PWA 模式运行。
2147
- */
2148
- type PWADisplayMode = "standalone" | "twa" | "browser";
2149
- /**
2150
- * 检测 PWA display mode
2151
- *
2152
- * - `standalone`: 已安装的 PWA(通过 Add to Home Screen)
2153
- * - `twa`: Trusted Web Activity(Android 原生壳)
2154
- * - `browser`: 普通浏览器标签页
2155
- */
2156
- declare function getPWADisplayMode(): PWADisplayMode;
2157
- //#endregion
2158
- //#region ../core/src/utils/url.d.ts
2159
- /**
2160
- * URL 工具函数
2161
- */
2162
- /** 移除 URL scheme (https://, http://) */
2163
- declare function removeScheme(url: string): string;
2164
- /** 移除 URL host 部分,保留路径 */
2165
- declare function removeHost(url: string): string;
2166
- /** 移除 query 参数 */
2167
- declare function removeQueryParams(url: string): string;
2168
- /** 获取 URL 的基础路径(无 query、hash) */
2169
- declare function getBaseUrl(url: string): string;
2170
- /** 构建 URL(路径 + query 参数) */
2171
- declare function buildUrl(path: string, params?: Record<string, string | undefined>): string;
2172
- //#endregion
2173
- //#region ../core/src/utils/uuid.d.ts
2174
- /**
2175
- * UUID v4 生成器
2176
- */
2177
- declare function generateUuid(): string;
2178
- //#endregion
2179
- //#region ../core/src/middleware/context.d.ts
2180
- interface ServerContextOptions {
2181
- url: string;
2182
- intent: Intent;
2183
- container: Container;
2184
- /** 原始 Request 对象(提取 cookie 和 header) */
2185
- request?: Request;
2186
- }
2187
- /** 从 Request 对象构建服务端上下文 */
2188
- declare function createServerContext(options: ServerContextOptions): NavigationContext;
2189
- interface BrowserContextOptions {
2190
- url: string;
2191
- intent: Intent;
2192
- container: Container;
2193
- }
2194
- /** 从 document.cookie 构建浏览器端上下文 */
2195
- declare function createBrowserContext(options: BrowserContextOptions): NavigationContext;
2196
- //#endregion
2197
- //#region ../core/src/middleware/pipeline.d.ts
2198
- /** 执行 beforeLoad 守卫链 */
2199
- declare function runBeforeLoadGuards(guards: BeforeLoadGuard[], ctx: NavigationContext): Promise<MiddlewareResult>;
2200
- /** 执行 afterLoad 守卫链 */
2201
- declare function runAfterLoadGuards(guards: AfterLoadGuard[], ctx: PostLoadContext): Promise<MiddlewareResult>;
2202
- //#endregion
2203
- //#region ../core/src/metrics/composite-recorder.d.ts
2204
- declare class CompositeEventRecorder implements EventRecorder {
2205
- private readonly recorders;
2206
- constructor(recorders: EventRecorder[]);
2207
- record(type: string, fields?: Record<string, unknown>): void;
2208
- flush(): Promise<void>;
2209
- destroy(): void;
2210
- }
2211
- //#endregion
2212
- //#region ../core/src/metrics/console-recorder.d.ts
2213
- declare class ConsoleEventRecorder implements EventRecorder {
2214
- private readonly prefix;
2215
- constructor(prefix?: string);
2216
- record(type: string, fields?: Record<string, unknown>): void;
2217
- flush(): Promise<void>;
2218
- destroy(): void;
2219
- }
2220
- //#endregion
2221
- //#region ../core/src/metrics/impression-observer.d.ts
2222
- interface ImpressionObserverOptions {
2223
- /** 可见比例阈值(0~1),默认 0.5 */
2224
- threshold?: number;
2225
- /** 最小可见时长(毫秒),默认 1000 */
2226
- minVisibleDuration?: number;
2227
- }
2228
- declare class IntersectionImpressionObserver implements ImpressionObserver {
2229
- private readonly observer;
2230
- private readonly tracked;
2231
- private readonly captured;
2232
- private readonly minDuration;
2233
- constructor(options?: ImpressionObserverOptions);
2234
- observe(element: Element, id: string, metadata?: Record<string, unknown>): void;
2235
- unobserve(element: Element): void;
2236
- consume(): ImpressionEntry[];
2237
- destroy(): void;
2238
- }
2239
- //#endregion
2240
- //#region ../core/src/metrics/void-recorder.d.ts
2241
- declare class VoidEventRecorder implements EventRecorder {
2242
- record(): void;
2243
- flush(): Promise<void>;
2244
- destroy(): void;
2245
- }
2246
- //#endregion
2247
- //#region ../core/src/metrics/with-fields-recorder.d.ts
2248
- declare class WithFieldsRecorder implements EventRecorder {
2249
- private readonly inner;
2250
- private readonly providers;
2251
- constructor(inner: EventRecorder, providers: MetricsFieldsProvider[]);
2252
- record(type: string, fields?: Record<string, unknown>): void;
2253
- flush(): Promise<void>;
2254
- destroy(): void;
2255
- }
2256
- //#endregion
2257
- //#region ../core/src/i18n/interpolate.d.ts
2258
- /**
2259
- * ICU 消息格式插值
2260
- *
2261
- * 支持 `{name}` 占位符替换和基础复数规则。
2262
- */
2263
- /** 将 `{key}` 占位符替换为 values 中的对应值 */
2264
- declare function interpolate(template: string, values?: Record<string, string | number>): string;
2265
- /**
2266
- * CLDR 复数类别
2267
- *
2268
- * 简化版:覆盖 zero / one / two / few / many / other。
2269
- * 完整 CLDR 规则可通过 PluralRuleProvider 注入。
2270
- */
2271
- type PluralCategory = "zero" | "one" | "two" | "few" | "many" | "other";
2272
- /** 复数规则函数 — 给定数量返回复数类别 */
2273
- type PluralRuleProvider = (count: number) => PluralCategory;
2274
- /**
2275
- * 英语复数规则(默认)
2276
- * 0 → other, 1 → one, 2+ → other
2277
- */
2278
- declare function englishPlural(count: number): PluralCategory;
2279
- /**
2280
- * 解析带复数后缀的翻译 key
2281
- *
2282
- * 约定: `key.one`, `key.other`, `key.zero`, etc.
2283
- */
2284
- declare function resolvePluralKey(key: string, category: PluralCategory): string;
2285
- //#endregion
2286
- //#region ../core/src/i18n/locale.d.ts
2287
- /** 检测语言是否为 RTL */
2288
- declare function isRtl(language: string): boolean;
2289
- /** 获取文本方向 */
2290
- declare function getTextDirection(language: string): TextDirection;
2291
- /**
2292
- * 从语言代码生成 HTML lang/dir 属性
2293
- *
2294
- * @example
2295
- * ```ts
2296
- * getLocaleAttributes("ar-SA") // { lang: "ar-SA", dir: "rtl" }
2297
- * getLocaleAttributes("en-US") // { lang: "en-US", dir: "ltr" }
2298
- * ```
2299
- */
2300
- declare function getLocaleAttributes(language: string): LocaleAttributes;
2301
- /**
2302
- * 构建 LocaleInfo
2303
- *
2304
- * @param language - 语言代码(如 "zh-Hans")
2305
- * @param region - 地区代码(如 "CN"),可选
2306
- */
2307
- declare function makeLocaleInfo(language: string, region?: string): LocaleInfo;
2308
- /**
2309
- * 将 locale 属性应用到 `<html>` 元素
2310
- *
2311
- * 服务端渲染时可用于字符串拼接,浏览器端直接操作 DOM。
2312
- */
2313
- declare function setHtmlLocaleAttributes(attrs: LocaleAttributes): void;
2314
- /**
2315
- * 从 URL 前缀中提取 locale
2316
- *
2317
- * @param url - 请求 URL(如 "/zh/about")
2318
- * @param supportedLocales - 支持的 locale 列表(如 ["zh", "en", "ja"])
2319
- * @returns 匹配时返回 `{ locale, strippedUrl }`,不匹配返回 null
2320
- *
2321
- * @example
2322
- * ```ts
2323
- * resolveLocaleFromUrl("/zh/about", ["zh", "en"])
2324
- * // → { locale: "zh", strippedUrl: "/about" }
2325
- *
2326
- * resolveLocaleFromUrl("/about", ["zh", "en"])
2327
- * // → null
2328
- * ```
2329
- */
2330
- declare function resolveLocaleFromUrl(url: string, supportedLocales: string[]): {
2331
- locale: string;
2332
- strippedUrl: string;
2333
- } | null;
2334
- //#endregion
2335
- //#region ../core/src/i18n/translator.d.ts
2336
- interface SimpleTranslatorOptions {
2337
- /** 翻译映射 */
2338
- messages: Record<string, string>;
2339
- /** 当前 locale */
2340
- locale: string;
2341
- /** 复数规则函数(默认英语规则) */
2342
- pluralRule?: PluralRuleProvider;
2343
- /** 找不到翻译时的回退行为(默认返回 key) */
2344
- fallback?: (key: string) => string;
2345
- }
2346
- declare class SimpleTranslator implements Translator {
2347
- readonly locale: string;
2348
- private readonly messages;
2349
- private readonly pluralRule;
2350
- private readonly fallback;
2351
- constructor(options: SimpleTranslatorOptions);
2352
- t(key: string, values?: Record<string, string | number>): string;
2353
- plural(key: string, count: number, values?: Record<string, string | number>): string;
2354
- }
2355
- //#endregion
2356
- //#region ../browser/src/action-handlers/external-url-action.d.ts
2357
- interface ExternalUrlDependencies {
2358
- framework: Framework;
2359
- log: Logger;
2360
- }
2361
- declare function registerExternalUrlHandler(deps: ExternalUrlDependencies): void;
2362
- //#endregion
2363
- //#region ../browser/src/action-handlers/flow-action.d.ts
2364
- /** UI 框架回调 — 解耦 Svelte store 等依赖 */
2365
- interface FlowActionCallbacks {
2366
- /** 导航后更新当前路径(替代 currentPath.set()) */
2367
- onNavigate(pathname: string): void;
2368
- /** 模态页面展示(替代 openModal()) */
2369
- onModal(page: BasePage): void;
2370
- }
2371
- /** 注册 FlowAction handler 所需的依赖 */
2372
- interface FlowActionDependencies {
2373
- framework: Framework;
2374
- log: Logger;
2375
- callbacks: FlowActionCallbacks;
2376
- /** 更新应用 UI 的回调,page 可以是 Promise */
2377
- updateApp: (props: {
2378
- page: Promise<BasePage> | BasePage;
2379
- isFirstPage?: boolean;
2380
- }) => void;
2381
- /** 获取可滚动页面元素,用于滚动位置保存/恢复 */
2382
- getScrollablePageElement?: () => HTMLElement | null;
2383
- /**
2384
- * 是否由本 handler 管理浏览器 history(pushState / popstate)。缺省 `true`。
2385
- * 结构化导航(`startBrowserApp({ navigation })`)下应传 `false`:history 由
2386
- * NavigationBridge 独占,否则两套 `History` 实例会争抢同一个 `window.history.state`、
2387
- * 各自注册 popstate 互相 clobber,导致 back/forward 行为错乱。传 `false` 时本 handler
2388
- * 仍负责 dispatch + updateApp(初始渲染 / redirect / modal),只是不碰 history。
2389
- */
2390
- manageHistory?: boolean;
2391
- /**
2392
- * flat-islands 正向导航钩子(可选)。提供后,正向 FlowAction(非 modal)会调用此函数
2393
- * 并 return,**绕过** navigateTo / updateApp。由 activateFlatIslands 注入,把 URL
2394
- * 路由到隐式单栈 `NavigationController.push`。未提供时行为与今天扁平路径字节级相同。
2395
- */
2396
- onForward?: (url: string) => void | Promise<void>;
2397
- }
2398
- declare function registerFlowActionHandler(deps: FlowActionDependencies): void;
2399
- //#endregion
2400
- //#region ../browser/src/action-handlers/register.d.ts
2401
- interface ActionHandlerDependencies {
2402
- framework: Framework;
2403
- log: Logger;
2404
- callbacks: FlowActionCallbacks;
2405
- updateApp: (props: {
2406
- page: Promise<BasePage> | BasePage;
2407
- isFirstPage?: boolean;
2408
- }) => void;
2409
- /** 获取可滚动页面元素,用于滚动位置保存/恢复 */
2410
- getScrollablePageElement?: () => HTMLElement | null;
2411
- /** 是否由 FlowAction handler 管理 history;结构化导航下传 `false`(见 registerFlowActionHandler)。 */
2412
- manageHistory?: boolean;
2413
- /** flat-islands 正向导航钩子(透传给 registerFlowActionHandler)。 */
2414
- onForward?: (url: string) => void | Promise<void>;
2415
- }
2416
- declare function registerActionHandlers(deps: ActionHandlerDependencies): void;
2417
- //#endregion
2418
- //#region ../browser/src/navigation-bridge.d.ts
2419
- /** NavigationBridge 构造依赖。 */
2420
- interface NavigationBridgeDependencies {
2421
- /** 已构建好的导航控制器(持有 initial 树、intentDispatcher、router 等)。 */
2422
- readonly controller: NavigationController;
2423
- /** URL 编解码器(默认 `createActiveLeafCodec`)。 */
2424
- readonly codec: NavigationCodec;
2425
- /** Router 的最小读取面(encode 反查 / decode 用)。 */
2426
- readonly router: NavigationRouterLike;
2427
- /** 日志器。 */
2428
- readonly log: Logger;
2429
- /** 获取可滚动页面元素,用于滚动位置保存/恢复(透传给 History)。 */
2430
- readonly getScrollablePageElement?: () => HTMLElement | null;
2431
- }
2432
- /**
2433
- * 导航操作句柄 —— 向应用暴露的对外面。
2434
- *
2435
- * 所有写操作返回提交后的 `NavigationSnapshot`;写操作会同步把新树落到 history/URL。
2436
- * `subscribe` 与 controller 的订阅一致(每次提交都回调,含来自 popstate 的 hydrate)。
2437
- */
2438
- interface NavigationHandle {
2439
- /** 当前快照(树 + 已解析的可见目标)。 */
2440
- getSnapshot(): NavigationSnapshot;
2441
- /** 在激活栈压入新目标。 */
2442
- push(intent: string, params?: RouteParams, options?: {
2443
- target?: NavigationPath;
2444
- }): Promise<NavigationSnapshot>;
2445
- /** 从激活栈弹出 count 个(默认 1)。 */
2446
- pop(count?: number): Promise<NavigationSnapshot>;
2447
- /** 激活栈弹回根。 */
2448
- popToRoot(): Promise<NavigationSnapshot>;
2449
- /** 替换激活栈栈顶。 */
2450
- replaceTop(intent: string, params?: RouteParams): Promise<NavigationSnapshot>;
2451
- /** 切换 tabs 激活分支。 */
2452
- selectTab(key: string, target?: NavigationPath): Promise<NavigationSnapshot>;
2453
- /** 设置 split 列内容(intent=undefined 清空该列)。 */
2454
- selectColumn(columnId: string, intent: string | undefined, params?: RouteParams, target?: NavigationPath): Promise<NavigationSnapshot>;
2455
- /** 用外部树替换当前树并重解析(一般由桥内部 popstate 调用,亦对外暴露)。 */
2456
- hydrate(tree: NavigationNode): Promise<NavigationSnapshot>;
2457
- /** 订阅快照变更;返回取消订阅函数。 */
2458
- subscribe(listener: (snapshot: NavigationSnapshot) => void): () => void;
2459
- }
2460
- /**
2461
- * 创建 NavigationBridge:订阅 controller、装配 popstate、返回 navigation handle。
2462
- *
2463
- * 调用后 bridge 已激活(已订阅 controller + 已注册 popstate listener)。应用应在调用前/后
2464
- * 调一次 `controller.resolve()` 完成首屏解析;首屏的快照提交会被 bridge 用 `replaceState`
2465
- * 写入 history(first-page 语义),不会污染历史栈。
2466
- */
2467
- declare function createNavigationBridge(deps: NavigationBridgeDependencies): NavigationHandle;
2468
- //#endregion
2469
- //#region ../browser/src/session-bridge.d.ts
2470
- /** 导航变更后自动落盘的默认防抖窗口(ms):合并连续导航,避免每跳一屏写一次。 */
2471
- declare const SESSION_DEFAULT_DEBOUNCE_MS = 500;
2472
- /** `createSessionBridge` 选项。 */
2473
- interface SessionBridgeOptions {
2474
- /** 会话编排器(core)。 */
2475
- readonly store: SessionStore;
2476
- /** 导航适配器;导航变更时用其 `presentKeys()` 驱动 scoped prune。 */
2477
- readonly adapter: SessionNavigationAdapter;
2478
- /** 订阅导航变更;返回反订阅函数。省略 = 不自动捕获(仅靠生命周期事件 + 手动 `save`)。 */
2479
- readonly subscribeNavigation?: (onChange: () => void) => () => void;
2480
- /** 自动落盘防抖窗口(ms);默认 `SESSION_DEFAULT_DEBOUNCE_MS`。 */
2481
- readonly debounceMs?: number;
2482
- /** 恢复门控;默认 `defaultShouldRestore`(显式深链优先,见其文档)。 */
2483
- readonly shouldRestore?: (snapshot: SessionSnapshot, currentUrl: string) => boolean;
2484
- }
2485
- /** SessionBridge 对外句柄:导航作用域读写 + boot 恢复 + 手动逃生口 + 解绑。 */
2486
- interface SessionHandle {
2487
- /**
2488
- * 导航作用域状态(每屏 per-entry)。应用渲染某屏时用 `scope.get(entryKey)` /
2489
- * `set(entryKey, data)` 读写(`entryKey = sessionEntryKey(intent, params)`)。
2490
- * 始终委托当前 store 的 scope —— restore 会重建 scope map,经此 getter 取到的恒是最新实例。
2491
- */
2492
- readonly scope: NavigationScopedState;
2493
- /** boot 时调用:读快照,通过门控则整体恢复(nav + slices + scoped)。 */
2494
- restore(currentUrl: string): void | Promise<void>;
2495
- /** 手动落盘(= `store.save()`)。 */
2496
- save(): void;
2497
- /** 清除持久化快照(= `store.clear()`)。 */
2498
- clear(): void;
2499
- /** 反订阅导航 + 解绑全部监听 + 清挂起定时器(幂等)。 */
2500
- dispose(): void;
2501
- }
2502
- /**
2503
- * 默认恢复门控策略(精确、无歧义,遵循「显式深链优先」)。
2504
- *
2505
- * - **带可比 URL**(快照含 `url` —— 扁平天然有,结构化由适配器在 capture 时记录浏览器
2506
- * `location`):当且仅当 `currentUrl` 全等 `snapshot.url`,**或** `currentUrl` 路径为根 `/`
2507
- * (重载同深链 / 全新进入 → 恢复;改去别的深链 → 跳过,显式深链不被旧会话覆盖)。
2508
- * 这让结构化导航与扁平**对称**:重载 `/item/1` 即恢复其作用域状态。
2509
- * - **回退(无 `url`)**:旧快照 / 适配器不提供 URL 时 —— 扁平比 `nav.url`,结构化只在根 `/`
2510
- * 放行(树无单一可比 URL,门设在「入口」;要更细由应用覆盖 predicate)。
2511
- * - **无 `navigation`**(仅切片):总恢复(与 URL 无关)。
2512
- *
2513
- * 「根」判定为路径 `=== "/"`(剥离 query/hash);带 base path 的应用应覆盖 `shouldRestore`。
2514
- */
2515
- declare function defaultShouldRestore(snapshot: SessionSnapshot, currentUrl: string): boolean;
2516
- /**
2517
- * 创建 SessionBridge:订阅导航、装配生命周期监听、返回会话句柄。
2518
- *
2519
- * 调用后 bridge 已激活(已订阅导航 + 已注册 `pagehide` / `visibilitychange`)。应用应在
2520
- * 首次导航完成后调一次 `restore(initialUrl)` 完成 boot 恢复。
2521
- */
2522
- declare function createSessionBridge(options: SessionBridgeOptions): SessionHandle;
2523
- //#endregion
2524
- //#region ../browser/src/app-handle.d.ts
2525
- /** 统一 app 句柄。两个子系统都配齐时成员完整;只配其一则仅含该子系统成员(另一组运行期缺席)。 */
2526
- interface AppHandle {
2527
- getSnapshot: NavigationHandle["getSnapshot"];
2528
- subscribe: NavigationHandle["subscribe"];
2529
- push: NavigationHandle["push"];
2530
- pop: NavigationHandle["pop"];
2531
- popToRoot: NavigationHandle["popToRoot"];
2532
- replaceTop: NavigationHandle["replaceTop"];
2533
- selectTab: NavigationHandle["selectTab"];
2534
- selectColumn: NavigationHandle["selectColumn"];
2535
- save: SessionHandle["save"];
2536
- clear: SessionHandle["clear"];
2537
- readonly scope: SessionHandle["scope"];
2538
- }
2539
- /**
2540
- * 构建统一 app 句柄。`navigation`/`session` 任一缺省则其成员不并入。
2541
- * 至少应有一个非空(调用方在配了 navigation 和/或 session 时才构建)。
2542
- */
2543
- declare function createAppHandle(navigation: NavigationHandle | undefined, session: SessionHandle | undefined): AppHandle;
2544
- //#endregion
2545
- //#region ../browser/src/navigation-islands.d.ts
2546
- /** `mountEntry` 返回的 island 句柄。 */
2547
- interface IslandHandle {
2548
- /** 销毁该 island 实例(应用用 app.unmount() / root.unmount() / comp.$destroy() 实现)。 */
2549
- unmount(): void;
2550
- }
2551
- /** 通用挂载原语:把一个条目的视图挂进 container,返回卸载句柄。 */
2552
- type MountEntry = (entry: ResolvedEntry, container: HTMLElement) => IslandHandle;
2553
- /** `createIslandOrchestrator` 选项。 */
2554
- interface IslandOrchestratorOptions {
2555
- /** 框架在其内构建/挂载 island 容器的 outlet 元素(应用渲染、稳定、空)。 */
2556
- readonly outlet: HTMLElement;
2557
- /** 应用提供的挂载原语。 */
2558
- readonly mountEntry: MountEntry;
2559
- /** 重放调度(默认 requestAnimationFrame;测试可注入同步执行)。 */
2560
- readonly schedule?: (cb: () => void) => void;
2561
- }
2562
- /** 编排器对外接口。 */
2563
- interface IslandOrchestrator {
2564
- /** 把 DOM 同步到一个导航快照(挂新/卸离树/detach 隐藏/attach 可见)。 */
2565
- sync(snapshot: NavigationSnapshot): void;
2566
- /** 卸载全部 island、清空 outlet。 */
2567
- dispose(): void;
2568
- }
2569
- declare function createIslandOrchestrator(options: IslandOrchestratorOptions): IslandOrchestrator;
2570
- //#endregion
2571
- //#region ../browser/src/start-app.d.ts
2572
- /**
2573
- * 结构化导航定义 —— 由应用通过 `defineNavigation(...)` 或手写提供。
2574
- *
2575
- * 仅当该字段出现时 bridge 才激活;缺省时 `startBrowserApp` 走原有扁平单页路径,行为不变。
2576
- * 提供后:
2577
- * - 用 `initial` 树构建 `NavigationController`(守卫上下文走 `createBrowserContext`,
2578
- * 预取缓存复用 `framework.prefetchedIntents`);
2579
- * - 装配 `NavigationBridge`(snapshot → history/URL,popstate → hydrate);
2580
- * - 解析首屏树(一次 `resolve()`),通过 mount context 把 handle 交给应用。
2581
- */
2582
- interface BrowserNavigationConfig {
2583
- /** 初始导航树(单 LeafNode = 今天的扁平单页)。 */
2584
- readonly initial: NavigationNode;
2585
- /** URL 编解码器;缺省 `createActiveLeafCodec()`。 */
2586
- readonly codec?: NavigationCodec;
2587
- /** 导航级 beforeLoad 守卫(对主目标执行)。 */
2588
- readonly beforeLoad?: readonly BeforeLoadGuard[];
2589
- /** 导航级 afterLoad 守卫。 */
2590
- readonly afterLoad?: readonly AfterLoadGuard[];
2591
- /** dispatch 失败 / deny 时的兜底错误页工厂。 */
2592
- readonly getErrorPage?: (status: number, message: string) => BasePage;
2593
- /**
2594
- * opt-in islands 挂载原语:提供后框架按 per-entry 把视图挂为独立 root 并保活(见 Phase 2)。
2595
- * 缺省时走原有「单 mount + 应用订阅 snapshot 重渲」路径,不变。
2596
- */
2597
- readonly mountEntry?: MountEntry;
2598
- }
2599
- /**
2600
- * 会话恢复定义(可选)。
2601
- *
2602
- * 仅当该字段出现时会话能力才激活;缺省时 `startBrowserApp` 启动路径字节级不变。提供后:
2603
- * - 构建 `SessionStore`(导航适配器:有 `navigation` 配置 → 结构化
2604
- * `createNavigationSessionAdapter(controller)`;否则 → 扁平 `createUrlSessionAdapter`
2605
- * 接 `framework.perform(makeFlowAction(url))`);
2606
- * - 注册 `providers` 为全局切片来源;
2607
- * - 装配 `SessionBridge`(导航变更防抖落盘 + `pagehide` / `visibilitychange` 即时落盘 + scoped prune);
2608
- * - mount 后 `restore(initialUrl)` 完成 boot 恢复,handle 经 mount context 交付。
2609
- */
2610
- interface BrowserSessionConfig {
2611
- /** 全局状态切片 provider;启动时全部注册。 */
2612
- readonly providers?: readonly SessionStateProvider[];
2613
- /** 快照存储;缺省 `createWebStorage("session")`(标签级,关闭即清)。 */
2614
- readonly storage?: Storage;
2615
- /** 快照版本;缺省 `SESSION_DEFAULT_VERSION`,不符即整份丢弃。 */
2616
- readonly version?: number;
2617
- /** 快照最大存活时长(ms);省略 = 不过期。 */
2618
- readonly maxAgeMs?: number;
2619
- /** 导航变更自动落盘防抖窗口(ms);缺省 `SESSION_DEFAULT_DEBOUNCE_MS`。 */
2620
- readonly debounceMs?: number;
2621
- /** 恢复门控;缺省 `defaultShouldRestore`(显式深链优先)。 */
2622
- readonly shouldRestore?: (snapshot: SessionSnapshot, currentUrl: string) => boolean;
2623
- }
2624
- interface BrowserAppConfig {
2625
- /** 注册 controllers 和路由的引导函数 */
2626
- bootstrap: (framework: Framework) => void;
2627
- /** DOM 挂载点 ID(默认 "app") */
2628
- mountId?: string;
2629
- /** 获取可滚动页面元素,用于滚动位置保存/恢复 */
2630
- getScrollablePageElement?: () => HTMLElement | null;
2631
- /**
2632
- * 启动前钩子 — 在 Framework 创建后、挂载前执行
2633
- *
2634
- * 用于初始化错误监控、埋点 SDK、i18n 等。
2635
- */
2636
- onBeforeStart?: (framework: Framework) => void | Promise<void>;
2637
- /**
2638
- * 启动后钩子 — 在初始页面触发后执行
2639
- *
2640
- * 用于启动后操作(如 service worker 注册、性能打点)。
2641
- */
2642
- onAfterStart?: (framework: Framework) => void | Promise<void>;
2643
- /**
2644
- * 挂载应用到 DOM
2645
- *
2646
- * 框架无关 — Svelte / React / Vue 均可通过此回调实现。
2647
- *
2648
- * @param target - DOM 挂载点
2649
- * @param context - Framework 实例 + 语言 + handle(配了 navigation/session 时)
2650
- * @returns 更新函数,用于后续页面切换
2651
- */
2652
- mount: (target: HTMLElement, context: {
2653
- framework: Framework;
2654
- navigation?: NavigationHandle;
2655
- session?: SessionHandle;
2656
- app?: AppHandle;
2657
- }) => (props: {
2658
- page: Promise<BasePage> | BasePage;
2659
- isFirstPage?: boolean;
2660
- }) => void;
2661
- /** FlowAction / ExternalUrl 回调 */
2662
- callbacks: FlowActionCallbacks;
2663
- /**
2664
- * Framework 配置 — locale、reportCallback、eventRecorder 等
2665
- *
2666
- * 传入后会在 Framework.create() 时合并。
2667
- * prefetchedIntents 由框架自动从 DOM 提取,无需传入。
2668
- */
2669
- frameworkConfig?: Omit<FrameworkConfig, "prefetchedIntents">;
2670
- /**
2671
- * 异步加载当前 locale 的翻译字典。
2672
- *
2673
- * 显式传入时会覆盖 bootstrap / Vite 自动生成的 loader。
2674
- */
2675
- loadMessages?: MessagesLoader;
2676
- /**
2677
- * 结构化导航定义(可选)。
2678
- *
2679
- * 提供后,`startBrowserApp` 在 mount 前构建 NavigationController + NavigationBridge,
2680
- * 并通过 mount context 把导航 handle 交给应用。缺省时走原有扁平单页路径,行为完全不变。
2681
- */
2682
- navigation?: BrowserNavigationConfig;
2683
- /**
2684
- * 会话恢复定义(可选)。
2685
- *
2686
- * 提供后,`startBrowserApp` 在 mount 前装配 `SessionStore` + `SessionBridge`,
2687
- * 并通过 mount context 把会话 handle 交给应用;boot 恢复(restore)在 mount 后执行。
2688
- * 缺省时整段不生效,启动路径字节级不变。
2689
- */
2690
- session?: BrowserSessionConfig;
2691
- /**
2692
- * opt-in 重载 DOM 自动恢复(spec §4.5)。仅当同时提供 `navigation.mountEntry`(islands)+
2693
- * `session` 时生效:标 `data-restore-root` 的容器内表单/滚动/<details> 自动捕获进会话作用域、
2694
- * 重载后回填。缺省关闭。
2695
- */
2696
- domRestore?: boolean;
2697
- /**
2698
- * 顶层 islands 挂载原语(flat-islands opt-in,Phase 4)。
2699
- *
2700
- * 提供此字段且**未提供** `navigation` 时,框架合成一个隐式单栈 `NavigationController`,
2701
- * 把 `FlowAction` 正向导航路由为 `controller.push`,`popstate` 路由为 `hydrate`,
2702
- * island 实例在 back/forward 时保活不重挂。
2703
- *
2704
- * 提供了 `navigation` 时请使用 `navigation.mountEntry`,顶层该字段被忽略。
2705
- */
2706
- mountEntry?: MountEntry;
2707
- }
2708
- /**
2709
- * 启动客户端应用
2710
- *
2711
- * 自动执行 hydration 全流程。
2712
- */
2713
- declare function startBrowserApp(config: BrowserAppConfig): Promise<void>;
2714
- //#endregion
2715
- //#region ../browser/src/islands-shell.d.ts
2716
- /**
2717
- * islands 应用 shell(方案 C):chrome-root 与 outlet 为兄弟。
2718
- *
2719
- * SSR 已渲 → target 内已有 `[data-fs-chrome]` + `[data-fs-outlet]`,复用之,`hydrate` 取决于
2720
- * chrome-root 是否有内容(SSR 渲过 → true)。纯 CSR(无 SSR shell)→ 建出二者兄弟,`hydrate=false`。
2721
- * 编排器经 `[data-fs-outlet]` 找 outlet(与此约定同源)。
2722
- */
2723
- interface IslandsShell {
2724
- readonly chromeRoot: HTMLElement;
2725
- readonly outlet: HTMLElement;
2726
- /** chrome-root 是否含 SSR 内容(true → 应水合 chrome,false → 客户端新建)。 */
2727
- readonly hydrate: boolean;
2728
- }
2729
- declare function resolveIslandsShell(target: HTMLElement): IslandsShell;
2730
- //#endregion
2731
- //#region ../browser/src/web-storage.d.ts
2732
- /** Web Storage 类型:会话级(标签关闭即清)或本地级(跨会话持久)。 */
2733
- type WebStorageKind = "session" | "local";
2734
- /**
2735
- * 创建一个由浏览器 Web Storage 支撑的 core `Storage`。
2736
- *
2737
- * @param kind `"session"` → `sessionStorage`;`"local"` → `localStorage`。
2738
- */
2739
- declare function createWebStorage(kind: WebStorageKind): Storage;
2740
- //#endregion
2741
- //#region ../browser/src/dom-restore.d.ts
2742
- interface DomRestoreOptions {
2743
- /** 会话作用域(来自 SessionHandle.scope);DOM 状态写进 `scope[key].__dom`。 */
2744
- readonly scope: NavigationScopedState;
2745
- /** 回填调度(默认 requestAnimationFrame;测试可注入同步执行)。 */
2746
- readonly schedule?: (cb: () => void) => void;
2747
- }
2748
- interface DomRestore {
2749
- /** 捕获一个 island container 的 DOM 状态进 scope(供测试 + 内部接线复用)。 */
2750
- captureEntry(container: HTMLElement): void;
2751
- /** 回填一个 island container(从 scope 读,scheduled)。 */
2752
- restoreEntry(container: HTMLElement): void;
2753
- /** 接线进 outlet(fs:* / input / pagehide + boot catch-up)。 */
2754
- attach(outlet: HTMLElement): void;
2755
- /** 解绑全部监听(幂等)。 */
2756
- dispose(): void;
2757
- }
2758
- declare function createDomRestore(options: DomRestoreOptions): DomRestore;
2759
- //#endregion
2760
- //#region ../browser/src/utils/history.d.ts
2761
- interface HistoryOptions {
2762
- getScrollablePageElement: () => HTMLElement | null;
2763
- /**
2764
- * 是否把 `state` 一并写入 `window.history.state`(而非仅 `{ id }` + 内存 LruMap)。缺省 `false`。
2765
- *
2766
- * 内存 LruMap 在整页刷新后丢失;若 state 小且可结构化克隆(如导航树 `{ tree }`),开启此项
2767
- * 可让 state 随 `window.history.state` **跨刷新按 entry 保留**,刷新后 back/forward 仍能从
2768
- * history.state 还原(onPopState 在 LruMap 未命中时回退到 `event.state.state`)。
2769
- * 大状态(如整页 `{ page }`)不应开启,避免撑爆 history.state。
2770
- */
2771
- persistInHistoryState?: boolean;
2772
- }
2773
- declare class History<State> {
2774
- private readonly entries;
2775
- private readonly log;
2776
- private readonly getScrollablePageElement;
2777
- private readonly persistInHistoryState;
2778
- private currentStateId;
2779
- constructor(log: Logger, options: HistoryOptions, sizeLimit?: number);
2780
- /** 写入 window.history.state 的载荷:persist 时连 state 一起带(跨刷新保留)。 */
2781
- private historyState;
2782
- replaceState(state: State, url: string): void;
2783
- pushState(state: State, url: string): void;
2784
- beforeTransition(): void;
2785
- onPopState(listener: (url: string, state?: State) => void | Promise<void>): void;
2786
- /** 仅推入 URL,不缓存页面状态(用于页面加载失败场景) */
2787
- pushUrl(url: string): void;
2788
- /** 仅替换 URL,不缓存页面状态(用于页面加载失败场景) */
2789
- replaceUrl(url: string): void;
2790
- updateState(update: (current?: State) => State): void;
2791
- private get scrollTop();
2792
- private set scrollTop(value);
2793
- }
2794
- //#endregion
2795
- //#region ../browser/src/utils/try-scroll.d.ts
2796
- declare function tryScroll(log: Logger, getScrollableElement: () => HTMLElement | null, scrollY: number): void;
2797
- //#endregion
2798
- //#region ../browser/src/server-data.d.ts
2799
- /**
2800
- * 从 DOM 反序列化服务端嵌入的数据。
2801
- * 读取 `<script id="serialized-server-data">` 的内容并移除标签。
2802
- */
2803
- declare function deserializeServerData(): PrefetchedIntent[] | undefined;
2804
- /**
2805
- * 从 DOM 提取 SSR 数据并构建 PrefetchedIntents 实例。
2806
- * 替代原来的 PrefetchedIntents.fromDom()。
2807
- */
2808
- declare function createPrefetchedIntentsFromDom(): PrefetchedIntents;
2809
- //#endregion
2810
- export { IntersectionImpressionObserver as $, markPublic as $i, TabsInit as $n, shouldLog as $r, defineNavigation as $t, ActionHandlerDependencies as A, IntentDispatcher as Aa, StandardIssue as Ai, SerializedSplitColumn as An, HostCheckResult as Ar, NavigationScopedState as At, getTextDirection as B, FlowAction as Ba, NextResult as Bi, pop as Bn, BaseItem as Br, SessionUrlLocation as Bt, SessionBridgeOptions as C, ReportingLoggerFactory as Ca, StripOptional as Ci, createFlatStackCodec as Cn, TabsNode as Cr, createUrlSessionAdapter as Ct, NavigationBridgeDependencies as D, LoggerFactory as Da, list as Di, SerializedLeaf as Dn, pipe as Dr, sessionEntryKey as Dt, defaultShouldRestore as E, Logger as Ea, isMultiValueSchema as Ei, encodeNavigationTreeParam as En, mapEach as Er, createNavigationScopedState as Et, ExternalUrlDependencies as F, ActionHandler as Fa, AfterLoadGuard as Fi, serializeNavigationStable as Fn, HttpClientConfig as Fr, SessionNavigationAdapter as Ft, PluralCategory as G, makeFlowAction as Ga, next as Gi, resolveActivePath as Gn, FrameworkConfig as Gr, defineRoute as Gt, makeLocaleInfo as H, isExternalUrlAction as Ha, RedirectResult as Hi, popToRoot as Hn, SafeErrorPageOptions as Hr, DefineRoutesOptions as Ht, registerExternalUrlHandler as I, ACTION_KINDS as Ia, BeforeLoadGuard as Ii, collectAllLeaves as In, HttpError as Ir, SessionSnapshot as It, interpolate as J, BASE_PAGE_FIELDS as Ji, setVisibility as Jn, LocaleAttributes as Jr, DefineNavigationOptions as Jt, PluralRuleProvider as K, redirect as Ki, selectColumn as Kn, PrefetchedIntent as Kr, defineRoutes as Kt, SimpleTranslator as L, Action as La, DenyResult as Li, collectVisibleDestinations as Ln, RequestInterceptor as Lr, SessionStateProvider as Lt, FlowActionCallbacks as M, IntentController as Ma, StandardSchemaV1 as Mi, SerializedTabs as Mn, classifyUrl as Mr, SESSION_DEFAULT_VERSION as Mt, FlowActionDependencies as N, Container as Na, makeSchema as Ni, deserializeNavigation as Nn, HostGuardError as Nr, SessionError as Nt, NavigationHandle as O, SecureFetchOptions as Oa, InferOutput as Oi, SerializedNavigation as On, pipeAsync as Or, decodeSnapshot as Ot, registerFlowActionHandler as P, ActionDispatcher as Pa, runStandard as Pi, serializeNavigation as Pn, HttpClient as Pr, SessionErrorContext as Pt, ImpressionObserverOptions as Q, isPublicMarked as Qi, SplitColumnInit as Qn, resetFilterCache as Qr, NavigationSSRDefinition as Qt, SimpleTranslatorOptions as R, CompoundAction as Ra, MiddlewareResult as Ri, findNearestStack as Rn, ResponseInterceptor as Rr, SessionStore as Rt, SESSION_DEFAULT_DEBOUNCE_MS as S, ReportingLogger as Sa, QuerySchemaMap as Si, createActiveLeafCodec as Sn, StackNode as Sr, createNavigationSessionAdapter as St, createSessionBridge as T, BaseLogger as Ta, MultiValueSchema as Ti, decodeNavigationTreeParam as Tn, Mapper as Tr, collectLeafKeys as Tt, resolveLocaleFromUrl as U, isFlowAction as Ua, RewriteResult as Ui, push as Un, safeErrorPage as Ur, RenderMode as Ut, isRtl as V, isCompoundAction as Va, PostLoadContext as Vi, popTo as Vn, BaseShelf as Vr, isUrlLocation as Vt, setHtmlLocaleAttributes as W, makeExternalUrlAction as Wa, deny as Wi, replaceTop as Wn, Framework as Wr, RouteDefinition as Wt, WithFieldsRecorder as X, FINESOFT_PUBLIC as Xi, ResolvedEntry as Xn, TextDirection as Xr, NavigationDefinition as Xt, resolvePluralKey as Y, BasePage as Yi, visibleSplitColumns as Yn, LocaleInfo as Yr, NavigationBrowserConfig as Yt, VoidEventRecorder as Z, getPublicFields as Zi, islandContainerAttributes as Zn, Translator as Zr, NavigationInitial as Zt, IslandOrchestratorOptions as _, EventRecorder as _a, Router as _i, createNavigationController as _n, ResolvedDestination as _r, Optional as _t, DomRestore as a, MakeDependenciesOptions as aa, withDefault as ai, NavigationDispatchContext as an, split as ar, ServerContextOptions as at, AppHandle as b, MetricsFieldsProvider as ba, InferQuery as bi, NavigationCodec as bn, SplitNode as br, LruMap as bt, createWebStorage as c, Storage as ca, bool as ci, PopOperation as cn, LeafNode as cr, generateUuid as ct, BrowserAppConfig as d, MessagesLoaderContext as da, oneOf as di, PushOperation as dn, NavigationNode as dr, removeHost as dt, RequestScopedKey as ea, ConsoleLogger as ei, HydrateOperation as en, isLeafNode as er, ConsoleEventRecorder as et, BrowserNavigationConfig as f, TranslationMessages as fa, str as fi, PushOptions as fn, NavigationNodeKind as fr, removeQueryParams as ft, IslandOrchestrator as g, detectPlatform as ga, RouteMatch as gi, SetVisibilityOperation as gn, Page as gr, None as gt, IslandHandle as h, PlatformInfo as ha, RouteAddOptions as hi, SelectTabOperation as hn, NavigationSnapshot as hr, getPWADisplayMode as ht, History as i, FeatureFlagsProvider as ia, optional as ii, NavigationControllerOptions as in, leaf as ir, BrowserContextOptions as it, registerActionHandlers as j, Intent as ja, StandardResult as ji, SerializedStack as jn, classifyHost as jr, SESSION_DEFAULT_KEY as jt, createNavigationBridge as k, secureFetch as ka, ParamSchema as ki, SerializedSplit as kn, BaseController as kr, encodeSnapshot as kt, IslandsShell as l, makeDependencies as la, int as li, PopToOperation as ln, NAVIGATION_NODE_KINDS as lr, buildUrl as lt, startBrowserApp as m, resolveMessages as ma, RouteParams as mi, SelectColumnOperation as mn, NavigationPathStep as mr, PWADisplayMode as mt, deserializeServerData as n, DEP_KEYS as na, CompositeLogger as ni, NavigationContextInput as nn, isStackNode as nr, runAfterLoadGuards as nt, DomRestoreOptions as o, MetricsRecorder as oa, NumOptions as oi, NavigationOpKind as on, stack as or, createBrowserContext as ot, BrowserSessionConfig as p, resolveConfiguredMessages as pa, uuid as pi, ReplaceTopOperation as pn, NavigationPath as pr, removeScheme as pt, englishPlural as q, rewrite as qi, selectTab as qn, PrefetchedIntents as qr, route as qt, tryScroll as r, FeatureFlags as ra, CompositeLoggerFactory as ri, NavigationController as rn, isTabsNode as rr, runBeforeLoadGuards as rt, createDomRestore as s, Net as sa, StrOptions as si, NavigationOperation as sn, tabs as sr, createServerContext as st, createPrefetchedIntentsFromDom as t, defineRequestScopedKey as ta, ConsoleLoggerFactory as ti, NAVIGATION_OP_KINDS as tn, isSplitNode as tr, CompositeEventRecorder as tt, resolveIslandsShell as u, MessagesLoader as ua, num as ui, PopToRootOperation as un, NavigationError as ur, getBaseUrl as ut, MountEntry as v, ImpressionEntry as va, ExtractParamNames as vi, DEFAULT_NAV_PARAM as vn, SPLIT_VISIBILITIES as vr, isNone as vt, SessionHandle as w, ReportingLoggerOptions as wa, ListOptions as wi, createFullStateCodec as wn, AsyncMapper as wr, createSessionStore as wt, createAppHandle as x, ReportCallback as xa, ParamsFor as xi, NavigationRouterLike as xn, SplitVisibility as xr, UrlAdapterOptions as xt, createIslandOrchestrator as y, ImpressionObserver as ya, InferParams as yi, FullStateCodecOptions as yn, SplitColumn as yr, isSome as yt, getLocaleAttributes as z, ExternalUrlAction as za, NavigationContext as zi, findNode as zn, stableStringify as zr, SessionStoreOptions as zt };
2811
- //# sourceMappingURL=browser-JTs2jqVY.d.mts.map