@dino-dev/bridge-core-ts 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +145 -0
  2. package/dist/generated/ai_tutor.d.ts +63 -0
  3. package/dist/generated/ai_tutor.d.ts.map +1 -0
  4. package/dist/generated/ai_tutor.js +6 -0
  5. package/dist/generated/ai_tutor.js.map +1 -0
  6. package/dist/generated/audio.d.ts +29 -0
  7. package/dist/generated/audio.d.ts.map +1 -0
  8. package/dist/generated/audio.js +6 -0
  9. package/dist/generated/audio.js.map +1 -0
  10. package/dist/generated/device.d.ts +23 -0
  11. package/dist/generated/device.d.ts.map +1 -0
  12. package/dist/generated/device.js +6 -0
  13. package/dist/generated/device.js.map +1 -0
  14. package/dist/generated/index.d.ts +17 -0
  15. package/dist/generated/index.d.ts.map +1 -0
  16. package/dist/generated/index.js +25 -0
  17. package/dist/generated/index.js.map +1 -0
  18. package/dist/generated/user.d.ts +25 -0
  19. package/dist/generated/user.d.ts.map +1 -0
  20. package/dist/generated/user.js +6 -0
  21. package/dist/generated/user.js.map +1 -0
  22. package/dist/src/index.d.ts +17 -0
  23. package/dist/src/index.d.ts.map +1 -0
  24. package/dist/src/index.js +18 -0
  25. package/dist/src/index.js.map +1 -0
  26. package/dist/src/lifecycle.d.ts +85 -0
  27. package/dist/src/lifecycle.d.ts.map +1 -0
  28. package/dist/src/lifecycle.js +97 -0
  29. package/dist/src/lifecycle.js.map +1 -0
  30. package/dist/src/mrp-bridge.d.ts +108 -0
  31. package/dist/src/mrp-bridge.d.ts.map +1 -0
  32. package/dist/src/mrp-bridge.js +185 -0
  33. package/dist/src/mrp-bridge.js.map +1 -0
  34. package/dist/src/system-module.d.ts +59 -0
  35. package/dist/src/system-module.d.ts.map +1 -0
  36. package/dist/src/system-module.js +33 -0
  37. package/dist/src/system-module.js.map +1 -0
  38. package/generated/ai_tutor.ts +72 -0
  39. package/generated/audio.ts +35 -0
  40. package/generated/device.ts +28 -0
  41. package/generated/index.ts +40 -0
  42. package/generated/user.ts +30 -0
  43. package/package.json +44 -0
  44. package/src/index.ts +33 -0
  45. package/src/lifecycle.ts +127 -0
  46. package/src/mrp-bridge.ts +297 -0
  47. package/src/system-module.ts +77 -0
package/src/index.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @h5-container/bridge-core-ts
3
+ *
4
+ * H5 端与 Flutter 容器通信的核心 SDK(零框架依赖)。
5
+ * 提供 Bridge 消息通信 + 原生模块类型安全调用。
6
+ *
7
+ * 适用于 React / Vue / Svelte / Vanilla JS 等任何前端框架。
8
+ * 所有 API 从此入口统一导出,消费者无需感知内部目录结构。
9
+ */
10
+
11
+ // 核心 Bridge
12
+ export { bridge, MrpBridge } from './mrp-bridge';
13
+ export type {
14
+ BridgeMessage,
15
+ MessageHandler,
16
+ Unsubscribe,
17
+ } from './mrp-bridge';
18
+
19
+ // 内置系统模块类型 + 工厂函数(容器核心契约)
20
+ export { getSystemModule } from './system-module';
21
+ export type {
22
+ SystemModule,
23
+ ModuleAllowedResult,
24
+ AvailableModulesResult,
25
+ ContainerInfoResult,
26
+ } from './system-module';
27
+
28
+ // Generated 模块:类型 + 工厂函数
29
+ export * from '../generated/index';
30
+
31
+ // 生命周期上报工具
32
+ export { lifecycle } from './lifecycle';
33
+ export type { RouteChangedPayload, AppMountedPayload } from './lifecycle';
@@ -0,0 +1,127 @@
1
+ /**
2
+ * H5 容器生命周期上报工具。
3
+ *
4
+ * 提供类型安全的生命周期事件上报接口,H5 应用通过此模块通知
5
+ * Flutter 宿主当前的运行状态。
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { lifecycle } from '@h5-container/bridge-core-ts';
10
+ *
11
+ * // React
12
+ * useEffect(() => {
13
+ * lifecycle.appMounted({ route: location.pathname });
14
+ * return () => lifecycle.appDestroyed();
15
+ * }, []);
16
+ *
17
+ * // 路由跳转
18
+ * lifecycle.routeChanged({ from: '/home', to: '/game' });
19
+ *
20
+ * // 自定义事件
21
+ * lifecycle.custom('gameStarted', { level: 1 });
22
+ * ```
23
+ */
24
+
25
+ // ============================================================
26
+ // 类型定义
27
+ // ============================================================
28
+
29
+ /** SPA 路由跳转事件的 payload */
30
+ export interface RouteChangedPayload {
31
+ /** 跳转前路由 */
32
+ from: string;
33
+ /** 跳转后路由 */
34
+ to: string;
35
+ }
36
+
37
+ /** appMounted 事件的 payload */
38
+ export interface AppMountedPayload {
39
+ /** 当前路由(可选) */
40
+ route?: string;
41
+ }
42
+
43
+ // ============================================================
44
+ // 工具函数
45
+ // ============================================================
46
+
47
+ /** 底层上报函数,通过 Bridge 发送 lifecycle 类型消息。 */
48
+ function report(action: string, payload: object = {}): void {
49
+ if (window.__FLUTTER_BRIDGE__) {
50
+ window.__FLUTTER_BRIDGE__.send(
51
+ 'lifecycle',
52
+ action,
53
+ payload as Record<string, unknown>,
54
+ );
55
+ }
56
+ }
57
+
58
+ // ============================================================
59
+ // 生命周期对象(命名空间风格,方便 tree-shaking)
60
+ // ============================================================
61
+
62
+ /**
63
+ * H5 容器生命周期上报 API。
64
+ *
65
+ * 调用这些方法通知宿主 Flutter 应用当前 H5 的运行状态。
66
+ * 宿主通过 `controller.onContainerEvent` 流接收事件。
67
+ */
68
+ export const lifecycle = {
69
+ /**
70
+ * 上报 H5 应用挂载完成。
71
+ *
72
+ * 建议在 React 根组件 `useEffect` 或 Vue `onMounted` 中调用。
73
+ */
74
+ appMounted(payload?: AppMountedPayload): void {
75
+ report('appMounted', payload ?? {});
76
+ },
77
+
78
+ /**
79
+ * 上报 H5 应用销毁。
80
+ *
81
+ * 建议在 React 根组件 `useEffect` 的 cleanup 或 Vue `onUnmounted` 中调用。
82
+ */
83
+ appDestroyed(): void {
84
+ report('appDestroyed');
85
+ },
86
+
87
+ /**
88
+ * 上报 SPA 内部路由跳转。
89
+ *
90
+ * 建议在路由守卫或 React Router 的 `useLocation` 变化时调用。
91
+ */
92
+ routeChanged(payload: RouteChangedPayload): void {
93
+ report('routeChanged', payload);
94
+ },
95
+
96
+ /**
97
+ * 上报页面恢复可见(`visibilitychange: visible`)。
98
+ *
99
+ * 建议监听 `document.addEventListener('visibilitychange', ...)` 后调用。
100
+ */
101
+ pageVisible(): void {
102
+ report('pageVisible');
103
+ },
104
+
105
+ /**
106
+ * 上报页面进入后台(`visibilitychange: hidden`)。
107
+ */
108
+ pageHidden(): void {
109
+ report('pageHidden');
110
+ },
111
+
112
+ /**
113
+ * 上报自定义事件(业务自定义语义)。
114
+ *
115
+ * @param action 事件名称(宿主侧收到 `event.action` 即此值,type 为 `custom`)
116
+ * @param payload 事件数据
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * lifecycle.custom('gameStarted', { level: 1, mode: 'easy' });
121
+ * lifecycle.custom('scoreUpdated', { score: 200 });
122
+ * ```
123
+ */
124
+ custom(action: string, payload?: Record<string, unknown>): void {
125
+ report(action, payload ?? {});
126
+ },
127
+ } as const;
@@ -0,0 +1,297 @@
1
+ /**
2
+ * h5_app_container Bridge SDK
3
+ *
4
+ * H5 端与 Flutter 容器通信的核心 SDK。
5
+ * 提供 Bridge 消息通信 + 原生模块调用能力。
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { bridge } from './mrp-bridge';
10
+ *
11
+ * // 发送消息到 Flutter
12
+ * bridge.send('interaction', 'onFindTarget', { target: 'ladybug' });
13
+ *
14
+ * // 调用原生模块(类型安全,搭配 generated 类型使用)
15
+ * const audio = bridge.module<AudioModule>('audio');
16
+ * await audio.play({ soundId: 'success', volume: 0.8 });
17
+ * ```
18
+ */
19
+
20
+ // ============================================================
21
+ // 类型定义
22
+ // ============================================================
23
+
24
+ /** Bridge 消息格式 */
25
+ export interface BridgeMessage<T = Record<string, unknown>> {
26
+ type: string;
27
+ action: string;
28
+ payload: T;
29
+ messageId?: string;
30
+ timestamp: number;
31
+ }
32
+
33
+ /** 消息处理函数 */
34
+ export type MessageHandler<T = Record<string, unknown>> = (
35
+ message: BridgeMessage<T>,
36
+ ) => void;
37
+
38
+ /** 取消监听函数 */
39
+ export type Unsubscribe = () => void;
40
+
41
+ // ============================================================
42
+ // Window 全局类型扩展
43
+ // ============================================================
44
+
45
+ declare global {
46
+ interface Window {
47
+ /** Bridge SDK 单例 */
48
+ mrpBridge: MrpBridge;
49
+ /** Flutter 内部 Bridge 接口 */
50
+ __FLUTTER_BRIDGE__?: {
51
+ send(
52
+ type: string,
53
+ action: string,
54
+ payload: Record<string, unknown>,
55
+ ): Promise<unknown>;
56
+ onMessage(jsonString: string): void;
57
+ };
58
+ /** 宿主注册的原生模块(按命名空间分组) */
59
+ NativeModules?: Record<
60
+ string,
61
+ Record<string, (...args: unknown[]) => Promise<unknown>>
62
+ >;
63
+ }
64
+ }
65
+
66
+ // ============================================================
67
+ // 内部类型
68
+ // ============================================================
69
+
70
+ interface WrappedHandler extends MessageHandler {
71
+ _original?: MessageHandler;
72
+ _type?: string;
73
+ _action?: string | null;
74
+ }
75
+
76
+ interface PendingMessage {
77
+ type: string;
78
+ action: string;
79
+ payload: Record<string, unknown>;
80
+ }
81
+
82
+ // ============================================================
83
+ // MrpBridge
84
+ // ============================================================
85
+
86
+ /**
87
+ * Bridge SDK 主类。
88
+ *
89
+ * 提供两大能力:
90
+ * 1. **消息通信**:H5 ↔ Flutter 的双向消息收发
91
+ * 2. **模块调用**:通过 `module<T>()` 获取类型安全的原生模块代理
92
+ */
93
+ export class MrpBridge {
94
+ private _messageHandlers: WrappedHandler[] = [];
95
+ private _ready = false;
96
+ private _pendingMessages: PendingMessage[] = [];
97
+ private _readyPromiseResolve: (() => void) | null = null;
98
+ private _readyPromise: Promise<void>;
99
+ private _retryCount = 0;
100
+
101
+ constructor() {
102
+ this._readyPromise = new Promise<void>((resolve) => {
103
+ this._readyPromiseResolve = resolve;
104
+ });
105
+
106
+ // 监听 Flutter → H5 消息
107
+ window.addEventListener('flutterMessage', ((event: CustomEvent) => {
108
+ const msg = event.detail as BridgeMessage;
109
+ // 复制一份避免迭代中修改
110
+ const handlers = [...this._messageHandlers];
111
+ for (const handler of handlers) {
112
+ try {
113
+ handler(msg);
114
+ } catch (e) {
115
+ console.error('[MRP Bridge] 消息处理错误:', e);
116
+ }
117
+ }
118
+ }) as EventListener);
119
+
120
+ this._checkReady();
121
+ }
122
+
123
+ // ---- 就绪检测 ----
124
+
125
+ private _checkReady(): void {
126
+ if (window.__FLUTTER_BRIDGE__) {
127
+ this._ready = true;
128
+ this._readyPromiseResolve?.();
129
+ this._pendingMessages.forEach((msg) => this._doSend(msg));
130
+ this._pendingMessages = [];
131
+ } else {
132
+ this._retryCount++;
133
+ if (this._retryCount < 50) {
134
+ setTimeout(() => this._checkReady(), 100);
135
+ } else {
136
+ console.warn(
137
+ '[MRP Bridge] Flutter Bridge 未注入,可能不在容器环境中运行',
138
+ );
139
+ }
140
+ }
141
+ }
142
+
143
+ /** 等待 Bridge 就绪 */
144
+ whenReady(): Promise<void> {
145
+ return this._readyPromise;
146
+ }
147
+
148
+ /** 是否在 Flutter WebView 容器中运行 */
149
+ get isInContainer(): boolean {
150
+ return !!window.__FLUTTER_BRIDGE__ || !!(window as any).flutter_inappwebview;
151
+ }
152
+
153
+ // ---- 消息发送 ----
154
+
155
+ /**
156
+ * 发送消息到 Flutter
157
+ * @param type - 消息类型
158
+ * @param action - 动作名称
159
+ * @param payload - 消息数据
160
+ */
161
+ send<R = unknown>(
162
+ type: string,
163
+ action: string,
164
+ payload: Record<string, unknown> = {},
165
+ ): Promise<R> {
166
+ const msg: PendingMessage = { type, action, payload };
167
+ if (this._ready) {
168
+ return this._doSend(msg);
169
+ }
170
+ this._pendingMessages.push(msg);
171
+ return Promise.resolve(null as R);
172
+ }
173
+
174
+ private _doSend<R>(msg: PendingMessage): Promise<R> {
175
+ if (window.__FLUTTER_BRIDGE__) {
176
+ return window.__FLUTTER_BRIDGE__.send(
177
+ msg.type,
178
+ msg.action,
179
+ msg.payload,
180
+ ) as Promise<R>;
181
+ }
182
+ return Promise.resolve(null as R);
183
+ }
184
+
185
+ // ---- 消息监听 ----
186
+
187
+ /**
188
+ * 监听 Flutter 发来的所有消息
189
+ * @returns 取消监听的函数
190
+ */
191
+ onMessage<T = Record<string, unknown>>(
192
+ handler: MessageHandler<T>,
193
+ ): Unsubscribe {
194
+ this._messageHandlers.push(handler as WrappedHandler);
195
+ return () => {
196
+ this._messageHandlers = this._messageHandlers.filter(
197
+ (h) => h !== handler,
198
+ );
199
+ };
200
+ }
201
+
202
+ /**
203
+ * 监听特定类型/动作的消息
204
+ * @param type - 消息类型
205
+ * @param action - 动作名称,传 null 匹配所有
206
+ */
207
+ on<T = Record<string, unknown>>(
208
+ type: string,
209
+ action: string | null,
210
+ handler: MessageHandler<T>,
211
+ ): Unsubscribe {
212
+ const wrappedHandler: WrappedHandler = (msg) => {
213
+ if (msg.type === type && (action === null || msg.action === action)) {
214
+ (handler as MessageHandler)(msg);
215
+ }
216
+ };
217
+ wrappedHandler._original = handler as MessageHandler;
218
+ wrappedHandler._type = type;
219
+ wrappedHandler._action = action;
220
+ this._messageHandlers.push(wrappedHandler);
221
+ return () => {
222
+ this._messageHandlers = this._messageHandlers.filter(
223
+ (h) => h !== wrappedHandler,
224
+ );
225
+ };
226
+ }
227
+
228
+ // ---- 原生模块调用 ----
229
+
230
+ /**
231
+ * 获取模块代理,按命名空间调用原生 API(类型安全)。
232
+ *
233
+ * 搭配 TypeScript Generator 生成的类型使用,获得完整的类型推断。
234
+ *
235
+ * @param namespace - 模块命名空间
236
+ * @returns 模块方法代理对象
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * import type { AudioModule } from '../generated/audio_module';
241
+ * import type { UserModule } from '../generated/user_module';
242
+ *
243
+ * const audio = bridge.module<AudioModule>('audio');
244
+ * await audio.play({ soundId: 'success', volume: 0.8 });
245
+ *
246
+ * const user = bridge.module<UserModule>('user');
247
+ * const profile = await user.getProfile();
248
+ * ```
249
+ */
250
+ module<
251
+ T extends object = Record<
252
+ string,
253
+ (...args: any[]) => Promise<any>
254
+ >,
255
+ >(namespace: string): T {
256
+ return new Proxy({} as T, {
257
+ get: (_target, method: string | symbol) => {
258
+ if (typeof method !== 'string') return undefined;
259
+ return async (...args: unknown[]) => {
260
+ if (window.NativeModules?.[namespace]?.[method]) {
261
+ const result = await window.NativeModules[namespace][method](...args);
262
+ // 检查模块访问被拒绝错误
263
+ if (
264
+ result &&
265
+ typeof result === 'object' &&
266
+ (result as Record<string, unknown>).__error__ === 'MODULE_ACCESS_DENIED'
267
+ ) {
268
+ console.error(
269
+ `[MRP Bridge] 模块访问被拒绝: ${namespace}.${method}`,
270
+ (result as Record<string, unknown>).__message__,
271
+ );
272
+ return null;
273
+ }
274
+ return result;
275
+ }
276
+ console.warn(
277
+ `[MRP Bridge] NativeModules.${namespace}.${method} 不存在`,
278
+ );
279
+ return Promise.resolve(null);
280
+ };
281
+ },
282
+ });
283
+ }
284
+
285
+ }
286
+
287
+ // ============================================================
288
+ // 单例 & 导出
289
+ // ============================================================
290
+
291
+ /** Bridge 单例实例 */
292
+ export const bridge = new MrpBridge();
293
+
294
+ // 挂载到 window,方便非模块化项目使用
295
+ window.mrpBridge = bridge;
296
+
297
+ export default bridge;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 容器内置系统模块类型定义与工厂函数。
3
+ *
4
+ * System 模块由容器自动注册,不受 `allowedModules` 白名单限制,始终可用。
5
+ * 通过 `getSystemModule()` 或 `bridge.module<SystemModule>('system')` 访问。
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { getSystemModule } from '@h5-container/bridge-core-ts';
10
+ *
11
+ * const system = getSystemModule();
12
+ * const { allowed } = await system.isModuleAllowed({ namespace: 'audio' });
13
+ * const { modules } = await system.getAvailableModules();
14
+ * const info = await system.getContainerInfo();
15
+ * ```
16
+ */
17
+
18
+ import { bridge } from './mrp-bridge';
19
+
20
+ // ============================================================
21
+ // System 模块返回值类型
22
+ // ============================================================
23
+
24
+ /** isModuleAllowed 返回值 */
25
+ export interface ModuleAllowedResult {
26
+ allowed: boolean;
27
+ namespace: string;
28
+ error?: string;
29
+ }
30
+
31
+ /** getAvailableModules 返回值 */
32
+ export interface AvailableModulesResult {
33
+ modules: string[];
34
+ }
35
+
36
+ /** getContainerInfo 返回值 */
37
+ export interface ContainerInfoResult {
38
+ packageId: string | null;
39
+ registeredModules: string[];
40
+ allowedModules: string[] | null;
41
+ [key: string]: unknown;
42
+ }
43
+
44
+ // ============================================================
45
+ // System 模块接口
46
+ // ============================================================
47
+
48
+ /**
49
+ * 容器内置系统模块接口。
50
+ *
51
+ * 提供模块权限查询、可用模块列表、容器元信息获取等能力。
52
+ */
53
+ export interface SystemModule {
54
+ /** 查询指定模块是否被当前包授权 */
55
+ isModuleAllowed(params: { namespace: string }): Promise<ModuleAllowedResult>;
56
+ /** 获取当前包可用的所有模块命名空间列表 */
57
+ getAvailableModules(): Promise<AvailableModulesResult>;
58
+ /** 获取容器元信息 */
59
+ getContainerInfo(): Promise<ContainerInfoResult>;
60
+ }
61
+
62
+ // ============================================================
63
+ // 工厂函数
64
+ // ============================================================
65
+
66
+ /**
67
+ * 获取系统模块的类型安全代理。
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const system = getSystemModule();
72
+ * const { allowed } = await system.isModuleAllowed({ namespace: 'audio' });
73
+ * ```
74
+ */
75
+ export function getSystemModule(): SystemModule {
76
+ return bridge.module<SystemModule>('system');
77
+ }