@finesoft/front 0.1.78 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.mts +2 -2
- package/dist/browser.mjs +1 -1
- package/dist/index.d.mts +152 -3
- package/dist/index.mjs +22 -22
- package/dist/server-data-HVSgxEac.d.mts +2272 -0
- package/dist/src-BI4eMjHk.mjs +2 -0
- package/docs/11-navigation.md +355 -0
- package/docs/zh/11-navigation.md +355 -0
- package/package.json +3 -3
- package/dist/server-data-DGbiKzMS.d.mts +0 -1249
- package/dist/start-app-BdXBCcor.mjs +0 -2
|
@@ -0,0 +1,2272 @@
|
|
|
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 的校验,吸收同步/异步差异 */
|
|
584
|
+
declare function runStandard(schema: StandardSchemaV1, raw: 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/router.d.ts
|
|
593
|
+
/** 路由匹配结果 */
|
|
594
|
+
interface RouteMatch {
|
|
595
|
+
intent: Intent;
|
|
596
|
+
action: FlowAction;
|
|
597
|
+
renderMode?: string;
|
|
598
|
+
/** 该路由绑定的 beforeLoad 守卫 */
|
|
599
|
+
beforeGuards?: BeforeLoadGuard[];
|
|
600
|
+
/** 该路由绑定的 afterLoad 守卫 */
|
|
601
|
+
afterGuards?: AfterLoadGuard[];
|
|
602
|
+
}
|
|
603
|
+
/** 路由添加选项 */
|
|
604
|
+
interface RouteAddOptions {
|
|
605
|
+
renderMode?: string;
|
|
606
|
+
beforeGuards?: BeforeLoadGuard[];
|
|
607
|
+
afterGuards?: AfterLoadGuard[];
|
|
608
|
+
paramCodecs?: Record<string, ParamSchema>;
|
|
609
|
+
queryCodecs?: Record<string, StandardSchemaV1<string, unknown>>;
|
|
610
|
+
}
|
|
611
|
+
declare class Router {
|
|
612
|
+
private readonly debug?;
|
|
613
|
+
private routes;
|
|
614
|
+
constructor(debug?: ((message: string) => void) | undefined);
|
|
615
|
+
/** 添加路由规则 */
|
|
616
|
+
add(pattern: string, intentId: string, renderModeOrOptions?: string | RouteAddOptions): this;
|
|
617
|
+
/** 解析 URL → RouteMatch(含参数校验;校验失败则 fall-through 到下一条路由) */
|
|
618
|
+
resolve(urlOrPath: string): Promise<RouteMatch | null>;
|
|
619
|
+
/** 获取所有已注册的路由 */
|
|
620
|
+
getRoutes(): string[];
|
|
621
|
+
private parseUrl;
|
|
622
|
+
}
|
|
623
|
+
//#endregion
|
|
624
|
+
//#region ../core/src/router/types.d.ts
|
|
625
|
+
/**
|
|
626
|
+
* Router 共享类型
|
|
627
|
+
*
|
|
628
|
+
* `RouteParams` 是路由参数(path + query 经 codec 转换后)的统一形状,
|
|
629
|
+
* 同时也是 Intent.params、NavigationContext.params 与 LeafNode.params 的共同类型。
|
|
630
|
+
*/
|
|
631
|
+
/** 路由参数:键为参数名,值为 codec 转换后的任意类型(string / number / boolean …)。 */
|
|
632
|
+
type RouteParams = Record<string, unknown>;
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region ../core/src/router/params/primitives.d.ts
|
|
635
|
+
interface StrOptions {
|
|
636
|
+
minLength?: number;
|
|
637
|
+
maxLength?: number;
|
|
638
|
+
pattern?: RegExp;
|
|
639
|
+
}
|
|
640
|
+
declare function str(opts?: StrOptions): ParamSchema<string>;
|
|
641
|
+
interface NumOptions {
|
|
642
|
+
min?: number;
|
|
643
|
+
max?: number;
|
|
644
|
+
}
|
|
645
|
+
declare function int(opts?: NumOptions): ParamSchema<number>;
|
|
646
|
+
declare function num(opts?: NumOptions): ParamSchema<number>;
|
|
647
|
+
declare function bool(): ParamSchema<boolean>;
|
|
648
|
+
declare function oneOf<const T extends readonly string[]>(values: T): ParamSchema<T[number]>;
|
|
649
|
+
declare function uuid(): ParamSchema<string>;
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region ../core/src/router/params/modifiers.d.ts
|
|
652
|
+
/** 输入缺失(undefined)时跳过校验、产出 undefined;否则委托内部 codec(同步或异步均可)。 */
|
|
653
|
+
declare function optional<T>(codec: ParamSchema<T>): ParamSchema<T | undefined>;
|
|
654
|
+
/** 输入缺失时用 fallback;否则委托内部 codec。 */
|
|
655
|
+
declare function withDefault<T>(codec: ParamSchema<T>, fallback: T): ParamSchema<T>;
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region ../core/src/router/params/infer.d.ts
|
|
658
|
+
/** 剥离可选参数尾随的 "?" */
|
|
659
|
+
type StripOptional<S extends string> = S extends `${infer N}?` ? N : S;
|
|
660
|
+
/** 从 path pattern 字面量提取参数名联合(处理 :param 与 :param?) */
|
|
661
|
+
type ExtractParamNames<Path extends string> = Path extends `${infer _Head}:${infer Rest}` ? Rest extends `${infer Name}/${infer Tail}` ? StripOptional<Name> | ExtractParamNames<`/${Tail}`> : StripOptional<Rest> : never;
|
|
662
|
+
/** path 参数 codec map 的形状:key 只能是 path 中出现的参数名(均可选声明) */
|
|
663
|
+
type ParamsFor<Path extends string> = { [K in ExtractParamNames<Path>]?: ParamSchema };
|
|
664
|
+
/** query 参数 codec map:key 自由开放 */
|
|
665
|
+
type QuerySchemaMap = Record<string, StandardSchemaV1<string, unknown>>;
|
|
666
|
+
/** 从 codec map 推导运行期参数类型 */
|
|
667
|
+
type InferParams<P extends Record<string, ParamSchema>> = { [K in keyof P]: InferOutput<P[K]> };
|
|
668
|
+
type InferQuery<Q extends QuerySchemaMap> = { [K in keyof Q]: InferOutput<Q[K]> };
|
|
669
|
+
//#endregion
|
|
670
|
+
//#region ../core/src/logger/composite.d.ts
|
|
671
|
+
declare class CompositeLoggerFactory implements LoggerFactory {
|
|
672
|
+
private readonly factories;
|
|
673
|
+
constructor(factories: LoggerFactory[]);
|
|
674
|
+
loggerFor(name: string): Logger;
|
|
675
|
+
}
|
|
676
|
+
declare class CompositeLogger implements Logger {
|
|
677
|
+
private readonly loggers;
|
|
678
|
+
constructor(loggers: Logger[]);
|
|
679
|
+
debug(...args: unknown[]): string;
|
|
680
|
+
info(...args: unknown[]): string;
|
|
681
|
+
warn(...args: unknown[]): string;
|
|
682
|
+
error(...args: unknown[]): string;
|
|
683
|
+
private callAll;
|
|
684
|
+
}
|
|
685
|
+
//#endregion
|
|
686
|
+
//#region ../core/src/logger/console.d.ts
|
|
687
|
+
declare class ConsoleLogger extends BaseLogger {
|
|
688
|
+
debug(...args: unknown[]): string;
|
|
689
|
+
info(...args: unknown[]): string;
|
|
690
|
+
warn(...args: unknown[]): string;
|
|
691
|
+
error(...args: unknown[]): string;
|
|
692
|
+
}
|
|
693
|
+
declare class ConsoleLoggerFactory implements LoggerFactory {
|
|
694
|
+
loggerFor(category: string): Logger;
|
|
695
|
+
}
|
|
696
|
+
//#endregion
|
|
697
|
+
//#region ../core/src/logger/local-storage-filter.d.ts
|
|
698
|
+
declare function shouldLog(name: string, level: Level): boolean;
|
|
699
|
+
declare function resetFilterCache(): void;
|
|
700
|
+
//#endregion
|
|
701
|
+
//#region ../core/src/i18n/types.d.ts
|
|
702
|
+
/**
|
|
703
|
+
* i18n — 类型定义
|
|
704
|
+
*
|
|
705
|
+
* 框架级国际化基础设施。
|
|
706
|
+
*/
|
|
707
|
+
/** 翻译函数 */
|
|
708
|
+
interface Translator {
|
|
709
|
+
/**
|
|
710
|
+
* 翻译 key → 本地化字符串
|
|
711
|
+
* @param key - 翻译 key
|
|
712
|
+
* @param values - 插值参数
|
|
713
|
+
*/
|
|
714
|
+
t(key: string, values?: Record<string, string | number>): string;
|
|
715
|
+
/**
|
|
716
|
+
* 复数形式翻译
|
|
717
|
+
* @param key - 翻译 key 前缀
|
|
718
|
+
* @param count - 数量
|
|
719
|
+
* @param values - 附加插值
|
|
720
|
+
*/
|
|
721
|
+
plural(key: string, count: number, values?: Record<string, string | number>): string;
|
|
722
|
+
/** 当前 locale(如 "zh-Hans" / "en-US") */
|
|
723
|
+
readonly locale: string;
|
|
724
|
+
}
|
|
725
|
+
/** 文本方向 */
|
|
726
|
+
type TextDirection = "ltr" | "rtl";
|
|
727
|
+
/** HTML 语言属性 */
|
|
728
|
+
interface LocaleAttributes {
|
|
729
|
+
/** BCP 47 语言标签 */
|
|
730
|
+
lang: string;
|
|
731
|
+
/** 文本方向 */
|
|
732
|
+
dir: TextDirection;
|
|
733
|
+
}
|
|
734
|
+
/** Locale 信息 */
|
|
735
|
+
interface LocaleInfo {
|
|
736
|
+
/** 语言代码(如 "zh-Hans", "en") */
|
|
737
|
+
language: string;
|
|
738
|
+
/** 地区/Storefront 代码(如 "CN", "US") */
|
|
739
|
+
region?: string;
|
|
740
|
+
/** BCP 47 完整标签 */
|
|
741
|
+
bcp47: string;
|
|
742
|
+
/** 文本方向 */
|
|
743
|
+
dir: TextDirection;
|
|
744
|
+
}
|
|
745
|
+
//#endregion
|
|
746
|
+
//#region ../core/src/prefetched-intents/prefetched-intents.d.ts
|
|
747
|
+
/** 预获取的 Intent-Data 对 */
|
|
748
|
+
interface PrefetchedIntent {
|
|
749
|
+
intent: Intent;
|
|
750
|
+
data: unknown;
|
|
751
|
+
}
|
|
752
|
+
declare class PrefetchedIntents {
|
|
753
|
+
private intents;
|
|
754
|
+
private constructor();
|
|
755
|
+
/** 从 PrefetchedIntent 数组创建缓存实例 */
|
|
756
|
+
static fromArray(items: PrefetchedIntent[]): PrefetchedIntents;
|
|
757
|
+
/** 创建空缓存实例 */
|
|
758
|
+
static empty(): PrefetchedIntents;
|
|
759
|
+
/**
|
|
760
|
+
* 获取缓存的 Intent 结果(一次性使用)。
|
|
761
|
+
* 命中后从缓存中删除。
|
|
762
|
+
*/
|
|
763
|
+
get<T>(intent: Intent<T>): T | undefined;
|
|
764
|
+
/** 检查缓存中是否有某个 Intent 的数据 */
|
|
765
|
+
has(intent: Intent): boolean;
|
|
766
|
+
/** 缓存中的条目数 */
|
|
767
|
+
get size(): number;
|
|
768
|
+
}
|
|
769
|
+
//#endregion
|
|
770
|
+
//#region ../core/src/framework.d.ts
|
|
771
|
+
/** Framework 初始化配置 */
|
|
772
|
+
interface FrameworkConfig extends MakeDependenciesOptions {
|
|
773
|
+
setupRoutes?: (router: Router) => void;
|
|
774
|
+
prefetchedIntents?: PrefetchedIntents;
|
|
775
|
+
}
|
|
776
|
+
declare class Framework {
|
|
777
|
+
readonly container: Container;
|
|
778
|
+
readonly intentDispatcher: IntentDispatcher;
|
|
779
|
+
readonly actionDispatcher: ActionDispatcher;
|
|
780
|
+
readonly router: Router;
|
|
781
|
+
readonly prefetchedIntents: PrefetchedIntents;
|
|
782
|
+
private readonly beforeGuards;
|
|
783
|
+
private readonly afterGuards;
|
|
784
|
+
private _logger?;
|
|
785
|
+
private constructor();
|
|
786
|
+
/** 创建并初始化 Framework 实例 */
|
|
787
|
+
static create(config?: FrameworkConfig): Framework;
|
|
788
|
+
private getLogger;
|
|
789
|
+
/** 分发 Intent — 获取页面数据 */
|
|
790
|
+
dispatch<T>(intent: Intent<T>): Promise<T>;
|
|
791
|
+
/** 执行 Action — 处理用户交互 */
|
|
792
|
+
perform(action: Action): Promise<void>;
|
|
793
|
+
/** 路由 URL — 将 URL 解析为 Intent + Action */
|
|
794
|
+
routeUrl(url: string): Promise<RouteMatch | null>;
|
|
795
|
+
/** 记录页面访问事件 */
|
|
796
|
+
didEnterPage(page: BasePage): void;
|
|
797
|
+
/** 获取 locale 信息(如果已配置) */
|
|
798
|
+
getLocale(): LocaleAttributes | undefined;
|
|
799
|
+
/** 获取翻译器(如果当前 locale 已经初始化了翻译字典) */
|
|
800
|
+
getTranslator(): Translator | undefined;
|
|
801
|
+
/** 获取平台信息 */
|
|
802
|
+
getPlatform(): PlatformInfo;
|
|
803
|
+
/** 注册 Action 处理器 */
|
|
804
|
+
onAction<A extends Action>(kind: string, handler: ActionHandler<A>): void;
|
|
805
|
+
/** 注册 Intent Controller */
|
|
806
|
+
registerIntent(controller: IntentController): void;
|
|
807
|
+
/** 注册 beforeLoad 守卫(路由匹配后、数据加载前) */
|
|
808
|
+
beforeLoad(guard: BeforeLoadGuard): void;
|
|
809
|
+
/** 注册 afterLoad 守卫(数据加载后、渲染前) */
|
|
810
|
+
afterLoad(guard: AfterLoadGuard): void;
|
|
811
|
+
/** 执行所有 beforeLoad 守卫(全局 → 路由级) */
|
|
812
|
+
runBeforeLoad(ctx: NavigationContext, routeGuards?: BeforeLoadGuard[]): Promise<MiddlewareResult>;
|
|
813
|
+
/** 执行所有 afterLoad 守卫(全局 → 路由级) */
|
|
814
|
+
runAfterLoad(ctx: PostLoadContext, routeGuards?: AfterLoadGuard[]): Promise<MiddlewareResult>;
|
|
815
|
+
/** 销毁 Framework 实例 */
|
|
816
|
+
dispose(): void;
|
|
817
|
+
}
|
|
818
|
+
//#endregion
|
|
819
|
+
//#region ../core/src/models/safe-error-page.d.ts
|
|
820
|
+
interface SafeErrorPageOptions {
|
|
821
|
+
/** HTTP-style status code used in the page title (e.g. 404, 500). */
|
|
822
|
+
status: number;
|
|
823
|
+
/**
|
|
824
|
+
* The message users / client code may safely see in production. Should
|
|
825
|
+
* contain no stack, file paths, hostnames, or secrets.
|
|
826
|
+
*/
|
|
827
|
+
publicMessage: string;
|
|
828
|
+
/**
|
|
829
|
+
* Optional error / debug payload to surface only in non-production. In
|
|
830
|
+
* production this is dropped entirely; only `publicMessage` is exposed.
|
|
831
|
+
*/
|
|
832
|
+
devError?: unknown;
|
|
833
|
+
/**
|
|
834
|
+
* Override the production detection. Pass `true` to force the prod-safe
|
|
835
|
+
* variant (drops devError). Defaults to detecting `process.env.NODE_ENV`.
|
|
836
|
+
*/
|
|
837
|
+
isProduction?: boolean;
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Build a BasePage for an error condition. In production, only `publicMessage`
|
|
841
|
+
* makes it into the page. In dev, `devError` (if provided) is appended.
|
|
842
|
+
*/
|
|
843
|
+
declare function safeErrorPage(options: SafeErrorPageOptions): BasePage;
|
|
844
|
+
//#endregion
|
|
845
|
+
//#region ../core/src/models/shelf.d.ts
|
|
846
|
+
interface BaseShelf {
|
|
847
|
+
id: string;
|
|
848
|
+
shelfType: string;
|
|
849
|
+
title?: string;
|
|
850
|
+
subtitle?: string;
|
|
851
|
+
seeAllAction?: Action;
|
|
852
|
+
isHorizontal?: boolean;
|
|
853
|
+
}
|
|
854
|
+
interface BaseItem {
|
|
855
|
+
id: string;
|
|
856
|
+
itemType: string;
|
|
857
|
+
clickAction?: Action;
|
|
858
|
+
}
|
|
859
|
+
//#endregion
|
|
860
|
+
//#region ../core/src/prefetched-intents/stable-stringify.d.ts
|
|
861
|
+
/**
|
|
862
|
+
* stableStringify — 确定性 JSON 序列化(keys 按字母排序)
|
|
863
|
+
*
|
|
864
|
+
* 用作缓存 key:相同内容的对象始终产生相同字符串。
|
|
865
|
+
*/
|
|
866
|
+
declare function stableStringify(obj: unknown): string;
|
|
867
|
+
//#endregion
|
|
868
|
+
//#region ../core/src/http/client.d.ts
|
|
869
|
+
/**
|
|
870
|
+
* HttpClient — 通用 HTTP 客户端基类
|
|
871
|
+
*
|
|
872
|
+
* 为 API Client 提供标准化的 HTTP 请求能力。
|
|
873
|
+
* 子类继承后只需关注业务端点定义,不需要重复实现 fetch / JSON 解析 / 错误处理。
|
|
874
|
+
*
|
|
875
|
+
* 默认安全:拒绝向内网 / loopback / 保留地址发请求(SSRF 防御)。应用层
|
|
876
|
+
* 显式 opt-out 用 `allowInternalHosts: true`。详见 host-guard.ts。
|
|
877
|
+
*/
|
|
878
|
+
/** HTTP 请求错误 */
|
|
879
|
+
declare class HttpError extends Error {
|
|
880
|
+
readonly status: number;
|
|
881
|
+
readonly statusText: string;
|
|
882
|
+
readonly body?: string | undefined;
|
|
883
|
+
constructor(status: number, statusText: string, body?: string | undefined);
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* SSRF 防护拦截到不安全的目标地址时抛出。应用层可以 catch 它来给出业务友好的错误,
|
|
887
|
+
* 不需要靠 message 字符串匹配。
|
|
888
|
+
*/
|
|
889
|
+
declare class HostGuardError extends Error {
|
|
890
|
+
readonly url: string;
|
|
891
|
+
readonly reason: string;
|
|
892
|
+
constructor(url: string, reason: string);
|
|
893
|
+
}
|
|
894
|
+
/** 请求拦截器 — 在发送前修改请求 */
|
|
895
|
+
interface RequestInterceptor {
|
|
896
|
+
(url: string, init: RequestInit): RequestInit | Promise<RequestInit>;
|
|
897
|
+
}
|
|
898
|
+
/** 响应拦截器 — 在解析前修改响应 */
|
|
899
|
+
interface ResponseInterceptor {
|
|
900
|
+
(response: Response, url: string): Response | Promise<Response>;
|
|
901
|
+
}
|
|
902
|
+
/** HttpClient 构造配置 */
|
|
903
|
+
interface HttpClientConfig {
|
|
904
|
+
/** API base URL(如 "/api" 或 "https://example.com/api") */
|
|
905
|
+
baseUrl: string;
|
|
906
|
+
/** 默认请求头 */
|
|
907
|
+
defaultHeaders?: Record<string, string>;
|
|
908
|
+
/** 自定义 fetch 实现(便于测试或 SSR) */
|
|
909
|
+
fetch?: typeof globalThis.fetch;
|
|
910
|
+
/** 请求拦截器(按注册顺序执行) */
|
|
911
|
+
requestInterceptors?: RequestInterceptor[];
|
|
912
|
+
/** 响应拦截器(按注册顺序执行) */
|
|
913
|
+
responseInterceptors?: ResponseInterceptor[];
|
|
914
|
+
/**
|
|
915
|
+
* 是否允许向私有 / loopback / 保留 IP 段发请求。
|
|
916
|
+
*
|
|
917
|
+
* **默认 false** —— 阻止内网穿透(SSRF)。如果你的服务正常需要打内网(如
|
|
918
|
+
* 微服务对内 API、127.0.0.1 上的开发依赖),把它设为 true 显式 opt-out,并
|
|
919
|
+
* 自己做来源校验。
|
|
920
|
+
*/
|
|
921
|
+
allowInternalHosts?: boolean;
|
|
922
|
+
/**
|
|
923
|
+
* 是否在请求前 DNS 解析 hostname 并对解析结果做 IP 段校验。
|
|
924
|
+
*
|
|
925
|
+
* **默认 true**(仅 Node 环境有效;浏览器静默跳过)。配合 `allowInternalHosts`
|
|
926
|
+
* 防御 DNS rebinding:如果 hostname 不是 IP 字面量,框架会 resolve 它的 A/AAAA
|
|
927
|
+
* 记录并按 IP 段校验。`false` 关闭只剩 IP 字面量同步校验。
|
|
928
|
+
*/
|
|
929
|
+
validateDns?: boolean;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* 通用 HTTP 客户端基类
|
|
933
|
+
*
|
|
934
|
+
* 使用方式: 创建子类继承 HttpClient,定义业务方法调用 this.get() / this.post() 等。
|
|
935
|
+
*
|
|
936
|
+
* @example
|
|
937
|
+
* ```ts
|
|
938
|
+
* class MyApiClient extends HttpClient {
|
|
939
|
+
* async getUser(id: string) {
|
|
940
|
+
* return this.get<User>(`/users/${id}`);
|
|
941
|
+
* }
|
|
942
|
+
* }
|
|
943
|
+
* ```
|
|
944
|
+
*/
|
|
945
|
+
declare abstract class HttpClient {
|
|
946
|
+
protected readonly baseUrl: string;
|
|
947
|
+
protected readonly defaultHeaders: Record<string, string>;
|
|
948
|
+
protected readonly fetchFn: typeof globalThis.fetch;
|
|
949
|
+
private readonly requestInterceptors;
|
|
950
|
+
private readonly responseInterceptors;
|
|
951
|
+
private readonly allowInternalHosts;
|
|
952
|
+
private readonly validateDns;
|
|
953
|
+
constructor(config: HttpClientConfig);
|
|
954
|
+
/** 动态添加请求拦截器 */
|
|
955
|
+
useRequestInterceptor(interceptor: RequestInterceptor): this;
|
|
956
|
+
/** 动态添加响应拦截器 */
|
|
957
|
+
useResponseInterceptor(interceptor: ResponseInterceptor): this;
|
|
958
|
+
/** GET 请求,返回解析后的 JSON */
|
|
959
|
+
protected get<T>(path: string, params?: Record<string, string>): Promise<T>;
|
|
960
|
+
/** POST 请求,自动序列化 body 为 JSON */
|
|
961
|
+
protected post<T>(path: string, body?: unknown, params?: Record<string, string>): Promise<T>;
|
|
962
|
+
/** PUT 请求 */
|
|
963
|
+
protected put<T>(path: string, body?: unknown, params?: Record<string, string>): Promise<T>;
|
|
964
|
+
/** DELETE 请求 */
|
|
965
|
+
protected del<T>(path: string, params?: Record<string, string>): Promise<T>;
|
|
966
|
+
/**
|
|
967
|
+
* 底层请求方法 — 子类可覆写以自定义行为
|
|
968
|
+
*
|
|
969
|
+
* 自动处理:
|
|
970
|
+
* - URL 拼接 (baseUrl + path + params)
|
|
971
|
+
* - SSRF 防护(IP 字面量同步校验 + 可选 DNS 解析校验)
|
|
972
|
+
* - 默认 headers 合并
|
|
973
|
+
* - JSON body 序列化
|
|
974
|
+
* - 响应 JSON 解析
|
|
975
|
+
* - 非 2xx 状态码抛出 HttpError
|
|
976
|
+
*/
|
|
977
|
+
protected request<T>(method: string, path: string, options?: {
|
|
978
|
+
params?: Record<string, string>;
|
|
979
|
+
body?: unknown;
|
|
980
|
+
headers?: Record<string, string>;
|
|
981
|
+
}): Promise<T>;
|
|
982
|
+
/** 构建完整 URL — 子类可覆写以自定义 URL 拼接逻辑 */
|
|
983
|
+
protected buildUrl(path: string, params?: Record<string, string>): string;
|
|
984
|
+
private enforceHostGuard;
|
|
985
|
+
}
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region ../core/src/http/host-guard.d.ts
|
|
988
|
+
/**
|
|
989
|
+
* host-guard — refuse outbound requests to private / loopback / reserved hosts.
|
|
990
|
+
*
|
|
991
|
+
* Why this exists: `HttpClient` and the raw `fetch` injected via DI both used
|
|
992
|
+
* to accept any URL the application built. SSR controllers that take a
|
|
993
|
+
* user-controlled URL (image proxy, link preview, OAuth callback) could be
|
|
994
|
+
* tricked into fetching `http://127.0.0.1/admin` and embedding the response in
|
|
995
|
+
* the SSR HTML. Round 1 of the adversarial drill walked straight through this;
|
|
996
|
+
* round 2's hand-rolled IPv4 regex was bypassed in five minutes with
|
|
997
|
+
* `[::ffff:7f00:1]`. This module centralises the check so applications can stop
|
|
998
|
+
* re-implementing it (badly).
|
|
999
|
+
*
|
|
1000
|
+
* Coverage (synchronous, IP-literal forms):
|
|
1001
|
+
* - IPv4 dotted-decimal: `127.0.0.1`, `10.0.0.5`, `192.168.1.1`, `172.16.0.1`
|
|
1002
|
+
* - IPv4 zero / link-local / multicast / reserved: `0.0.0.0`, `169.254.0.1`,
|
|
1003
|
+
* `224.0.0.1`, `240.0.0.0/4`
|
|
1004
|
+
* - IPv4 non-dotted forms accepted by some parsers: decimal `2130706433`,
|
|
1005
|
+
* hex `0x7f000001`, octal `0177.0.0.1`
|
|
1006
|
+
* - IPv6 loopback `::1`, link-local `fe80::/10`, ULA `fc00::/7`, unspecified `::`
|
|
1007
|
+
* - IPv4-mapped IPv6: `::ffff:7f00:1`, `::ffff:127.0.0.1`
|
|
1008
|
+
* - Names: `localhost`, `*.localhost`
|
|
1009
|
+
*
|
|
1010
|
+
* NOT covered here (callers can layer on top):
|
|
1011
|
+
* - DNS resolution of arbitrary hostnames — see `validateUrlWithDns` in
|
|
1012
|
+
* server-only callers. DNS rebinding is impossible to fix at this layer
|
|
1013
|
+
* alone; the right pattern is "resolve once, then fetch by IP".
|
|
1014
|
+
* - IDN / homograph attacks — Node's URL parser punycode-encodes hostnames
|
|
1015
|
+
* already, so the hostname this code sees is the ASCII form.
|
|
1016
|
+
*/
|
|
1017
|
+
type HostCheckResult = {
|
|
1018
|
+
ok: true;
|
|
1019
|
+
} | {
|
|
1020
|
+
ok: false;
|
|
1021
|
+
reason: string;
|
|
1022
|
+
};
|
|
1023
|
+
/**
|
|
1024
|
+
* Inspect a hostname string (the `URL.hostname` value, without brackets, port,
|
|
1025
|
+
* or userinfo). Returns `{ok: false}` for anything in a private/loopback/
|
|
1026
|
+
* reserved range; `{ok: true}` if the literal looks like a public address or a
|
|
1027
|
+
* non-IP name (the caller may then DNS-resolve and re-check).
|
|
1028
|
+
*/
|
|
1029
|
+
declare function classifyHost(rawHost: string): HostCheckResult;
|
|
1030
|
+
/**
|
|
1031
|
+
* Convenience wrapper for callers holding a full URL string. Also rejects
|
|
1032
|
+
* non-http(s) schemes (gopher, file, data, …).
|
|
1033
|
+
*/
|
|
1034
|
+
declare function classifyUrl(rawUrl: string): HostCheckResult;
|
|
1035
|
+
//#endregion
|
|
1036
|
+
//#region ../core/src/intents/base-controller.d.ts
|
|
1037
|
+
/**
|
|
1038
|
+
* 抽象 Controller 基类
|
|
1039
|
+
*
|
|
1040
|
+
* 统一处理:
|
|
1041
|
+
* - 类型安全的参数提取 (TParams)
|
|
1042
|
+
* - 返回类型约束 (TResult)
|
|
1043
|
+
* - try/catch 错误处理 + 可选 fallback
|
|
1044
|
+
*
|
|
1045
|
+
* @example
|
|
1046
|
+
* ```ts
|
|
1047
|
+
* class ProductController extends BaseController<{ productId: string }, ProductPage> {
|
|
1048
|
+
* readonly intentId = "product-page";
|
|
1049
|
+
*
|
|
1050
|
+
* async execute(params: { productId: string }, container: Container) {
|
|
1051
|
+
* const api = container.resolve<ApiClient>("api");
|
|
1052
|
+
* return api.getProduct(params.productId);
|
|
1053
|
+
* }
|
|
1054
|
+
*
|
|
1055
|
+
* fallback(params: { productId: string }, error: Error) {
|
|
1056
|
+
* return getMockProduct(params.productId);
|
|
1057
|
+
* }
|
|
1058
|
+
* }
|
|
1059
|
+
* ```
|
|
1060
|
+
*/
|
|
1061
|
+
declare abstract class BaseController<TParams extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> implements IntentController<TResult> {
|
|
1062
|
+
/** Controller 对应的 Intent ID */
|
|
1063
|
+
abstract readonly intentId: string;
|
|
1064
|
+
/**
|
|
1065
|
+
* 执行业务逻辑 — 子类必须实现
|
|
1066
|
+
*
|
|
1067
|
+
* @param params - Intent 参数(已类型化)
|
|
1068
|
+
* @param container - DI 容器
|
|
1069
|
+
* @returns 页面数据
|
|
1070
|
+
*/
|
|
1071
|
+
abstract execute(params: TParams, container: Container): Promise<TResult> | TResult;
|
|
1072
|
+
/**
|
|
1073
|
+
* 错误回退 — 子类可选覆写
|
|
1074
|
+
*
|
|
1075
|
+
* 当 execute() 抛出异常时调用。
|
|
1076
|
+
* 默认行为: 重新抛出原始错误。
|
|
1077
|
+
*
|
|
1078
|
+
* @param params - Intent 参数
|
|
1079
|
+
* @param error - execute() 抛出的错误
|
|
1080
|
+
* @returns 回退数据
|
|
1081
|
+
*/
|
|
1082
|
+
fallback(params: TParams, error: Error): Promise<TResult> | TResult;
|
|
1083
|
+
/**
|
|
1084
|
+
* IntentController.perform() 实现
|
|
1085
|
+
*
|
|
1086
|
+
* 自动 try/catch → fallback 模式。
|
|
1087
|
+
*/
|
|
1088
|
+
perform(intent: Intent<TResult>, container: Container): Promise<TResult>;
|
|
1089
|
+
}
|
|
1090
|
+
//#endregion
|
|
1091
|
+
//#region ../core/src/data/mapper.d.ts
|
|
1092
|
+
/**
|
|
1093
|
+
* Mapper 类型工具 — 标准化数据转换管线
|
|
1094
|
+
*
|
|
1095
|
+
* 提供类型约定和组合函数,让 API 响应 → 页面模型 的转换有统一的签名模式。
|
|
1096
|
+
*/
|
|
1097
|
+
/** 同步映射函数 */
|
|
1098
|
+
type Mapper<TInput, TOutput> = (input: TInput) => TOutput;
|
|
1099
|
+
/** 异步映射函数 */
|
|
1100
|
+
type AsyncMapper<TInput, TOutput> = (input: TInput) => TOutput | Promise<TOutput>;
|
|
1101
|
+
/**
|
|
1102
|
+
* 组合两个同步 Mapper: A → B → C
|
|
1103
|
+
*/
|
|
1104
|
+
declare function pipe<A, B, C>(m1: Mapper<A, B>, m2: Mapper<B, C>): Mapper<A, C>;
|
|
1105
|
+
/**
|
|
1106
|
+
* 组合三个同步 Mapper: A → B → C → D
|
|
1107
|
+
*/
|
|
1108
|
+
declare function pipe<A, B, C, D>(m1: Mapper<A, B>, m2: Mapper<B, C>, m3: Mapper<C, D>): Mapper<A, D>;
|
|
1109
|
+
/**
|
|
1110
|
+
* 组合四个同步 Mapper: A → B → C → D → E
|
|
1111
|
+
*/
|
|
1112
|
+
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>;
|
|
1113
|
+
/**
|
|
1114
|
+
* 组合任意数量的同步 Mapper
|
|
1115
|
+
*/
|
|
1116
|
+
declare function pipe(...mappers: Mapper<unknown, unknown>[]): Mapper<unknown, unknown>;
|
|
1117
|
+
/**
|
|
1118
|
+
* 组合两个可能异步的 Mapper: A → B → C
|
|
1119
|
+
*/
|
|
1120
|
+
declare function pipeAsync<A, B, C>(m1: AsyncMapper<A, B>, m2: AsyncMapper<B, C>): AsyncMapper<A, C>;
|
|
1121
|
+
/**
|
|
1122
|
+
* 组合三个可能异步的 Mapper
|
|
1123
|
+
*/
|
|
1124
|
+
declare function pipeAsync<A, B, C, D>(m1: AsyncMapper<A, B>, m2: AsyncMapper<B, C>, m3: AsyncMapper<C, D>): AsyncMapper<A, D>;
|
|
1125
|
+
/**
|
|
1126
|
+
* 将一个 Mapper 应用到数组的每个元素
|
|
1127
|
+
*/
|
|
1128
|
+
declare function mapEach<TInput, TOutput>(mapper: Mapper<TInput, TOutput>): Mapper<TInput[], TOutput[]>;
|
|
1129
|
+
//#endregion
|
|
1130
|
+
//#region ../core/src/navigation/types.d.ts
|
|
1131
|
+
/**
|
|
1132
|
+
* 导航树里 `Page` 的别名 —— 运行期 dispatch 始终产出 `BasePage`,
|
|
1133
|
+
* 但导航层对内容无关,字段语义由应用决定。
|
|
1134
|
+
*/
|
|
1135
|
+
type Page = BasePage;
|
|
1136
|
+
/** 导航节点 Kind 常量 */
|
|
1137
|
+
declare const NAVIGATION_NODE_KINDS: {
|
|
1138
|
+
readonly LEAF: "leaf";
|
|
1139
|
+
readonly STACK: "stack";
|
|
1140
|
+
readonly TABS: "tabs";
|
|
1141
|
+
readonly SPLIT: "split";
|
|
1142
|
+
};
|
|
1143
|
+
/** 所有导航节点 Kind 的联合类型 */
|
|
1144
|
+
type NavigationNodeKind = (typeof NAVIGATION_NODE_KINDS)[keyof typeof NAVIGATION_NODE_KINDS];
|
|
1145
|
+
/** 叶子:一个具体导航目标 */
|
|
1146
|
+
interface LeafNode {
|
|
1147
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.LEAF;
|
|
1148
|
+
readonly intent: string;
|
|
1149
|
+
readonly params: RouteParams;
|
|
1150
|
+
}
|
|
1151
|
+
/** 栈:有序路径,entries[0]=根,末尾=栈顶(可见) */
|
|
1152
|
+
interface StackNode {
|
|
1153
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.STACK;
|
|
1154
|
+
readonly entries: readonly NavigationNode[];
|
|
1155
|
+
}
|
|
1156
|
+
/** Tabs:并列分支 + 当前激活键 + 稳定顺序;仅激活分支可见 */
|
|
1157
|
+
interface TabsNode {
|
|
1158
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.TABS;
|
|
1159
|
+
readonly active: string;
|
|
1160
|
+
readonly order: readonly string[];
|
|
1161
|
+
readonly branches: Readonly<Record<string, NavigationNode>>;
|
|
1162
|
+
}
|
|
1163
|
+
/** Split 列:列 id + 该列内容(undefined = 尚未选择) */
|
|
1164
|
+
interface SplitColumn {
|
|
1165
|
+
readonly id: string;
|
|
1166
|
+
readonly content: NavigationNode | undefined;
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Split 列可见性,对标 SwiftUI `NavigationSplitViewVisibility`。
|
|
1170
|
+
*
|
|
1171
|
+
* 这是**可绑定 / 可序列化 / 可恢复的导航状态**(不是渲染样式):它决定哪些列算「可见」,
|
|
1172
|
+
* 进而影响 `collectVisibleDestinations` 与 SSR 预取——例如深链到 `detailOnly` 时服务端只预取 detail 列。
|
|
1173
|
+
*
|
|
1174
|
+
* - `automatic`(缺省):框架不裁剪,所有有内容的列都可见(SSR 端无视口信息时的安全默认;客户端再按视口自适应)。
|
|
1175
|
+
* - `all`:显式所有列可见(语义同 automatic 的全列)。
|
|
1176
|
+
* - `doubleColumn`:仅首列 + 末列可见(三列时隐藏中间 content 列)。
|
|
1177
|
+
* - `detailOnly`:仅末列(detail)可见。
|
|
1178
|
+
*
|
|
1179
|
+
* 注意:compact 视口塌缩成单栈(SwiftUI 的 `preferredCompactColumn`)是视口反应式的纯渲染决策,
|
|
1180
|
+
* 框架不建模,交给应用按 `getPlatform()` / 视口自行处理。
|
|
1181
|
+
*/
|
|
1182
|
+
declare const SPLIT_VISIBILITIES: {
|
|
1183
|
+
readonly AUTOMATIC: "automatic";
|
|
1184
|
+
readonly ALL: "all";
|
|
1185
|
+
readonly DOUBLE_COLUMN: "doubleColumn";
|
|
1186
|
+
readonly DETAIL_ONLY: "detailOnly";
|
|
1187
|
+
};
|
|
1188
|
+
/** Split 列可见性的联合类型 */
|
|
1189
|
+
type SplitVisibility = (typeof SPLIT_VISIBILITIES)[keyof typeof SPLIT_VISIBILITIES];
|
|
1190
|
+
/**
|
|
1191
|
+
* Split:多列并存,列间通过 selectColumn 设置后续列内容。
|
|
1192
|
+
* `visibility` 决定哪些列算可见(缺省 `automatic` = 全列),是可序列化的导航状态。
|
|
1193
|
+
*/
|
|
1194
|
+
interface SplitNode {
|
|
1195
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.SPLIT;
|
|
1196
|
+
readonly columns: readonly SplitColumn[];
|
|
1197
|
+
readonly visibility?: SplitVisibility;
|
|
1198
|
+
}
|
|
1199
|
+
/** 所有导航节点的联合类型 */
|
|
1200
|
+
type NavigationNode = LeafNode | StackNode | TabsNode | SplitNode;
|
|
1201
|
+
/** 指向树中某节点的路径(从根到目标)的一步 */
|
|
1202
|
+
type NavigationPathStep = {
|
|
1203
|
+
readonly kind: "stack-entry";
|
|
1204
|
+
readonly index: number;
|
|
1205
|
+
} | {
|
|
1206
|
+
readonly kind: "tab";
|
|
1207
|
+
readonly key: string;
|
|
1208
|
+
} | {
|
|
1209
|
+
readonly kind: "column";
|
|
1210
|
+
readonly id: string;
|
|
1211
|
+
};
|
|
1212
|
+
/** 指向树中某节点的完整路径(从根到目标) */
|
|
1213
|
+
type NavigationPath = readonly NavigationPathStep[];
|
|
1214
|
+
/** 单个可见目标的解析结果 */
|
|
1215
|
+
interface ResolvedDestination {
|
|
1216
|
+
readonly intent: string;
|
|
1217
|
+
readonly params: RouteParams;
|
|
1218
|
+
readonly page: Page;
|
|
1219
|
+
readonly status?: number;
|
|
1220
|
+
}
|
|
1221
|
+
/** 导航快照:当前树 + 所有可见目标解析结果(顺序与 collectVisibleDestinations 一致) */
|
|
1222
|
+
interface NavigationSnapshot {
|
|
1223
|
+
readonly tree: NavigationNode;
|
|
1224
|
+
readonly destinations: readonly ResolvedDestination[];
|
|
1225
|
+
}
|
|
1226
|
+
/** 错误类型:序列化 / 路径 / 操作非法时抛出 */
|
|
1227
|
+
declare class NavigationError extends Error {
|
|
1228
|
+
constructor(message: string);
|
|
1229
|
+
}
|
|
1230
|
+
//#endregion
|
|
1231
|
+
//#region ../core/src/navigation/nodes.d.ts
|
|
1232
|
+
/** 构造叶子节点(一个具体导航目标)。 */
|
|
1233
|
+
declare function leaf(intent: string, params?: RouteParams): LeafNode;
|
|
1234
|
+
/**
|
|
1235
|
+
* 构造栈节点。
|
|
1236
|
+
* 接受单个根节点(栈仅含根)或一个 entries 数组(entries[0]=根,末尾=栈顶)。
|
|
1237
|
+
*
|
|
1238
|
+
* @example
|
|
1239
|
+
* stack(leaf("home")) // 单根栈
|
|
1240
|
+
* stack([leaf("home"), leaf("detail")]) // 根 + 栈顶
|
|
1241
|
+
*/
|
|
1242
|
+
declare function stack(rootOrEntries: NavigationNode | readonly NavigationNode[]): StackNode;
|
|
1243
|
+
/** tabs 构造选项 */
|
|
1244
|
+
interface TabsInit {
|
|
1245
|
+
/** 当前激活分支键 */
|
|
1246
|
+
readonly active: string;
|
|
1247
|
+
/** 分支映射(键 → 子节点) */
|
|
1248
|
+
readonly branches: Readonly<Record<string, NavigationNode>>;
|
|
1249
|
+
/** 稳定顺序;缺省时按 branches 的插入顺序推导 */
|
|
1250
|
+
readonly order?: readonly string[];
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* 构造 Tabs 节点。
|
|
1254
|
+
* 缺省 `order` 时按 `branches` 的插入顺序(`Object.keys`)推导稳定顺序。
|
|
1255
|
+
*/
|
|
1256
|
+
declare function tabs(init: TabsInit): TabsNode;
|
|
1257
|
+
/** split 列初始化(content 可缺省 = 尚未选择) */
|
|
1258
|
+
interface SplitColumnInit {
|
|
1259
|
+
readonly id: string;
|
|
1260
|
+
readonly content?: NavigationNode;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* 构造 Split 节点(多列并存)。
|
|
1264
|
+
* `visibility` 缺省(不写字段)等价 `automatic` = 全列可见;
|
|
1265
|
+
* 显式传入时纳入节点状态,影响 `collectVisibleDestinations` 与 SSR 预取。
|
|
1266
|
+
*
|
|
1267
|
+
* @example
|
|
1268
|
+
* split([{ id: "sidebar", content: leaf("folders") }, { id: "detail" }])
|
|
1269
|
+
* split([...], "detailOnly") // 深链:仅 detail 列可见
|
|
1270
|
+
*/
|
|
1271
|
+
declare function split(columns: readonly SplitColumnInit[], visibility?: SplitVisibility): SplitNode;
|
|
1272
|
+
declare function isLeafNode(node: NavigationNode): node is LeafNode;
|
|
1273
|
+
declare function isStackNode(node: NavigationNode): node is StackNode;
|
|
1274
|
+
declare function isTabsNode(node: NavigationNode): node is TabsNode;
|
|
1275
|
+
declare function isSplitNode(node: NavigationNode): node is SplitNode;
|
|
1276
|
+
//#endregion
|
|
1277
|
+
//#region ../core/src/navigation/operations.d.ts
|
|
1278
|
+
/**
|
|
1279
|
+
* 解析「激活路径」:从根沿可见分支一路向下,直到叶子或无法继续。
|
|
1280
|
+
* - leaf:路径在此结束
|
|
1281
|
+
* - stack:进入栈顶 entry
|
|
1282
|
+
* - tabs:进入 active 分支
|
|
1283
|
+
* - split:进入最后一个有内容的列(无任何内容则结束)
|
|
1284
|
+
*/
|
|
1285
|
+
declare function resolveActivePath(tree: NavigationNode): NavigationPath;
|
|
1286
|
+
/**
|
|
1287
|
+
* 按路径定位节点;任一步无效(索引越界 / 键不存在 / 列为空 / kind 不匹配)返回 undefined。
|
|
1288
|
+
*/
|
|
1289
|
+
declare function findNode(tree: NavigationNode, path: NavigationPath): NavigationNode | undefined;
|
|
1290
|
+
/**
|
|
1291
|
+
* 找到 target 处(默认激活路径)「at/under」最近的 StackNode 路径。
|
|
1292
|
+
* 从 target 节点沿激活分支向下,返回第一个遇到的 StackNode 的完整路径;
|
|
1293
|
+
* 找不到则返回 undefined。
|
|
1294
|
+
*/
|
|
1295
|
+
declare function findNearestStack(tree: NavigationNode, path: NavigationPath): NavigationPath | undefined;
|
|
1296
|
+
/**
|
|
1297
|
+
* 收集所有可见的叶子目标(顺序即渲染/解析顺序)。
|
|
1298
|
+
* - leaf → [leaf]
|
|
1299
|
+
* - stack → 栈顶 entry 的可见目标
|
|
1300
|
+
* - tabs → active 分支的可见目标
|
|
1301
|
+
* - split → 每个有内容的列的可见目标,按列序拼接
|
|
1302
|
+
*/
|
|
1303
|
+
declare function collectVisibleDestinations(tree: NavigationNode): readonly LeafNode[];
|
|
1304
|
+
/**
|
|
1305
|
+
* 按 `visibility` 求出一个 split 节点当前**可见**的列(不裁剪空内容列——空 content 由调用方处理)。
|
|
1306
|
+
*
|
|
1307
|
+
* - `automatic`(缺省)/ `all`:全部列。
|
|
1308
|
+
* - `doubleColumn`:首列 + 末列(三列时隐藏中间 content 列;列数 ≤ 2 时等价全部)。
|
|
1309
|
+
* - `detailOnly`:仅末列(detail)。
|
|
1310
|
+
*
|
|
1311
|
+
* 应用渲染时也可用它决定该画哪几列,无需自行重实现可见性映射。
|
|
1312
|
+
*/
|
|
1313
|
+
declare function visibleSplitColumns(node: SplitNode): readonly SplitColumn[];
|
|
1314
|
+
/** 在目标栈(默认激活栈,或 target「at/under」最近的 stack)顶部 push 一个节点。 */
|
|
1315
|
+
declare function push(tree: NavigationNode, node: NavigationNode, target?: NavigationPath): NavigationNode;
|
|
1316
|
+
/** 从 target 处最近的 stack 弹出 count 个 entry(默认 1);绝不弹到根 entry 之下。 */
|
|
1317
|
+
declare function pop(tree: NavigationNode, count?: number, target?: NavigationPath): NavigationNode;
|
|
1318
|
+
/** 把 target 处最近的 stack 弹回到根 entry。 */
|
|
1319
|
+
declare function popToRoot(tree: NavigationNode, target?: NavigationPath): NavigationNode;
|
|
1320
|
+
/** 把 target 处最近的 stack 弹回到指定 index(保留 [0..index])。 */
|
|
1321
|
+
declare function popTo(tree: NavigationNode, index: number, target?: NavigationPath): NavigationNode;
|
|
1322
|
+
/** 替换 target 处最近 stack 的栈顶 entry(栈为空时抛错)。 */
|
|
1323
|
+
declare function replaceTop(tree: NavigationNode, node: NavigationNode, target?: NavigationPath): NavigationNode;
|
|
1324
|
+
/**
|
|
1325
|
+
* 切换 tabs 节点的激活分支。
|
|
1326
|
+
* target 默认为「最近的激活 tabs 节点」;target 必须指向 TabsNode,且 key 必须是已知分支。
|
|
1327
|
+
*/
|
|
1328
|
+
declare function selectTab(tree: NavigationNode, key: string, target?: NavigationPath): NavigationNode;
|
|
1329
|
+
/**
|
|
1330
|
+
* 设置 split 某列内容,并清空它之后的所有列(content 置 undefined)。
|
|
1331
|
+
* target 默认为「最近的激活 split 节点」;columnId 必须是已知列。
|
|
1332
|
+
*/
|
|
1333
|
+
declare function selectColumn(tree: NavigationNode, columnId: string, content: NavigationNode | undefined, target?: NavigationPath): NavigationNode;
|
|
1334
|
+
/**
|
|
1335
|
+
* 设置 split 节点的列可见性(对标 SwiftUI `NavigationSplitViewVisibility`)。
|
|
1336
|
+
* target 默认为「最近的激活 split 节点」。改变 visibility 会影响 `collectVisibleDestinations`,
|
|
1337
|
+
* 进而触发 controller 对「新变可见」的列做 dispatch / SSR 预取(如 detailOnly → all 时补预取 sidebar/content)。
|
|
1338
|
+
*/
|
|
1339
|
+
declare function setVisibility(tree: NavigationNode, visibility: SplitVisibility, target?: NavigationPath): NavigationNode;
|
|
1340
|
+
//#endregion
|
|
1341
|
+
//#region ../core/src/navigation/serialization.d.ts
|
|
1342
|
+
/** 序列化叶子 */
|
|
1343
|
+
interface SerializedLeaf {
|
|
1344
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.LEAF;
|
|
1345
|
+
readonly intent: string;
|
|
1346
|
+
readonly params: RouteParams;
|
|
1347
|
+
}
|
|
1348
|
+
/** 序列化栈 */
|
|
1349
|
+
interface SerializedStack {
|
|
1350
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.STACK;
|
|
1351
|
+
readonly entries: readonly SerializedNavigation[];
|
|
1352
|
+
}
|
|
1353
|
+
/** 序列化 Tabs */
|
|
1354
|
+
interface SerializedTabs {
|
|
1355
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.TABS;
|
|
1356
|
+
readonly active: string;
|
|
1357
|
+
readonly order: readonly string[];
|
|
1358
|
+
readonly branches: Readonly<Record<string, SerializedNavigation>>;
|
|
1359
|
+
}
|
|
1360
|
+
/** 序列化 Split 列(空内容用 null 表示,JSON 安全) */
|
|
1361
|
+
interface SerializedSplitColumn {
|
|
1362
|
+
readonly id: string;
|
|
1363
|
+
readonly content: SerializedNavigation | null;
|
|
1364
|
+
}
|
|
1365
|
+
/** 序列化 Split(visibility 缺省时不写该字段,保持紧凑) */
|
|
1366
|
+
interface SerializedSplit {
|
|
1367
|
+
readonly kind: typeof NAVIGATION_NODE_KINDS.SPLIT;
|
|
1368
|
+
readonly columns: readonly SerializedSplitColumn[];
|
|
1369
|
+
readonly visibility?: SplitVisibility;
|
|
1370
|
+
}
|
|
1371
|
+
/** 序列化后的导航树(JSON 安全的可辨识联合) */
|
|
1372
|
+
type SerializedNavigation = SerializedLeaf | SerializedStack | SerializedTabs | SerializedSplit;
|
|
1373
|
+
/** 把导航树序列化为 JSON 安全的纯对象。 */
|
|
1374
|
+
declare function serializeNavigation(tree: NavigationNode): SerializedNavigation;
|
|
1375
|
+
/** 导航树的确定性字符串形式(keys 排序);用于稳定缓存键 / 紧凑编码。 */
|
|
1376
|
+
declare function serializeNavigationStable(tree: NavigationNode): string;
|
|
1377
|
+
/** 从 JSON 安全数据还原导航树;结构畸形抛 NavigationError。 */
|
|
1378
|
+
declare function deserializeNavigation(data: unknown): NavigationNode;
|
|
1379
|
+
//#endregion
|
|
1380
|
+
//#region ../core/src/navigation/codec.d.ts
|
|
1381
|
+
/**
|
|
1382
|
+
* Router 的最小读取面 —— codec 只依赖这两个公共方法,避免与 Router 实现耦合。
|
|
1383
|
+
* (`reverse` 为可选:若 Router 将来提供则优先使用。)
|
|
1384
|
+
*/
|
|
1385
|
+
interface NavigationRouterLike {
|
|
1386
|
+
/** 所有已注册路由的 `"pattern → intentId"` 摘要 */
|
|
1387
|
+
getRoutes(): string[];
|
|
1388
|
+
/** 可选:把 intentId + 参数反查为 URL(若实现则 encode 优先使用) */
|
|
1389
|
+
reverse?(intentId: string, params: RouteParams): string | undefined;
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* 导航 URL 编解码器。
|
|
1393
|
+
* - `encode`:把导航树映射为 URL。
|
|
1394
|
+
* - `decode`:把 URL 还原为导航树;无法(或无需)从 URL 同步还原时返回 `undefined`。
|
|
1395
|
+
*/
|
|
1396
|
+
interface NavigationCodec {
|
|
1397
|
+
encode(tree: NavigationNode, router: NavigationRouterLike): string;
|
|
1398
|
+
decode(url: string, router: NavigationRouterLike): NavigationNode | undefined;
|
|
1399
|
+
}
|
|
1400
|
+
/** 默认结构化覆盖参数名(full-state 编码所用的保留 query key)。 */
|
|
1401
|
+
declare const DEFAULT_NAV_PARAM = "__nav";
|
|
1402
|
+
/**
|
|
1403
|
+
* 把整棵树编码为紧凑、URL 安全、确定性的字符串。
|
|
1404
|
+
* 用 `serializeNavigationStable`(keys 排序)保证相同树产出相同串,再做 base64url。
|
|
1405
|
+
*/
|
|
1406
|
+
declare function encodeNavigationTreeParam(tree: NavigationNode): string;
|
|
1407
|
+
/**
|
|
1408
|
+
* 还原 `encodeNavigationTreeParam` 的输出为导航树;畸形输入抛 NavigationError。
|
|
1409
|
+
*/
|
|
1410
|
+
declare function decodeNavigationTreeParam(encoded: string): NavigationNode;
|
|
1411
|
+
/**
|
|
1412
|
+
* 默认 codec:
|
|
1413
|
+
* - `encode`:把激活叶子反查为 URL(`Router.reverse` 或路由摘要反查);激活叶子无对应路由时回退 `"/"`。
|
|
1414
|
+
* - `decode`:仅在 URL 带 `__nav` 结构化覆盖时同步还原整棵树;否则返回 `undefined`,
|
|
1415
|
+
* 交由调用方走 `await router.resolve(url)` 异步重建单个 LeafNode(今天的行为)。
|
|
1416
|
+
*/
|
|
1417
|
+
declare function createActiveLeafCodec(): NavigationCodec;
|
|
1418
|
+
/** full-state codec 选项 */
|
|
1419
|
+
interface FullStateCodecOptions {
|
|
1420
|
+
/** 保留 query 参数名(整树编码所用);默认 `__nav`。 */
|
|
1421
|
+
readonly param?: string;
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* 整树 codec:把整棵树编码进保留 query 参数(默认 `__nav`),支持完整深链。
|
|
1425
|
+
* - `encode`:以激活叶子的 URL 作为基底路径(保留 app 可能依赖的 path/query),
|
|
1426
|
+
* 再写入保留参数承载整棵树。激活叶子无对应路由时基底退化为 `"/"`。
|
|
1427
|
+
* - `decode`:读取保留参数无损还原整棵树;缺失该参数时返回 `undefined`(交由调用方走默认路径)。
|
|
1428
|
+
*/
|
|
1429
|
+
declare function createFullStateCodec(options?: FullStateCodecOptions): NavigationCodec;
|
|
1430
|
+
//#endregion
|
|
1431
|
+
//#region ../core/src/navigation/controller.d.ts
|
|
1432
|
+
/** 导航操作 Kind 常量 */
|
|
1433
|
+
declare const NAVIGATION_OP_KINDS: {
|
|
1434
|
+
readonly PUSH: "push";
|
|
1435
|
+
readonly POP: "pop";
|
|
1436
|
+
readonly POP_TO_ROOT: "popToRoot";
|
|
1437
|
+
readonly POP_TO: "popTo";
|
|
1438
|
+
readonly REPLACE_TOP: "replaceTop";
|
|
1439
|
+
readonly SELECT_TAB: "selectTab";
|
|
1440
|
+
readonly SELECT_COLUMN: "selectColumn";
|
|
1441
|
+
readonly SET_VISIBILITY: "setVisibility";
|
|
1442
|
+
readonly HYDRATE: "hydrate";
|
|
1443
|
+
};
|
|
1444
|
+
/** 所有导航操作 Kind 的联合类型 */
|
|
1445
|
+
type NavigationOpKind = (typeof NAVIGATION_OP_KINDS)[keyof typeof NAVIGATION_OP_KINDS];
|
|
1446
|
+
/** push:在目标栈顶压入一个新 leaf(intent + params)。 */
|
|
1447
|
+
interface PushOperation {
|
|
1448
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.PUSH;
|
|
1449
|
+
readonly intent: string;
|
|
1450
|
+
readonly params?: RouteParams;
|
|
1451
|
+
readonly target?: NavigationPath;
|
|
1452
|
+
}
|
|
1453
|
+
/** pop:从目标栈弹出 count 个 entry(默认 1)。 */
|
|
1454
|
+
interface PopOperation {
|
|
1455
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.POP;
|
|
1456
|
+
readonly count?: number;
|
|
1457
|
+
readonly target?: NavigationPath;
|
|
1458
|
+
}
|
|
1459
|
+
/** popToRoot:把目标栈弹回根 entry。 */
|
|
1460
|
+
interface PopToRootOperation {
|
|
1461
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.POP_TO_ROOT;
|
|
1462
|
+
readonly target?: NavigationPath;
|
|
1463
|
+
}
|
|
1464
|
+
/** popTo:把目标栈弹回指定 index。 */
|
|
1465
|
+
interface PopToOperation {
|
|
1466
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.POP_TO;
|
|
1467
|
+
readonly index: number;
|
|
1468
|
+
readonly target?: NavigationPath;
|
|
1469
|
+
}
|
|
1470
|
+
/** replaceTop:替换目标栈的栈顶为新 leaf。 */
|
|
1471
|
+
interface ReplaceTopOperation {
|
|
1472
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.REPLACE_TOP;
|
|
1473
|
+
readonly intent: string;
|
|
1474
|
+
readonly params?: RouteParams;
|
|
1475
|
+
readonly target?: NavigationPath;
|
|
1476
|
+
}
|
|
1477
|
+
/** selectTab:切换 tabs 节点的激活分支。 */
|
|
1478
|
+
interface SelectTabOperation {
|
|
1479
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.SELECT_TAB;
|
|
1480
|
+
readonly key: string;
|
|
1481
|
+
readonly target?: NavigationPath;
|
|
1482
|
+
}
|
|
1483
|
+
/** selectColumn:设置 split 某列内容(intent 为 undefined 表示清空该列)。 */
|
|
1484
|
+
interface SelectColumnOperation {
|
|
1485
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.SELECT_COLUMN;
|
|
1486
|
+
readonly columnId: string;
|
|
1487
|
+
readonly intent: string | undefined;
|
|
1488
|
+
readonly params?: RouteParams;
|
|
1489
|
+
readonly target?: NavigationPath;
|
|
1490
|
+
}
|
|
1491
|
+
/** setVisibility:设置 split 节点的列可见性(对标 NavigationSplitViewVisibility)。 */
|
|
1492
|
+
interface SetVisibilityOperation {
|
|
1493
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.SET_VISIBILITY;
|
|
1494
|
+
readonly visibility: SplitVisibility;
|
|
1495
|
+
readonly target?: NavigationPath;
|
|
1496
|
+
}
|
|
1497
|
+
/** hydrate:用外部给定的整棵树替换当前树(来自 history/URL 还原)。 */
|
|
1498
|
+
interface HydrateOperation {
|
|
1499
|
+
readonly kind: typeof NAVIGATION_OP_KINDS.HYDRATE;
|
|
1500
|
+
readonly tree: NavigationNode;
|
|
1501
|
+
}
|
|
1502
|
+
/** 所有导航操作的可辨识联合。 */
|
|
1503
|
+
type NavigationOperation = PushOperation | PopOperation | PopToRootOperation | PopToOperation | ReplaceTopOperation | SelectTabOperation | SelectColumnOperation | SetVisibilityOperation | HydrateOperation;
|
|
1504
|
+
/**
|
|
1505
|
+
* 控制器解析单个目标时需要的「环境」——由应用提供。
|
|
1506
|
+
*
|
|
1507
|
+
* 仓库里没有契约所说的 `IntentContext`:dispatch 需要 `Container`,守卫需要
|
|
1508
|
+
* `NavigationContext`(含 url/cookie/header)。所以 `createContext` 在此被建模为
|
|
1509
|
+
* 「给定目标 intent/params,返回构建守卫上下文 + 派发所需的零件」:
|
|
1510
|
+
* - `container`:派发 intent 用(`intentDispatcher.dispatch(intent, container)`)。
|
|
1511
|
+
* - `navigation`:完整的 `NavigationContext`(应用按 SSR/CSR 用
|
|
1512
|
+
* `createServerContext`/`createBrowserContext` 造好传入);缺省时控制器用一个不含
|
|
1513
|
+
* cookie/header 的最小上下文兜底(含 url/path/params/intent/container/isServer,
|
|
1514
|
+
* 其中 isServer 取 `NavigationControllerOptions.isServer`,缺省按运行环境推断)。
|
|
1515
|
+
*
|
|
1516
|
+
* `signal` 暂无消费方(现有 runner 也没有 AbortSignal 管线),仅透传保留。
|
|
1517
|
+
*/
|
|
1518
|
+
interface NavigationContextInput {
|
|
1519
|
+
readonly intent: string;
|
|
1520
|
+
readonly params: RouteParams;
|
|
1521
|
+
readonly signal?: AbortSignal;
|
|
1522
|
+
}
|
|
1523
|
+
/** `createContext` 的返回:派发用的 Container + 守卫用的 NavigationContext(可选)。 */
|
|
1524
|
+
interface NavigationDispatchContext {
|
|
1525
|
+
/** DI 容器 —— intent 派发的必备参数。 */
|
|
1526
|
+
readonly container: Container;
|
|
1527
|
+
/** 守卫上下文;缺省时控制器用最小上下文兜底。 */
|
|
1528
|
+
readonly navigation?: NavigationContext;
|
|
1529
|
+
/** 该目标对应的完整 URL(用于最小兜底上下文的 url/path)。 */
|
|
1530
|
+
readonly url?: string;
|
|
1531
|
+
}
|
|
1532
|
+
/** NavigationController 构造选项。 */
|
|
1533
|
+
interface NavigationControllerOptions {
|
|
1534
|
+
/** Intent 派发器(派发可见目标的 intent → page)。 */
|
|
1535
|
+
readonly intentDispatcher: IntentDispatcher;
|
|
1536
|
+
/** 路由器(beforeLoad rewrite/redirect 时把 URL 重解析为 leaf)。 */
|
|
1537
|
+
readonly router: Router;
|
|
1538
|
+
/** 初始导航树(单 LeafNode = 今天的扁平单页)。 */
|
|
1539
|
+
readonly initial: NavigationNode;
|
|
1540
|
+
/** 应用提供的「目标 → 派发上下文」构建回调。 */
|
|
1541
|
+
readonly createContext: (input: NavigationContextInput) => NavigationDispatchContext;
|
|
1542
|
+
/**
|
|
1543
|
+
* 是否运行在服务端——仅用于 `createContext` 未返回 `navigation` 时的最小兜底上下文,
|
|
1544
|
+
* 决定该上下文的 `isServer` 字段。缺省时按运行环境推断(`typeof window === "undefined"`)。
|
|
1545
|
+
* 应用若已通过 `createContext` 提供完整 `navigation`,此项不生效。
|
|
1546
|
+
*/
|
|
1547
|
+
readonly isServer?: boolean;
|
|
1548
|
+
/** 目标级 beforeLoad 守卫(在全局/路由守卫之外,由控制器对主目标执行)。 */
|
|
1549
|
+
readonly beforeLoad?: readonly BeforeLoadGuard[];
|
|
1550
|
+
/** 目标级 afterLoad 守卫。 */
|
|
1551
|
+
readonly afterLoad?: readonly AfterLoadGuard[];
|
|
1552
|
+
/** SSR 预取缓存(浏览器 hydration 时复用服务端解析结果)。 */
|
|
1553
|
+
readonly prefetched?: PrefetchedIntents;
|
|
1554
|
+
/**
|
|
1555
|
+
* 兜底错误页工厂——dispatch 失败 / deny 时,用它产出该目标的 page。
|
|
1556
|
+
* 缺省用一个最小的 BasePage(pageType="error")。复刻 runner 的 fallback 语义。
|
|
1557
|
+
*/
|
|
1558
|
+
readonly getErrorPage?: (status: number, message: string) => Page;
|
|
1559
|
+
/**
|
|
1560
|
+
* redirect 处理器——beforeLoad/afterLoad 返回 redirect 时调用(SPA 内跳 / 外链)。
|
|
1561
|
+
* 控制器不持有 history,把「怎么跳」交给应用(浏览器侧 → `framework.perform`)。
|
|
1562
|
+
* 缺省为 no-op(该目标不 dispatch、不再跳,仅保留当前页/兜底页)。
|
|
1563
|
+
*/
|
|
1564
|
+
readonly onRedirect?: (redirect: {
|
|
1565
|
+
url: string;
|
|
1566
|
+
status: number;
|
|
1567
|
+
}) => void;
|
|
1568
|
+
}
|
|
1569
|
+
/** 导航控制器对外接口。 */
|
|
1570
|
+
interface NavigationController {
|
|
1571
|
+
/** 当前导航树。 */
|
|
1572
|
+
getTree(): NavigationNode;
|
|
1573
|
+
/** 当前快照(树 + 已解析的可见目标)。 */
|
|
1574
|
+
getSnapshot(): NavigationSnapshot;
|
|
1575
|
+
/** 应用一个声明式操作,重解析并提交,返回新快照。 */
|
|
1576
|
+
apply(op: NavigationOperation): Promise<NavigationSnapshot>;
|
|
1577
|
+
/** 便捷:在激活栈压入新目标。 */
|
|
1578
|
+
push(intent: string, params?: RouteParams, options?: PushOptions): Promise<NavigationSnapshot>;
|
|
1579
|
+
/** 便捷:从激活栈弹出。 */
|
|
1580
|
+
pop(count?: number): Promise<NavigationSnapshot>;
|
|
1581
|
+
/** 便捷:激活栈弹回根。 */
|
|
1582
|
+
popToRoot(): Promise<NavigationSnapshot>;
|
|
1583
|
+
/** 便捷:替换激活栈栈顶。 */
|
|
1584
|
+
replaceTop(intent: string, params?: RouteParams): Promise<NavigationSnapshot>;
|
|
1585
|
+
/** 便捷:切换 tabs 激活分支。 */
|
|
1586
|
+
selectTab(key: string, target?: NavigationPath): Promise<NavigationSnapshot>;
|
|
1587
|
+
/** 便捷:设置 split 列内容(intent=undefined 清空)。 */
|
|
1588
|
+
selectColumn(columnId: string, intent: string | undefined, params?: RouteParams, target?: NavigationPath): Promise<NavigationSnapshot>;
|
|
1589
|
+
/** 便捷:设置 split 列可见性(对标 NavigationSplitViewVisibility);改变可见集会触发新可见列的派发。 */
|
|
1590
|
+
setVisibility(visibility: SplitVisibility, target?: NavigationPath): Promise<NavigationSnapshot>;
|
|
1591
|
+
/** 用外部树替换当前树并重解析(history/URL 还原)。 */
|
|
1592
|
+
hydrate(tree: NavigationNode): Promise<NavigationSnapshot>;
|
|
1593
|
+
/** 订阅快照变更;返回取消订阅函数。 */
|
|
1594
|
+
subscribe(listener: (snapshot: NavigationSnapshot) => void): () => void;
|
|
1595
|
+
/** 解析当前树(首屏 SSR/CSR),提交并返回快照。 */
|
|
1596
|
+
resolve(): Promise<NavigationSnapshot>;
|
|
1597
|
+
}
|
|
1598
|
+
/** `push` 便捷方法的可选项。 */
|
|
1599
|
+
interface PushOptions {
|
|
1600
|
+
readonly target?: NavigationPath;
|
|
1601
|
+
}
|
|
1602
|
+
declare function createNavigationController(options: NavigationControllerOptions): NavigationController;
|
|
1603
|
+
//#endregion
|
|
1604
|
+
//#region ../core/src/bootstrap/define-navigation.d.ts
|
|
1605
|
+
/**
|
|
1606
|
+
* `initial` 既可是一棵静态初始树,也可是按 URL 产出树骨架的工厂。
|
|
1607
|
+
*
|
|
1608
|
+
* - 静态树:所有请求(CSR 首屏 / SSR 无深链回退)都以这棵树为初始结构。
|
|
1609
|
+
* - 工厂 `(url) => NavigationNode | undefined`:按请求 URL 动态决定骨架;返回 `undefined`
|
|
1610
|
+
* 表示「此 URL 无结构化骨架」,SSR 侧据此回退到「`Router.resolve` → 单 LeafNode」
|
|
1611
|
+
* (今天的单页行为)。CSR 侧首屏对工厂传入当前 `window.location` 的 path+query。
|
|
1612
|
+
*/
|
|
1613
|
+
type NavigationInitial = NavigationNode | ((url: string) => NavigationNode | undefined);
|
|
1614
|
+
/**
|
|
1615
|
+
* 浏览器 runner(`startBrowserApp`)期望的导航配置形态。
|
|
1616
|
+
*
|
|
1617
|
+
* 与 `@finesoft/browser` 的 `BrowserNavigationConfig` 结构等价(`initial` 为具体树);
|
|
1618
|
+
* 在 core 中以结构化形状声明,避免 core → browser 的反向依赖。
|
|
1619
|
+
*/
|
|
1620
|
+
interface NavigationBrowserConfig {
|
|
1621
|
+
readonly initial: NavigationNode;
|
|
1622
|
+
readonly codec?: NavigationCodec;
|
|
1623
|
+
readonly beforeLoad?: readonly BeforeLoadGuard[];
|
|
1624
|
+
readonly afterLoad?: readonly AfterLoadGuard[];
|
|
1625
|
+
readonly getErrorPage?: (status: number, message: string) => BasePage;
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* SSR runner(`createSSRNavigationRender` / `ssrRenderNavigation`)期望的导航定义形态。
|
|
1629
|
+
*
|
|
1630
|
+
* 与 `@finesoft/ssr` 的 `SSRNavigationDefinition` 结构等价(`codec` 必填、`initial`
|
|
1631
|
+
* 为骨架工厂);在 core 中以结构化形状声明,避免 core → ssr 的反向依赖。
|
|
1632
|
+
*/
|
|
1633
|
+
interface NavigationSSRDefinition {
|
|
1634
|
+
readonly codec: NavigationCodec;
|
|
1635
|
+
readonly initial?: (url: string) => NavigationNode | undefined;
|
|
1636
|
+
readonly beforeLoad?: readonly BeforeLoadGuard[];
|
|
1637
|
+
readonly afterLoad?: readonly AfterLoadGuard[];
|
|
1638
|
+
}
|
|
1639
|
+
/** `defineNavigation` 的输入声明。 */
|
|
1640
|
+
interface DefineNavigationOptions {
|
|
1641
|
+
/**
|
|
1642
|
+
* 初始导航结构:静态树或按 URL 产出树骨架的工厂。
|
|
1643
|
+
* 单个 `leaf(...)` 树即为今天的扁平单页(向后兼容)。
|
|
1644
|
+
*/
|
|
1645
|
+
readonly initial: NavigationInitial;
|
|
1646
|
+
/**
|
|
1647
|
+
* URL ⇄ 树 编解码器;缺省 `createActiveLeafCodec()`
|
|
1648
|
+
* (URL 只反映激活叶子,整树通过 history/hydration 旁路)。
|
|
1649
|
+
*/
|
|
1650
|
+
readonly codec?: NavigationCodec;
|
|
1651
|
+
/** 导航级 beforeLoad 守卫(控制器对主目标执行,叠加在全局/路由守卫之外)。 */
|
|
1652
|
+
readonly beforeLoad?: readonly BeforeLoadGuard[];
|
|
1653
|
+
/** 导航级 afterLoad 守卫。 */
|
|
1654
|
+
readonly afterLoad?: readonly AfterLoadGuard[];
|
|
1655
|
+
/** dispatch 失败 / deny 时的兜底错误页工厂(仅 CSR runner 直接消费;SSR runner 用其自带的 getErrorPage)。 */
|
|
1656
|
+
readonly getErrorPage?: (status: number, message: string) => BasePage;
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* `defineNavigation` 的产物:规范化后的导航定义。
|
|
1660
|
+
*
|
|
1661
|
+
* 既暴露规范化字段(应用可自取),也提供两个适配器把定义转成各 runner 需要的精确形态。
|
|
1662
|
+
* 字段全部 `readonly`、不可变。
|
|
1663
|
+
*/
|
|
1664
|
+
interface NavigationDefinition {
|
|
1665
|
+
/** 规范化的初始结构(静态树或工厂)。 */
|
|
1666
|
+
readonly initial: NavigationInitial;
|
|
1667
|
+
/** 最终生效的 codec(已套用默认值)。 */
|
|
1668
|
+
readonly codec: NavigationCodec;
|
|
1669
|
+
/** 导航级 beforeLoad 守卫。 */
|
|
1670
|
+
readonly beforeLoad?: readonly BeforeLoadGuard[];
|
|
1671
|
+
/** 导航级 afterLoad 守卫。 */
|
|
1672
|
+
readonly afterLoad?: readonly AfterLoadGuard[];
|
|
1673
|
+
/** 兜底错误页工厂。 */
|
|
1674
|
+
readonly getErrorPage?: (status: number, message: string) => BasePage;
|
|
1675
|
+
/**
|
|
1676
|
+
* 适配为浏览器 runner 配置(`initial` 收敛为具体树)。
|
|
1677
|
+
* `initial` 是工厂时,对 `url`(缺省当前 `window.location`)求值;返回 `undefined`
|
|
1678
|
+
* 时回退到一个最小的占位 leaf(`@finesoft/navigation-root`),保证 bridge 能挂载——
|
|
1679
|
+
* 浏览器首屏随后会用 SSR 注入的真实树 hydrate(见 navigation-bridge)。
|
|
1680
|
+
*/
|
|
1681
|
+
toBrowserConfig(url?: string): NavigationBrowserConfig;
|
|
1682
|
+
/** 适配为 SSR runner 定义(`initial` 收敛为骨架工厂、`codec` 必填)。 */
|
|
1683
|
+
toSSRDefinition(): NavigationSSRDefinition;
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* 声明结构化导航。
|
|
1687
|
+
*
|
|
1688
|
+
* 在 `bootstrap(framework)` 里与 `defineRoutes` 并列调用,返回一个 `NavigationDefinition`,
|
|
1689
|
+
* 由应用分别交给 CSR / SSR runner:
|
|
1690
|
+
*
|
|
1691
|
+
* @example
|
|
1692
|
+
* ```ts
|
|
1693
|
+
* const nav = defineNavigation({
|
|
1694
|
+
* initial: tabs({
|
|
1695
|
+
* active: "home",
|
|
1696
|
+
* branches: { home: stack(leaf("home")), me: stack(leaf("me")) },
|
|
1697
|
+
* }),
|
|
1698
|
+
* beforeLoad: [authGuard],
|
|
1699
|
+
* });
|
|
1700
|
+
*
|
|
1701
|
+
* // CSR
|
|
1702
|
+
* startBrowserApp({ bootstrap, mount, callbacks, navigation: nav.toBrowserConfig() });
|
|
1703
|
+
* // SSR
|
|
1704
|
+
* createSSRNavigationRender({ bootstrap, getErrorPage, renderApp, navigation: nav.toSSRDefinition() });
|
|
1705
|
+
* ```
|
|
1706
|
+
*/
|
|
1707
|
+
declare function defineNavigation(options: DefineNavigationOptions): NavigationDefinition;
|
|
1708
|
+
//#endregion
|
|
1709
|
+
//#region ../core/src/bootstrap/define-routes.d.ts
|
|
1710
|
+
/** 渲染模式 */
|
|
1711
|
+
type RenderMode = "ssr" | "csr" | "prerender";
|
|
1712
|
+
/** 单条路由定义 */
|
|
1713
|
+
interface RouteDefinition<Path extends string = string, P extends ParamsFor<Path> = ParamsFor<Path>, Q extends QuerySchemaMap = QuerySchemaMap> {
|
|
1714
|
+
/** URL pattern (如 "/product/:id") */
|
|
1715
|
+
path: Path;
|
|
1716
|
+
/** Intent ID */
|
|
1717
|
+
intentId: string;
|
|
1718
|
+
/** Controller 实例(可选)。同一 intentId 的多条路由只需在第一条提供。 */
|
|
1719
|
+
controller?: IntentController;
|
|
1720
|
+
/** path 参数 codec;key 必须是 path 中出现的 :param 名 */
|
|
1721
|
+
params?: P;
|
|
1722
|
+
/** query 参数 codec;key 自由 */
|
|
1723
|
+
query?: Q;
|
|
1724
|
+
/** 渲染模式(可选,默认 "ssr") */
|
|
1725
|
+
renderMode?: RenderMode;
|
|
1726
|
+
/** 路由级 beforeLoad 守卫 */
|
|
1727
|
+
beforeLoad?: BeforeLoadGuard[];
|
|
1728
|
+
/** 路由级 afterLoad 守卫 */
|
|
1729
|
+
afterLoad?: AfterLoadGuard[];
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* 构造一条强类型路由定义。
|
|
1733
|
+
* `params` 的 key 受 `path` 字面量约束——写入 path 中不存在的参数名会编译期报错。
|
|
1734
|
+
*
|
|
1735
|
+
* @example
|
|
1736
|
+
* route("/product/:id", { intentId: "product", controller, params: { id: int() } })
|
|
1737
|
+
*/
|
|
1738
|
+
declare function route<const Path extends string, P extends ParamsFor<Path> = ParamsFor<Path>, Q extends QuerySchemaMap = QuerySchemaMap>(path: Path, def: {
|
|
1739
|
+
intentId: string;
|
|
1740
|
+
controller?: IntentController;
|
|
1741
|
+
params?: P;
|
|
1742
|
+
query?: Q;
|
|
1743
|
+
renderMode?: RenderMode;
|
|
1744
|
+
beforeLoad?: BeforeLoadGuard[];
|
|
1745
|
+
afterLoad?: AfterLoadGuard[];
|
|
1746
|
+
}): RouteDefinition;
|
|
1747
|
+
/** defineRoutes 选项 */
|
|
1748
|
+
interface DefineRoutesOptions {
|
|
1749
|
+
/**
|
|
1750
|
+
* 支持的 locale 列表。
|
|
1751
|
+
* 提供后,每条路由会额外注册 `/:locale/path` 版本,
|
|
1752
|
+
* `:locale` 参数自动出现在 `intent.params.locale` 中。
|
|
1753
|
+
* 原始无前缀路径保留作为备选路由。
|
|
1754
|
+
*
|
|
1755
|
+
* @example
|
|
1756
|
+
* ```ts
|
|
1757
|
+
* defineRoutes(framework, routes, { locales: ["zh", "en", "ja"] });
|
|
1758
|
+
* // "/about" → 注册 /about + /zh/about + /en/about + /ja/about
|
|
1759
|
+
* ```
|
|
1760
|
+
*/
|
|
1761
|
+
locales?: string[];
|
|
1762
|
+
}
|
|
1763
|
+
/**
|
|
1764
|
+
* 声明式注册路由和 Controller
|
|
1765
|
+
*
|
|
1766
|
+
* - 自动去重: 同一 intentId 的 controller 只注册一次
|
|
1767
|
+
* - 路由和 controller 在同一个配置数组中,方便检查一致性
|
|
1768
|
+
*
|
|
1769
|
+
* @example
|
|
1770
|
+
* ```ts
|
|
1771
|
+
* defineRoutes(framework, [
|
|
1772
|
+
* { path: "/", intentId: "home", controller: new HomeController() },
|
|
1773
|
+
* { path: "/product/:id", intentId: "product", controller: new ProductController() },
|
|
1774
|
+
* { path: "/search", intentId: "search", controller: new SearchController() },
|
|
1775
|
+
* { path: "/charts/:type", intentId: "charts", controller: new ChartsController() },
|
|
1776
|
+
* { path: "/charts", intentId: "charts" }, // 同 intentId,不需要重复 controller
|
|
1777
|
+
* ]);
|
|
1778
|
+
* ```
|
|
1779
|
+
*/
|
|
1780
|
+
declare function defineRoutes(framework: Framework, definitions: RouteDefinition[], options?: DefineRoutesOptions): void;
|
|
1781
|
+
//#endregion
|
|
1782
|
+
//#region ../core/src/utils/lru-map.d.ts
|
|
1783
|
+
/**
|
|
1784
|
+
* LruMap — 固定容量的 LRU 缓存
|
|
1785
|
+
*/
|
|
1786
|
+
declare class LruMap<K, V> {
|
|
1787
|
+
private map;
|
|
1788
|
+
private readonly capacity;
|
|
1789
|
+
constructor(capacity: number);
|
|
1790
|
+
get(key: K): V | undefined;
|
|
1791
|
+
set(key: K, value: V): void;
|
|
1792
|
+
has(key: K): boolean;
|
|
1793
|
+
delete(key: K): boolean;
|
|
1794
|
+
get size(): number;
|
|
1795
|
+
clear(): void;
|
|
1796
|
+
}
|
|
1797
|
+
//#endregion
|
|
1798
|
+
//#region ../core/src/utils/optional.d.ts
|
|
1799
|
+
/**
|
|
1800
|
+
* Optional 类型工具
|
|
1801
|
+
*/
|
|
1802
|
+
type None = null | undefined;
|
|
1803
|
+
type Optional<T> = T | None;
|
|
1804
|
+
declare function isSome<T>(value: Optional<T>): value is T;
|
|
1805
|
+
declare function isNone<T>(value: Optional<T>): value is None;
|
|
1806
|
+
//#endregion
|
|
1807
|
+
//#region ../core/src/utils/pwa.d.ts
|
|
1808
|
+
/**
|
|
1809
|
+
* PWA Display Mode 检测
|
|
1810
|
+
*
|
|
1811
|
+
* 检测当前应用是否以 PWA 模式运行。
|
|
1812
|
+
*/
|
|
1813
|
+
type PWADisplayMode = "standalone" | "twa" | "browser";
|
|
1814
|
+
/**
|
|
1815
|
+
* 检测 PWA display mode
|
|
1816
|
+
*
|
|
1817
|
+
* - `standalone`: 已安装的 PWA(通过 Add to Home Screen)
|
|
1818
|
+
* - `twa`: Trusted Web Activity(Android 原生壳)
|
|
1819
|
+
* - `browser`: 普通浏览器标签页
|
|
1820
|
+
*/
|
|
1821
|
+
declare function getPWADisplayMode(): PWADisplayMode;
|
|
1822
|
+
//#endregion
|
|
1823
|
+
//#region ../core/src/utils/url.d.ts
|
|
1824
|
+
/**
|
|
1825
|
+
* URL 工具函数
|
|
1826
|
+
*/
|
|
1827
|
+
/** 移除 URL scheme (https://, http://) */
|
|
1828
|
+
declare function removeScheme(url: string): string;
|
|
1829
|
+
/** 移除 URL host 部分,保留路径 */
|
|
1830
|
+
declare function removeHost(url: string): string;
|
|
1831
|
+
/** 移除 query 参数 */
|
|
1832
|
+
declare function removeQueryParams(url: string): string;
|
|
1833
|
+
/** 获取 URL 的基础路径(无 query、hash) */
|
|
1834
|
+
declare function getBaseUrl(url: string): string;
|
|
1835
|
+
/** 构建 URL(路径 + query 参数) */
|
|
1836
|
+
declare function buildUrl(path: string, params?: Record<string, string | undefined>): string;
|
|
1837
|
+
//#endregion
|
|
1838
|
+
//#region ../core/src/utils/uuid.d.ts
|
|
1839
|
+
/**
|
|
1840
|
+
* UUID v4 生成器
|
|
1841
|
+
*/
|
|
1842
|
+
declare function generateUuid(): string;
|
|
1843
|
+
//#endregion
|
|
1844
|
+
//#region ../core/src/middleware/context.d.ts
|
|
1845
|
+
interface ServerContextOptions {
|
|
1846
|
+
url: string;
|
|
1847
|
+
intent: Intent;
|
|
1848
|
+
container: Container;
|
|
1849
|
+
/** 原始 Request 对象(提取 cookie 和 header) */
|
|
1850
|
+
request?: Request;
|
|
1851
|
+
}
|
|
1852
|
+
/** 从 Request 对象构建服务端上下文 */
|
|
1853
|
+
declare function createServerContext(options: ServerContextOptions): NavigationContext;
|
|
1854
|
+
interface BrowserContextOptions {
|
|
1855
|
+
url: string;
|
|
1856
|
+
intent: Intent;
|
|
1857
|
+
container: Container;
|
|
1858
|
+
}
|
|
1859
|
+
/** 从 document.cookie 构建浏览器端上下文 */
|
|
1860
|
+
declare function createBrowserContext(options: BrowserContextOptions): NavigationContext;
|
|
1861
|
+
//#endregion
|
|
1862
|
+
//#region ../core/src/middleware/pipeline.d.ts
|
|
1863
|
+
/** 执行 beforeLoad 守卫链 */
|
|
1864
|
+
declare function runBeforeLoadGuards(guards: BeforeLoadGuard[], ctx: NavigationContext): Promise<MiddlewareResult>;
|
|
1865
|
+
/** 执行 afterLoad 守卫链 */
|
|
1866
|
+
declare function runAfterLoadGuards(guards: AfterLoadGuard[], ctx: PostLoadContext): Promise<MiddlewareResult>;
|
|
1867
|
+
//#endregion
|
|
1868
|
+
//#region ../core/src/metrics/composite-recorder.d.ts
|
|
1869
|
+
declare class CompositeEventRecorder implements EventRecorder {
|
|
1870
|
+
private readonly recorders;
|
|
1871
|
+
constructor(recorders: EventRecorder[]);
|
|
1872
|
+
record(type: string, fields?: Record<string, unknown>): void;
|
|
1873
|
+
flush(): Promise<void>;
|
|
1874
|
+
destroy(): void;
|
|
1875
|
+
}
|
|
1876
|
+
//#endregion
|
|
1877
|
+
//#region ../core/src/metrics/console-recorder.d.ts
|
|
1878
|
+
declare class ConsoleEventRecorder implements EventRecorder {
|
|
1879
|
+
private readonly prefix;
|
|
1880
|
+
constructor(prefix?: string);
|
|
1881
|
+
record(type: string, fields?: Record<string, unknown>): void;
|
|
1882
|
+
flush(): Promise<void>;
|
|
1883
|
+
destroy(): void;
|
|
1884
|
+
}
|
|
1885
|
+
//#endregion
|
|
1886
|
+
//#region ../core/src/metrics/impression-observer.d.ts
|
|
1887
|
+
interface ImpressionObserverOptions {
|
|
1888
|
+
/** 可见比例阈值(0~1),默认 0.5 */
|
|
1889
|
+
threshold?: number;
|
|
1890
|
+
/** 最小可见时长(毫秒),默认 1000 */
|
|
1891
|
+
minVisibleDuration?: number;
|
|
1892
|
+
}
|
|
1893
|
+
declare class IntersectionImpressionObserver implements ImpressionObserver {
|
|
1894
|
+
private readonly observer;
|
|
1895
|
+
private readonly tracked;
|
|
1896
|
+
private readonly captured;
|
|
1897
|
+
private readonly minDuration;
|
|
1898
|
+
constructor(options?: ImpressionObserverOptions);
|
|
1899
|
+
observe(element: Element, id: string, metadata?: Record<string, unknown>): void;
|
|
1900
|
+
unobserve(element: Element): void;
|
|
1901
|
+
consume(): ImpressionEntry[];
|
|
1902
|
+
destroy(): void;
|
|
1903
|
+
}
|
|
1904
|
+
//#endregion
|
|
1905
|
+
//#region ../core/src/metrics/void-recorder.d.ts
|
|
1906
|
+
declare class VoidEventRecorder implements EventRecorder {
|
|
1907
|
+
record(): void;
|
|
1908
|
+
flush(): Promise<void>;
|
|
1909
|
+
destroy(): void;
|
|
1910
|
+
}
|
|
1911
|
+
//#endregion
|
|
1912
|
+
//#region ../core/src/metrics/with-fields-recorder.d.ts
|
|
1913
|
+
declare class WithFieldsRecorder implements EventRecorder {
|
|
1914
|
+
private readonly inner;
|
|
1915
|
+
private readonly providers;
|
|
1916
|
+
constructor(inner: EventRecorder, providers: MetricsFieldsProvider[]);
|
|
1917
|
+
record(type: string, fields?: Record<string, unknown>): void;
|
|
1918
|
+
flush(): Promise<void>;
|
|
1919
|
+
destroy(): void;
|
|
1920
|
+
}
|
|
1921
|
+
//#endregion
|
|
1922
|
+
//#region ../core/src/i18n/interpolate.d.ts
|
|
1923
|
+
/**
|
|
1924
|
+
* ICU 消息格式插值
|
|
1925
|
+
*
|
|
1926
|
+
* 支持 `{name}` 占位符替换和基础复数规则。
|
|
1927
|
+
*/
|
|
1928
|
+
/** 将 `{key}` 占位符替换为 values 中的对应值 */
|
|
1929
|
+
declare function interpolate(template: string, values?: Record<string, string | number>): string;
|
|
1930
|
+
/**
|
|
1931
|
+
* CLDR 复数类别
|
|
1932
|
+
*
|
|
1933
|
+
* 简化版:覆盖 zero / one / two / few / many / other。
|
|
1934
|
+
* 完整 CLDR 规则可通过 PluralRuleProvider 注入。
|
|
1935
|
+
*/
|
|
1936
|
+
type PluralCategory = "zero" | "one" | "two" | "few" | "many" | "other";
|
|
1937
|
+
/** 复数规则函数 — 给定数量返回复数类别 */
|
|
1938
|
+
type PluralRuleProvider = (count: number) => PluralCategory;
|
|
1939
|
+
/**
|
|
1940
|
+
* 英语复数规则(默认)
|
|
1941
|
+
* 0 → other, 1 → one, 2+ → other
|
|
1942
|
+
*/
|
|
1943
|
+
declare function englishPlural(count: number): PluralCategory;
|
|
1944
|
+
/**
|
|
1945
|
+
* 解析带复数后缀的翻译 key
|
|
1946
|
+
*
|
|
1947
|
+
* 约定: `key.one`, `key.other`, `key.zero`, etc.
|
|
1948
|
+
*/
|
|
1949
|
+
declare function resolvePluralKey(key: string, category: PluralCategory): string;
|
|
1950
|
+
//#endregion
|
|
1951
|
+
//#region ../core/src/i18n/locale.d.ts
|
|
1952
|
+
/** 检测语言是否为 RTL */
|
|
1953
|
+
declare function isRtl(language: string): boolean;
|
|
1954
|
+
/** 获取文本方向 */
|
|
1955
|
+
declare function getTextDirection(language: string): TextDirection;
|
|
1956
|
+
/**
|
|
1957
|
+
* 从语言代码生成 HTML lang/dir 属性
|
|
1958
|
+
*
|
|
1959
|
+
* @example
|
|
1960
|
+
* ```ts
|
|
1961
|
+
* getLocaleAttributes("ar-SA") // { lang: "ar-SA", dir: "rtl" }
|
|
1962
|
+
* getLocaleAttributes("en-US") // { lang: "en-US", dir: "ltr" }
|
|
1963
|
+
* ```
|
|
1964
|
+
*/
|
|
1965
|
+
declare function getLocaleAttributes(language: string): LocaleAttributes;
|
|
1966
|
+
/**
|
|
1967
|
+
* 构建 LocaleInfo
|
|
1968
|
+
*
|
|
1969
|
+
* @param language - 语言代码(如 "zh-Hans")
|
|
1970
|
+
* @param region - 地区代码(如 "CN"),可选
|
|
1971
|
+
*/
|
|
1972
|
+
declare function makeLocaleInfo(language: string, region?: string): LocaleInfo;
|
|
1973
|
+
/**
|
|
1974
|
+
* 将 locale 属性应用到 `<html>` 元素
|
|
1975
|
+
*
|
|
1976
|
+
* 服务端渲染时可用于字符串拼接,浏览器端直接操作 DOM。
|
|
1977
|
+
*/
|
|
1978
|
+
declare function setHtmlLocaleAttributes(attrs: LocaleAttributes): void;
|
|
1979
|
+
/**
|
|
1980
|
+
* 从 URL 前缀中提取 locale
|
|
1981
|
+
*
|
|
1982
|
+
* @param url - 请求 URL(如 "/zh/about")
|
|
1983
|
+
* @param supportedLocales - 支持的 locale 列表(如 ["zh", "en", "ja"])
|
|
1984
|
+
* @returns 匹配时返回 `{ locale, strippedUrl }`,不匹配返回 null
|
|
1985
|
+
*
|
|
1986
|
+
* @example
|
|
1987
|
+
* ```ts
|
|
1988
|
+
* resolveLocaleFromUrl("/zh/about", ["zh", "en"])
|
|
1989
|
+
* // → { locale: "zh", strippedUrl: "/about" }
|
|
1990
|
+
*
|
|
1991
|
+
* resolveLocaleFromUrl("/about", ["zh", "en"])
|
|
1992
|
+
* // → null
|
|
1993
|
+
* ```
|
|
1994
|
+
*/
|
|
1995
|
+
declare function resolveLocaleFromUrl(url: string, supportedLocales: string[]): {
|
|
1996
|
+
locale: string;
|
|
1997
|
+
strippedUrl: string;
|
|
1998
|
+
} | null;
|
|
1999
|
+
//#endregion
|
|
2000
|
+
//#region ../core/src/i18n/translator.d.ts
|
|
2001
|
+
interface SimpleTranslatorOptions {
|
|
2002
|
+
/** 翻译映射 */
|
|
2003
|
+
messages: Record<string, string>;
|
|
2004
|
+
/** 当前 locale */
|
|
2005
|
+
locale: string;
|
|
2006
|
+
/** 复数规则函数(默认英语规则) */
|
|
2007
|
+
pluralRule?: PluralRuleProvider;
|
|
2008
|
+
/** 找不到翻译时的回退行为(默认返回 key) */
|
|
2009
|
+
fallback?: (key: string) => string;
|
|
2010
|
+
}
|
|
2011
|
+
declare class SimpleTranslator implements Translator {
|
|
2012
|
+
readonly locale: string;
|
|
2013
|
+
private readonly messages;
|
|
2014
|
+
private readonly pluralRule;
|
|
2015
|
+
private readonly fallback;
|
|
2016
|
+
constructor(options: SimpleTranslatorOptions);
|
|
2017
|
+
t(key: string, values?: Record<string, string | number>): string;
|
|
2018
|
+
plural(key: string, count: number, values?: Record<string, string | number>): string;
|
|
2019
|
+
}
|
|
2020
|
+
//#endregion
|
|
2021
|
+
//#region ../browser/src/action-handlers/external-url-action.d.ts
|
|
2022
|
+
interface ExternalUrlDependencies {
|
|
2023
|
+
framework: Framework;
|
|
2024
|
+
log: Logger;
|
|
2025
|
+
}
|
|
2026
|
+
declare function registerExternalUrlHandler(deps: ExternalUrlDependencies): void;
|
|
2027
|
+
//#endregion
|
|
2028
|
+
//#region ../browser/src/action-handlers/flow-action.d.ts
|
|
2029
|
+
/** UI 框架回调 — 解耦 Svelte store 等依赖 */
|
|
2030
|
+
interface FlowActionCallbacks {
|
|
2031
|
+
/** 导航后更新当前路径(替代 currentPath.set()) */
|
|
2032
|
+
onNavigate(pathname: string): void;
|
|
2033
|
+
/** 模态页面展示(替代 openModal()) */
|
|
2034
|
+
onModal(page: BasePage): void;
|
|
2035
|
+
}
|
|
2036
|
+
/** 注册 FlowAction handler 所需的依赖 */
|
|
2037
|
+
interface FlowActionDependencies {
|
|
2038
|
+
framework: Framework;
|
|
2039
|
+
log: Logger;
|
|
2040
|
+
callbacks: FlowActionCallbacks;
|
|
2041
|
+
/** 更新应用 UI 的回调,page 可以是 Promise */
|
|
2042
|
+
updateApp: (props: {
|
|
2043
|
+
page: Promise<BasePage> | BasePage;
|
|
2044
|
+
isFirstPage?: boolean;
|
|
2045
|
+
}) => void;
|
|
2046
|
+
/** 获取可滚动页面元素,用于滚动位置保存/恢复 */
|
|
2047
|
+
getScrollablePageElement?: () => HTMLElement | null;
|
|
2048
|
+
/**
|
|
2049
|
+
* 是否由本 handler 管理浏览器 history(pushState / popstate)。缺省 `true`。
|
|
2050
|
+
* 结构化导航(`startBrowserApp({ navigation })`)下应传 `false`:history 由
|
|
2051
|
+
* NavigationBridge 独占,否则两套 `History` 实例会争抢同一个 `window.history.state`、
|
|
2052
|
+
* 各自注册 popstate 互相 clobber,导致 back/forward 行为错乱。传 `false` 时本 handler
|
|
2053
|
+
* 仍负责 dispatch + updateApp(初始渲染 / redirect / modal),只是不碰 history。
|
|
2054
|
+
*/
|
|
2055
|
+
manageHistory?: boolean;
|
|
2056
|
+
}
|
|
2057
|
+
declare function registerFlowActionHandler(deps: FlowActionDependencies): void;
|
|
2058
|
+
//#endregion
|
|
2059
|
+
//#region ../browser/src/action-handlers/register.d.ts
|
|
2060
|
+
interface ActionHandlerDependencies {
|
|
2061
|
+
framework: Framework;
|
|
2062
|
+
log: Logger;
|
|
2063
|
+
callbacks: FlowActionCallbacks;
|
|
2064
|
+
updateApp: (props: {
|
|
2065
|
+
page: Promise<BasePage> | BasePage;
|
|
2066
|
+
isFirstPage?: boolean;
|
|
2067
|
+
}) => void;
|
|
2068
|
+
/** 获取可滚动页面元素,用于滚动位置保存/恢复 */
|
|
2069
|
+
getScrollablePageElement?: () => HTMLElement | null;
|
|
2070
|
+
/** 是否由 FlowAction handler 管理 history;结构化导航下传 `false`(见 registerFlowActionHandler)。 */
|
|
2071
|
+
manageHistory?: boolean;
|
|
2072
|
+
}
|
|
2073
|
+
declare function registerActionHandlers(deps: ActionHandlerDependencies): void;
|
|
2074
|
+
//#endregion
|
|
2075
|
+
//#region ../browser/src/navigation-bridge.d.ts
|
|
2076
|
+
/** NavigationBridge 构造依赖。 */
|
|
2077
|
+
interface NavigationBridgeDependencies {
|
|
2078
|
+
/** 已构建好的导航控制器(持有 initial 树、intentDispatcher、router 等)。 */
|
|
2079
|
+
readonly controller: NavigationController;
|
|
2080
|
+
/** URL 编解码器(默认 `createActiveLeafCodec`)。 */
|
|
2081
|
+
readonly codec: NavigationCodec;
|
|
2082
|
+
/** Router 的最小读取面(encode 反查 / decode 用)。 */
|
|
2083
|
+
readonly router: NavigationRouterLike;
|
|
2084
|
+
/** 日志器。 */
|
|
2085
|
+
readonly log: Logger;
|
|
2086
|
+
/** 获取可滚动页面元素,用于滚动位置保存/恢复(透传给 History)。 */
|
|
2087
|
+
readonly getScrollablePageElement?: () => HTMLElement | null;
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* 导航操作句柄 —— 向应用暴露的对外面。
|
|
2091
|
+
*
|
|
2092
|
+
* 所有写操作返回提交后的 `NavigationSnapshot`;写操作会同步把新树落到 history/URL。
|
|
2093
|
+
* `subscribe` 与 controller 的订阅一致(每次提交都回调,含来自 popstate 的 hydrate)。
|
|
2094
|
+
*/
|
|
2095
|
+
interface NavigationHandle {
|
|
2096
|
+
/** 当前快照(树 + 已解析的可见目标)。 */
|
|
2097
|
+
getSnapshot(): NavigationSnapshot;
|
|
2098
|
+
/** 在激活栈压入新目标。 */
|
|
2099
|
+
push(intent: string, params?: RouteParams, options?: {
|
|
2100
|
+
target?: NavigationPath;
|
|
2101
|
+
}): Promise<NavigationSnapshot>;
|
|
2102
|
+
/** 从激活栈弹出 count 个(默认 1)。 */
|
|
2103
|
+
pop(count?: number): Promise<NavigationSnapshot>;
|
|
2104
|
+
/** 激活栈弹回根。 */
|
|
2105
|
+
popToRoot(): Promise<NavigationSnapshot>;
|
|
2106
|
+
/** 替换激活栈栈顶。 */
|
|
2107
|
+
replaceTop(intent: string, params?: RouteParams): Promise<NavigationSnapshot>;
|
|
2108
|
+
/** 切换 tabs 激活分支。 */
|
|
2109
|
+
selectTab(key: string, target?: NavigationPath): Promise<NavigationSnapshot>;
|
|
2110
|
+
/** 设置 split 列内容(intent=undefined 清空该列)。 */
|
|
2111
|
+
selectColumn(columnId: string, intent: string | undefined, params?: RouteParams, target?: NavigationPath): Promise<NavigationSnapshot>;
|
|
2112
|
+
/** 用外部树替换当前树并重解析(一般由桥内部 popstate 调用,亦对外暴露)。 */
|
|
2113
|
+
hydrate(tree: NavigationNode): Promise<NavigationSnapshot>;
|
|
2114
|
+
/** 订阅快照变更;返回取消订阅函数。 */
|
|
2115
|
+
subscribe(listener: (snapshot: NavigationSnapshot) => void): () => void;
|
|
2116
|
+
}
|
|
2117
|
+
/**
|
|
2118
|
+
* 创建 NavigationBridge:订阅 controller、装配 popstate、返回 navigation handle。
|
|
2119
|
+
*
|
|
2120
|
+
* 调用后 bridge 已激活(已订阅 controller + 已注册 popstate listener)。应用应在调用前/后
|
|
2121
|
+
* 调一次 `controller.resolve()` 完成首屏解析;首屏的快照提交会被 bridge 用 `replaceState`
|
|
2122
|
+
* 写入 history(first-page 语义),不会污染历史栈。
|
|
2123
|
+
*/
|
|
2124
|
+
declare function createNavigationBridge(deps: NavigationBridgeDependencies): NavigationHandle;
|
|
2125
|
+
//#endregion
|
|
2126
|
+
//#region ../browser/src/start-app.d.ts
|
|
2127
|
+
/**
|
|
2128
|
+
* 结构化导航定义 —— 由应用通过 `defineNavigation(...)` 或手写提供。
|
|
2129
|
+
*
|
|
2130
|
+
* 仅当该字段出现时 bridge 才激活;缺省时 `startBrowserApp` 走原有扁平单页路径,行为不变。
|
|
2131
|
+
* 提供后:
|
|
2132
|
+
* - 用 `initial` 树构建 `NavigationController`(守卫上下文走 `createBrowserContext`,
|
|
2133
|
+
* 预取缓存复用 `framework.prefetchedIntents`);
|
|
2134
|
+
* - 装配 `NavigationBridge`(snapshot → history/URL,popstate → hydrate);
|
|
2135
|
+
* - 解析首屏树(一次 `resolve()`),通过 `onNavigationReady` 把 handle 交给应用。
|
|
2136
|
+
*/
|
|
2137
|
+
interface BrowserNavigationConfig {
|
|
2138
|
+
/** 初始导航树(单 LeafNode = 今天的扁平单页)。 */
|
|
2139
|
+
readonly initial: NavigationNode;
|
|
2140
|
+
/** URL 编解码器;缺省 `createActiveLeafCodec()`。 */
|
|
2141
|
+
readonly codec?: NavigationCodec;
|
|
2142
|
+
/** 导航级 beforeLoad 守卫(对主目标执行)。 */
|
|
2143
|
+
readonly beforeLoad?: readonly BeforeLoadGuard[];
|
|
2144
|
+
/** 导航级 afterLoad 守卫。 */
|
|
2145
|
+
readonly afterLoad?: readonly AfterLoadGuard[];
|
|
2146
|
+
/** dispatch 失败 / deny 时的兜底错误页工厂。 */
|
|
2147
|
+
readonly getErrorPage?: (status: number, message: string) => BasePage;
|
|
2148
|
+
}
|
|
2149
|
+
interface BrowserAppConfig {
|
|
2150
|
+
/** 注册 controllers 和路由的引导函数 */
|
|
2151
|
+
bootstrap: (framework: Framework) => void;
|
|
2152
|
+
/** DOM 挂载点 ID(默认 "app") */
|
|
2153
|
+
mountId?: string;
|
|
2154
|
+
/** 获取可滚动页面元素,用于滚动位置保存/恢复 */
|
|
2155
|
+
getScrollablePageElement?: () => HTMLElement | null;
|
|
2156
|
+
/**
|
|
2157
|
+
* 启动前钩子 — 在 Framework 创建后、挂载前执行
|
|
2158
|
+
*
|
|
2159
|
+
* 用于初始化错误监控、埋点 SDK、i18n 等。
|
|
2160
|
+
*/
|
|
2161
|
+
onBeforeStart?: (framework: Framework) => void | Promise<void>;
|
|
2162
|
+
/**
|
|
2163
|
+
* 启动后钩子 — 在初始页面触发后执行
|
|
2164
|
+
*
|
|
2165
|
+
* 用于启动后操作(如 service worker 注册、性能打点)。
|
|
2166
|
+
*/
|
|
2167
|
+
onAfterStart?: (framework: Framework) => void | Promise<void>;
|
|
2168
|
+
/**
|
|
2169
|
+
* 挂载应用到 DOM
|
|
2170
|
+
*
|
|
2171
|
+
* 框架无关 — Svelte / React / Vue 均可通过此回调实现。
|
|
2172
|
+
*
|
|
2173
|
+
* @param target - DOM 挂载点
|
|
2174
|
+
* @param context - Framework 实例 + 语言
|
|
2175
|
+
* @returns 更新函数,用于后续页面切换
|
|
2176
|
+
*/
|
|
2177
|
+
mount: (target: HTMLElement, context: {
|
|
2178
|
+
framework: Framework;
|
|
2179
|
+
}) => (props: {
|
|
2180
|
+
page: Promise<BasePage> | BasePage;
|
|
2181
|
+
isFirstPage?: boolean;
|
|
2182
|
+
}) => void;
|
|
2183
|
+
/** FlowAction / ExternalUrl 回调 */
|
|
2184
|
+
callbacks: FlowActionCallbacks;
|
|
2185
|
+
/**
|
|
2186
|
+
* Framework 配置 — locale、reportCallback、eventRecorder 等
|
|
2187
|
+
*
|
|
2188
|
+
* 传入后会在 Framework.create() 时合并。
|
|
2189
|
+
* prefetchedIntents 由框架自动从 DOM 提取,无需传入。
|
|
2190
|
+
*/
|
|
2191
|
+
frameworkConfig?: Omit<FrameworkConfig, "prefetchedIntents">;
|
|
2192
|
+
/**
|
|
2193
|
+
* 异步加载当前 locale 的翻译字典。
|
|
2194
|
+
*
|
|
2195
|
+
* 显式传入时会覆盖 bootstrap / Vite 自动生成的 loader。
|
|
2196
|
+
*/
|
|
2197
|
+
loadMessages?: MessagesLoader;
|
|
2198
|
+
/**
|
|
2199
|
+
* 结构化导航定义(可选)。
|
|
2200
|
+
*
|
|
2201
|
+
* 提供后,`startBrowserApp` 在挂载后构建 NavigationController + NavigationBridge,
|
|
2202
|
+
* 解析首屏树,并通过 `onNavigationReady` 把导航 handle 交给应用。缺省时走原有
|
|
2203
|
+
* 扁平单页路径(FlowAction handler),行为完全不变。
|
|
2204
|
+
*/
|
|
2205
|
+
navigation?: BrowserNavigationConfig;
|
|
2206
|
+
/**
|
|
2207
|
+
* 导航就绪回调 —— bridge 装配并完成首屏 `resolve()` 后调用。
|
|
2208
|
+
*
|
|
2209
|
+
* 仅当提供了 `navigation` 时触发。应用拿到 handle 后用它驱动导航
|
|
2210
|
+
* (push/pop/selectTab…)并订阅快照渲染 UI。
|
|
2211
|
+
*/
|
|
2212
|
+
onNavigationReady?: (handle: NavigationHandle) => void | Promise<void>;
|
|
2213
|
+
}
|
|
2214
|
+
/**
|
|
2215
|
+
* 启动客户端应用
|
|
2216
|
+
*
|
|
2217
|
+
* 自动执行 hydration 全流程。
|
|
2218
|
+
*/
|
|
2219
|
+
declare function startBrowserApp(config: BrowserAppConfig): Promise<void>;
|
|
2220
|
+
//#endregion
|
|
2221
|
+
//#region ../browser/src/utils/history.d.ts
|
|
2222
|
+
interface HistoryOptions {
|
|
2223
|
+
getScrollablePageElement: () => HTMLElement | null;
|
|
2224
|
+
/**
|
|
2225
|
+
* 是否把 `state` 一并写入 `window.history.state`(而非仅 `{ id }` + 内存 LruMap)。缺省 `false`。
|
|
2226
|
+
*
|
|
2227
|
+
* 内存 LruMap 在整页刷新后丢失;若 state 小且可结构化克隆(如导航树 `{ tree }`),开启此项
|
|
2228
|
+
* 可让 state 随 `window.history.state` **跨刷新按 entry 保留**,刷新后 back/forward 仍能从
|
|
2229
|
+
* history.state 还原(onPopState 在 LruMap 未命中时回退到 `event.state.state`)。
|
|
2230
|
+
* 大状态(如整页 `{ page }`)不应开启,避免撑爆 history.state。
|
|
2231
|
+
*/
|
|
2232
|
+
persistInHistoryState?: boolean;
|
|
2233
|
+
}
|
|
2234
|
+
declare class History<State> {
|
|
2235
|
+
private readonly entries;
|
|
2236
|
+
private readonly log;
|
|
2237
|
+
private readonly getScrollablePageElement;
|
|
2238
|
+
private readonly persistInHistoryState;
|
|
2239
|
+
private currentStateId;
|
|
2240
|
+
constructor(log: Logger, options: HistoryOptions, sizeLimit?: number);
|
|
2241
|
+
/** 写入 window.history.state 的载荷:persist 时连 state 一起带(跨刷新保留)。 */
|
|
2242
|
+
private historyState;
|
|
2243
|
+
replaceState(state: State, url: string): void;
|
|
2244
|
+
pushState(state: State, url: string): void;
|
|
2245
|
+
beforeTransition(): void;
|
|
2246
|
+
onPopState(listener: (url: string, state?: State) => void | Promise<void>): void;
|
|
2247
|
+
/** 仅推入 URL,不缓存页面状态(用于页面加载失败场景) */
|
|
2248
|
+
pushUrl(url: string): void;
|
|
2249
|
+
/** 仅替换 URL,不缓存页面状态(用于页面加载失败场景) */
|
|
2250
|
+
replaceUrl(url: string): void;
|
|
2251
|
+
updateState(update: (current?: State) => State): void;
|
|
2252
|
+
private get scrollTop();
|
|
2253
|
+
private set scrollTop(value);
|
|
2254
|
+
}
|
|
2255
|
+
//#endregion
|
|
2256
|
+
//#region ../browser/src/utils/try-scroll.d.ts
|
|
2257
|
+
declare function tryScroll(log: Logger, getScrollableElement: () => HTMLElement | null, scrollY: number): void;
|
|
2258
|
+
//#endregion
|
|
2259
|
+
//#region ../browser/src/server-data.d.ts
|
|
2260
|
+
/**
|
|
2261
|
+
* 从 DOM 反序列化服务端嵌入的数据。
|
|
2262
|
+
* 读取 `<script id="serialized-server-data">` 的内容并移除标签。
|
|
2263
|
+
*/
|
|
2264
|
+
declare function deserializeServerData(): PrefetchedIntent[] | undefined;
|
|
2265
|
+
/**
|
|
2266
|
+
* 从 DOM 提取 SSR 数据并构建 PrefetchedIntents 实例。
|
|
2267
|
+
* 替代原来的 PrefetchedIntents.fromDom()。
|
|
2268
|
+
*/
|
|
2269
|
+
declare function createPrefetchedIntentsFromDom(): PrefetchedIntents;
|
|
2270
|
+
//#endregion
|
|
2271
|
+
export { isNone as $, Framework as $n, BASE_PAGE_FIELDS as $r, popToRoot as $t, resolvePluralKey as A, BaseLogger as Ai, SplitVisibility as An, RouteAddOptions as Ar, createNavigationController as At, ServerContextOptions as B, ActionHandler as Bi, classifyHost as Bn, AfterLoadGuard as Br, SerializedNavigation as Bt, makeLocaleInfo as C, ImpressionEntry as Ci, NavigationPathStep as Cn, bool as Cr, PopToRootOperation as Ct, PluralRuleProvider as D, ReportingLogger as Di, SPLIT_VISIBILITIES as Dn, str as Dr, SelectColumnOperation as Dt, PluralCategory as E, ReportCallback as Ei, ResolvedDestination as En, oneOf as Er, ReplaceTopOperation as Et, ConsoleEventRecorder as F, IntentDispatcher as Fi, mapEach as Fn, StandardIssue as Fr, createActiveLeafCodec as Ft, getBaseUrl as G, FlowAction as Gi, HttpError as Gn, NextResult as Gr, deserializeNavigation as Gt, createServerContext as H, Action as Hi, HostGuardError as Hn, DenyResult as Hr, SerializedSplitColumn as Ht, CompositeEventRecorder as I, Intent as Ii, pipe as In, StandardResult as Ir, createFullStateCodec as It, removeScheme as J, isFlowAction as Ji, stableStringify as Jn, RewriteResult as Jr, collectVisibleDestinations as Jt, removeHost as K, isCompoundAction as Ki, RequestInterceptor as Kn, PostLoadContext as Kr, serializeNavigation as Kt, runAfterLoadGuards as L, IntentController as Li, pipeAsync as Ln, StandardSchemaV1 as Lr, decodeNavigationTreeParam as Lt, VoidEventRecorder as M, LoggerFactory as Mi, TabsNode as Mn, Router as Mr, FullStateCodecOptions as Mt, ImpressionObserverOptions as N, SecureFetchOptions as Ni, AsyncMapper as Nn, InferOutput as Nr, NavigationCodec as Nt, englishPlural as O, ReportingLoggerFactory as Oi, SplitColumn as On, uuid as Or, SelectTabOperation as Ot, IntersectionImpressionObserver as P, secureFetch as Pi, Mapper as Pn, ParamSchema as Pr, NavigationRouterLike as Pt, Optional as Q, safeErrorPage as Qn, rewrite as Qr, popTo as Qt, runBeforeLoadGuards as R, Container as Ri, BaseController as Rn, makeSchema as Rr, encodeNavigationTreeParam as Rt, isRtl as S, EventRecorder as Si, NavigationPath as Sn, StrOptions as Sr, PopToOperation as St, setHtmlLocaleAttributes as T, MetricsFieldsProvider as Ti, Page as Tn, num as Tr, PushOptions as Tt, generateUuid as U, CompoundAction as Ui, HttpClient as Un, MiddlewareResult as Ur, SerializedStack as Ut, createBrowserContext as V, ACTION_KINDS as Vi, classifyUrl as Vn, BeforeLoadGuard as Vr, SerializedSplit as Vt, buildUrl as W, ExternalUrlAction as Wi, HttpClientConfig as Wn, NavigationContext as Wr, SerializedTabs as Wt, getPWADisplayMode as X, makeFlowAction as Xi, BaseShelf as Xn, next as Xr, findNode as Xt, PWADisplayMode as Y, makeExternalUrlAction as Yi, BaseItem as Yn, deny as Yr, findNearestStack as Yt, None as Z, SafeErrorPageOptions as Zn, redirect as Zr, pop as Zt, registerExternalUrlHandler as _, TranslationMessages as _i, LeafNode as _n, QuerySchemaMap as _r, NavigationControllerOptions as _t, BrowserAppConfig as a, RequestScopedKey as ai, setVisibility as an, TextDirection as ar, defineRoutes as at, getLocaleAttributes as b, PlatformInfo as bi, NavigationNode as bn, withDefault as br, NavigationOperation as bt, NavigationBridgeDependencies as c, FeatureFlags as ci, TabsInit as cn, shouldLog as cr, NavigationBrowserConfig as ct, ActionHandlerDependencies as d, MetricsRecorder as di, isStackNode as dn, CompositeLogger as dr, NavigationSSRDefinition as dt, BasePage as ei, push as en, FrameworkConfig as er, isSome as et, registerActionHandlers as f, Net as fi, isTabsNode as fn, CompositeLoggerFactory as fr, defineNavigation as ft, ExternalUrlDependencies as g, MessagesLoaderContext as gi, tabs as gn, ParamsFor as gr, NavigationController as gt, registerFlowActionHandler as h, MessagesLoader as hi, stack as hn, InferQuery as hr, NavigationContextInput as ht, History as i, markPublic as ii, selectTab as in, LocaleInfo as ir, RouteDefinition as it, WithFieldsRecorder as j, Logger as ji, StackNode as jn, RouteMatch as jr, DEFAULT_NAV_PARAM as jt, interpolate as k, ReportingLoggerOptions as ki, SplitNode as kn, RouteParams as kr, SetVisibilityOperation as kt, NavigationHandle as l, FeatureFlagsProvider as li, isLeafNode as ln, ConsoleLogger as lr, NavigationDefinition as lt, FlowActionDependencies as m, makeDependencies as mi, split as mn, InferParams as mr, NAVIGATION_OP_KINDS as mt, deserializeServerData as n, getPublicFields as ni, resolveActivePath as nn, PrefetchedIntents as nr, DefineRoutesOptions as nt, BrowserNavigationConfig as o, defineRequestScopedKey as oi, visibleSplitColumns as on, Translator as or, route as ot, FlowActionCallbacks as p, Storage as pi, leaf as pn, ExtractParamNames as pr, HydrateOperation as pt, removeQueryParams as q, isExternalUrlAction as qi, ResponseInterceptor as qn, RedirectResult as qr, serializeNavigationStable as qt, tryScroll as r, isPublicMarked as ri, selectColumn as rn, LocaleAttributes as rr, RenderMode as rt, startBrowserApp as s, DEP_KEYS as si, SplitColumnInit as sn, resetFilterCache as sr, DefineNavigationOptions as st, createPrefetchedIntentsFromDom as t, FINESOFT_PUBLIC as ti, replaceTop as tn, PrefetchedIntent as tr, LruMap as tt, createNavigationBridge as u, MakeDependenciesOptions as ui, isSplitNode as un, ConsoleLoggerFactory as ur, NavigationInitial as ut, SimpleTranslator as v, resolveConfiguredMessages as vi, NAVIGATION_NODE_KINDS as vn, StripOptional as vr, NavigationDispatchContext as vt, resolveLocaleFromUrl as w, ImpressionObserver as wi, NavigationSnapshot as wn, int as wr, PushOperation as wt, getTextDirection as x, detectPlatform as xi, NavigationNodeKind as xn, NumOptions as xr, PopOperation as xt, SimpleTranslatorOptions as y, resolveMessages as yi, NavigationError as yn, optional as yr, NavigationOpKind as yt, BrowserContextOptions as z, ActionDispatcher as zi, HostCheckResult as zn, runStandard as zr, SerializedLeaf as zt };
|
|
2272
|
+
//# sourceMappingURL=server-data-HVSgxEac.d.mts.map
|