@heybox/hb-sdk 0.1.3 → 0.2.0-alpha.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 (67) hide show
  1. package/README.md +149 -345
  2. package/bin/hb-sdk.cjs +3 -0
  3. package/dist/cli.cjs +10117 -0
  4. package/dist/devtools/mock-host/index.html +252 -0
  5. package/dist/devtools/mock-host/main.js +975 -0
  6. package/dist/index.cjs.js +474 -85
  7. package/dist/index.esm.js +465 -71
  8. package/dist/protocol.cjs.js +163 -0
  9. package/dist/protocol.esm.js +148 -0
  10. package/dist/templates/vue3-vite-ts/.gitignore.ejs +5 -0
  11. package/dist/templates/vue3-vite-ts/README.md.ejs +42 -0
  12. package/dist/templates/vue3-vite-ts/index.html.ejs +12 -0
  13. package/dist/templates/vue3-vite-ts/package.json.ejs +28 -0
  14. package/dist/templates/vue3-vite-ts/src/App.vue +63 -0
  15. package/dist/templates/vue3-vite-ts/src/__tests__/App.spec.ts +67 -0
  16. package/dist/templates/vue3-vite-ts/src/main.ts +5 -0
  17. package/dist/templates/vue3-vite-ts/src/styles.css +60 -0
  18. package/dist/templates/vue3-vite-ts/src/vite-env.d.ts +1 -0
  19. package/dist/templates/vue3-vite-ts/tsconfig.app.json +17 -0
  20. package/dist/templates/vue3-vite-ts/tsconfig.json +11 -0
  21. package/dist/templates/vue3-vite-ts/tsconfig.node.json +11 -0
  22. package/dist/templates/vue3-vite-ts/vite.config.ts +6 -0
  23. package/dist/templates/vue3-vite-ts/vitest.config.ts +10 -0
  24. package/package.json +30 -5
  25. package/skill/SKILL.md +95 -0
  26. package/skill/references/api-protocol.md +135 -0
  27. package/skill/references/api-root.md +346 -0
  28. package/skill/references/cli.md +360 -0
  29. package/skill/references/examples.md +107 -0
  30. package/skill/references/llms-index.md +44 -0
  31. package/skill/references/recipes.md +374 -0
  32. package/skill/references/safety-boundaries.md +28 -0
  33. package/skill/references/smoke-evaluation.md +24 -0
  34. package/skill/scripts/check-references.mjs +14 -0
  35. package/skill/scripts/package-skill.mjs +60 -0
  36. package/skill/scripts/package-skill.sh +6 -0
  37. package/skill/scripts/skill-metadata.mjs +74 -0
  38. package/skill/scripts/sync-references.mjs +541 -0
  39. package/skill/scripts/validate-skill.mjs +233 -0
  40. package/skill/skill.json +11 -0
  41. package/types/core/client.d.ts +23 -3
  42. package/types/core/errors.d.ts +45 -2
  43. package/types/core/sdk.d.ts +78 -10
  44. package/types/core/singleton.d.ts +33 -7
  45. package/types/core/utils.d.ts +2 -0
  46. package/types/index.d.ts +14 -6
  47. package/types/modules/auth/index.d.ts +35 -0
  48. package/types/modules/network/index.d.ts +120 -0
  49. package/types/modules/share/index.d.ts +9 -5
  50. package/types/modules/share/screenshot.d.ts +9 -3
  51. package/types/modules/share/show-share-menu.d.ts +9 -3
  52. package/types/modules/share/types.d.ts +24 -4
  53. package/types/modules/storage/index.d.ts +56 -0
  54. package/types/modules/user/get-info.d.ts +6 -2
  55. package/types/modules/user/index.d.ts +8 -10
  56. package/types/modules/user/types.d.ts +1 -0
  57. package/types/modules/viewport/index.d.ts +71 -0
  58. package/types/protocol/capabilities.d.ts +180 -0
  59. package/types/protocol/guards.d.ts +6 -1
  60. package/types/protocol/types.d.ts +19 -4
  61. package/types/protocol.d.ts +13 -0
  62. package/types/modules/system/get-storage.d.ts +0 -15
  63. package/types/modules/system/get-window-info.d.ts +0 -16
  64. package/types/modules/system/index.d.ts +0 -23
  65. package/types/modules/system/set-storage.d.ts +0 -12
  66. package/types/modules/system/types.d.ts +0 -34
  67. package/types/modules/user/login.d.ts +0 -18
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+
3
+ /** 小程序沙盒通信命名空间,父容器与 iframe 内 SDK 必须保持一致。 */
4
+ const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
5
+ /** 小程序沙盒通信协议版本。 */
6
+ const MINI_PROGRAM_MESSAGE_VERSION = 1;
7
+ /** 父容器注入到小程序 URL 上的通信 nonce 参数名。 */
8
+ const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
9
+ /** SDK 初始化时发给父容器的握手方法名。 */
10
+ const SDK_HANDSHAKE_METHOD = 'sdk.handshake';
11
+
12
+ /**
13
+ * 判断未知数据是否符合小程序 bridge 消息信封。
14
+ *
15
+ * @param value 待判断的未知数据。
16
+ * @returns 当数据满足小程序 bridge 消息基础结构时返回 `true`。
17
+ */
18
+ function isMiniProgramBridgeMessage(value) {
19
+ if (!value || typeof value !== 'object') {
20
+ return false;
21
+ }
22
+ const message = value;
23
+ return (message.namespace === MINI_PROGRAM_MESSAGE_NAMESPACE &&
24
+ message.version === MINI_PROGRAM_MESSAGE_VERSION &&
25
+ typeof message.nonce === 'string' &&
26
+ typeof message.type === 'string');
27
+ }
28
+
29
+ /**
30
+ * 登录授权能力方法名。
31
+ *
32
+ * @remarks
33
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
34
+ */
35
+ const AUTH_LOGIN_METHOD = 'auth.login';
36
+ /**
37
+ * 用户信息能力方法名。
38
+ *
39
+ * @remarks
40
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
41
+ */
42
+ const USER_GET_INFO_METHOD = 'user.getInfo';
43
+ /**
44
+ * 展示分享面板能力方法名。
45
+ *
46
+ * @remarks
47
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
48
+ */
49
+ const SHARE_SHOW_SHARE_MENU_METHOD = 'share.showShareMenu';
50
+ /**
51
+ * 截图分享能力方法名。
52
+ *
53
+ * @remarks
54
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
55
+ */
56
+ const SHARE_SCREENSHOT_METHOD = 'share.screenshot';
57
+ /**
58
+ * 视口窗口信息能力方法名。
59
+ *
60
+ * @remarks
61
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
62
+ */
63
+ const VIEWPORT_GET_WINDOW_INFO_METHOD = 'viewport.getWindowInfo';
64
+ /**
65
+ * 读取小程序隔离 storage 能力方法名。
66
+ *
67
+ * @remarks
68
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
69
+ */
70
+ const STORAGE_GET_STORAGE_METHOD = 'storage.getStorage';
71
+ /**
72
+ * 写入小程序隔离 storage 能力方法名。
73
+ *
74
+ * @remarks
75
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
76
+ */
77
+ const STORAGE_SET_STORAGE_METHOD = 'storage.setStorage';
78
+ /**
79
+ * 发起网络请求能力方法名。
80
+ *
81
+ * @remarks
82
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
83
+ */
84
+ const NETWORK_REQUEST_METHOD = 'network.request';
85
+ /**
86
+ * 小程序开放能力目录。
87
+ *
88
+ * @remarks
89
+ * 新增 bridge method 时应先更新这里,再分别补齐 SDK 模块、runtime handler 与测试。
90
+ */
91
+ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
92
+ {
93
+ method: AUTH_LOGIN_METHOD,
94
+ module: 'auth',
95
+ capability: AUTH_LOGIN_METHOD,
96
+ permission: 'auth.login',
97
+ risk: 'medium',
98
+ },
99
+ {
100
+ method: USER_GET_INFO_METHOD,
101
+ module: 'user',
102
+ capability: USER_GET_INFO_METHOD,
103
+ permission: 'user.getInfo',
104
+ risk: 'medium',
105
+ },
106
+ {
107
+ method: SHARE_SHOW_SHARE_MENU_METHOD,
108
+ module: 'share',
109
+ capability: SHARE_SHOW_SHARE_MENU_METHOD,
110
+ permission: 'share.basic',
111
+ risk: 'low',
112
+ },
113
+ {
114
+ method: SHARE_SCREENSHOT_METHOD,
115
+ module: 'share',
116
+ capability: SHARE_SCREENSHOT_METHOD,
117
+ permission: 'share.screenshot',
118
+ risk: 'medium',
119
+ },
120
+ {
121
+ method: VIEWPORT_GET_WINDOW_INFO_METHOD,
122
+ module: 'viewport',
123
+ capability: VIEWPORT_GET_WINDOW_INFO_METHOD,
124
+ permission: 'viewport.windowInfo',
125
+ risk: 'low',
126
+ },
127
+ {
128
+ method: STORAGE_GET_STORAGE_METHOD,
129
+ module: 'storage',
130
+ capability: STORAGE_GET_STORAGE_METHOD,
131
+ permission: 'storage.read',
132
+ risk: 'low',
133
+ },
134
+ {
135
+ method: STORAGE_SET_STORAGE_METHOD,
136
+ module: 'storage',
137
+ capability: STORAGE_SET_STORAGE_METHOD,
138
+ permission: 'storage.write',
139
+ risk: 'medium',
140
+ },
141
+ {
142
+ method: NETWORK_REQUEST_METHOD,
143
+ module: 'network',
144
+ capability: NETWORK_REQUEST_METHOD,
145
+ permission: 'network.request',
146
+ risk: 'high',
147
+ },
148
+ ];
149
+
150
+ exports.AUTH_LOGIN_METHOD = AUTH_LOGIN_METHOD;
151
+ exports.MINI_PROGRAM_BRIDGE_NONCE_PARAM = MINI_PROGRAM_BRIDGE_NONCE_PARAM;
152
+ exports.MINI_PROGRAM_MESSAGE_NAMESPACE = MINI_PROGRAM_MESSAGE_NAMESPACE;
153
+ exports.MINI_PROGRAM_MESSAGE_VERSION = MINI_PROGRAM_MESSAGE_VERSION;
154
+ exports.MINI_PROGRAM_PROTOCOL_CAPABILITIES = MINI_PROGRAM_PROTOCOL_CAPABILITIES;
155
+ exports.NETWORK_REQUEST_METHOD = NETWORK_REQUEST_METHOD;
156
+ exports.SDK_HANDSHAKE_METHOD = SDK_HANDSHAKE_METHOD;
157
+ exports.SHARE_SCREENSHOT_METHOD = SHARE_SCREENSHOT_METHOD;
158
+ exports.SHARE_SHOW_SHARE_MENU_METHOD = SHARE_SHOW_SHARE_MENU_METHOD;
159
+ exports.STORAGE_GET_STORAGE_METHOD = STORAGE_GET_STORAGE_METHOD;
160
+ exports.STORAGE_SET_STORAGE_METHOD = STORAGE_SET_STORAGE_METHOD;
161
+ exports.USER_GET_INFO_METHOD = USER_GET_INFO_METHOD;
162
+ exports.VIEWPORT_GET_WINDOW_INFO_METHOD = VIEWPORT_GET_WINDOW_INFO_METHOD;
163
+ exports.isMiniProgramBridgeMessage = isMiniProgramBridgeMessage;
@@ -0,0 +1,148 @@
1
+ /** 小程序沙盒通信命名空间,父容器与 iframe 内 SDK 必须保持一致。 */
2
+ const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
3
+ /** 小程序沙盒通信协议版本。 */
4
+ const MINI_PROGRAM_MESSAGE_VERSION = 1;
5
+ /** 父容器注入到小程序 URL 上的通信 nonce 参数名。 */
6
+ const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
7
+ /** SDK 初始化时发给父容器的握手方法名。 */
8
+ const SDK_HANDSHAKE_METHOD = 'sdk.handshake';
9
+
10
+ /**
11
+ * 判断未知数据是否符合小程序 bridge 消息信封。
12
+ *
13
+ * @param value 待判断的未知数据。
14
+ * @returns 当数据满足小程序 bridge 消息基础结构时返回 `true`。
15
+ */
16
+ function isMiniProgramBridgeMessage(value) {
17
+ if (!value || typeof value !== 'object') {
18
+ return false;
19
+ }
20
+ const message = value;
21
+ return (message.namespace === MINI_PROGRAM_MESSAGE_NAMESPACE &&
22
+ message.version === MINI_PROGRAM_MESSAGE_VERSION &&
23
+ typeof message.nonce === 'string' &&
24
+ typeof message.type === 'string');
25
+ }
26
+
27
+ /**
28
+ * 登录授权能力方法名。
29
+ *
30
+ * @remarks
31
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
32
+ */
33
+ const AUTH_LOGIN_METHOD = 'auth.login';
34
+ /**
35
+ * 用户信息能力方法名。
36
+ *
37
+ * @remarks
38
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
39
+ */
40
+ const USER_GET_INFO_METHOD = 'user.getInfo';
41
+ /**
42
+ * 展示分享面板能力方法名。
43
+ *
44
+ * @remarks
45
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
46
+ */
47
+ const SHARE_SHOW_SHARE_MENU_METHOD = 'share.showShareMenu';
48
+ /**
49
+ * 截图分享能力方法名。
50
+ *
51
+ * @remarks
52
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
53
+ */
54
+ const SHARE_SCREENSHOT_METHOD = 'share.screenshot';
55
+ /**
56
+ * 视口窗口信息能力方法名。
57
+ *
58
+ * @remarks
59
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
60
+ */
61
+ const VIEWPORT_GET_WINDOW_INFO_METHOD = 'viewport.getWindowInfo';
62
+ /**
63
+ * 读取小程序隔离 storage 能力方法名。
64
+ *
65
+ * @remarks
66
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
67
+ */
68
+ const STORAGE_GET_STORAGE_METHOD = 'storage.getStorage';
69
+ /**
70
+ * 写入小程序隔离 storage 能力方法名。
71
+ *
72
+ * @remarks
73
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
74
+ */
75
+ const STORAGE_SET_STORAGE_METHOD = 'storage.setStorage';
76
+ /**
77
+ * 发起网络请求能力方法名。
78
+ *
79
+ * @remarks
80
+ * 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
81
+ */
82
+ const NETWORK_REQUEST_METHOD = 'network.request';
83
+ /**
84
+ * 小程序开放能力目录。
85
+ *
86
+ * @remarks
87
+ * 新增 bridge method 时应先更新这里,再分别补齐 SDK 模块、runtime handler 与测试。
88
+ */
89
+ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
90
+ {
91
+ method: AUTH_LOGIN_METHOD,
92
+ module: 'auth',
93
+ capability: AUTH_LOGIN_METHOD,
94
+ permission: 'auth.login',
95
+ risk: 'medium',
96
+ },
97
+ {
98
+ method: USER_GET_INFO_METHOD,
99
+ module: 'user',
100
+ capability: USER_GET_INFO_METHOD,
101
+ permission: 'user.getInfo',
102
+ risk: 'medium',
103
+ },
104
+ {
105
+ method: SHARE_SHOW_SHARE_MENU_METHOD,
106
+ module: 'share',
107
+ capability: SHARE_SHOW_SHARE_MENU_METHOD,
108
+ permission: 'share.basic',
109
+ risk: 'low',
110
+ },
111
+ {
112
+ method: SHARE_SCREENSHOT_METHOD,
113
+ module: 'share',
114
+ capability: SHARE_SCREENSHOT_METHOD,
115
+ permission: 'share.screenshot',
116
+ risk: 'medium',
117
+ },
118
+ {
119
+ method: VIEWPORT_GET_WINDOW_INFO_METHOD,
120
+ module: 'viewport',
121
+ capability: VIEWPORT_GET_WINDOW_INFO_METHOD,
122
+ permission: 'viewport.windowInfo',
123
+ risk: 'low',
124
+ },
125
+ {
126
+ method: STORAGE_GET_STORAGE_METHOD,
127
+ module: 'storage',
128
+ capability: STORAGE_GET_STORAGE_METHOD,
129
+ permission: 'storage.read',
130
+ risk: 'low',
131
+ },
132
+ {
133
+ method: STORAGE_SET_STORAGE_METHOD,
134
+ module: 'storage',
135
+ capability: STORAGE_SET_STORAGE_METHOD,
136
+ permission: 'storage.write',
137
+ risk: 'medium',
138
+ },
139
+ {
140
+ method: NETWORK_REQUEST_METHOD,
141
+ module: 'network',
142
+ capability: NETWORK_REQUEST_METHOD,
143
+ permission: 'network.request',
144
+ risk: 'high',
145
+ },
146
+ ];
147
+
148
+ export { AUTH_LOGIN_METHOD, MINI_PROGRAM_BRIDGE_NONCE_PARAM, MINI_PROGRAM_MESSAGE_NAMESPACE, MINI_PROGRAM_MESSAGE_VERSION, MINI_PROGRAM_PROTOCOL_CAPABILITIES, NETWORK_REQUEST_METHOD, SDK_HANDSHAKE_METHOD, SHARE_SCREENSHOT_METHOD, SHARE_SHOW_SHARE_MENU_METHOD, STORAGE_GET_STORAGE_METHOD, STORAGE_SET_STORAGE_METHOD, USER_GET_INFO_METHOD, VIEWPORT_GET_WINDOW_INFO_METHOD, isMiniProgramBridgeMessage };
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ coverage
4
+ *.local
5
+ .DS_Store
@@ -0,0 +1,42 @@
1
+ # <%= projectName %>
2
+
3
+ 这是通过 `hb-sdk create` 生成的黑盒外部小程序开发模板。项目使用 Vue 3、Vite、TypeScript 和 `@heybox/hb-sdk`。
4
+
5
+ ## 使用命令
6
+
7
+ ```bash
8
+ npm install
9
+ npm run dev
10
+ npm run typecheck
11
+ npm run test:unit
12
+ npm run build
13
+ ```
14
+
15
+ ## 开发模式
16
+
17
+ - `npm run dev`:启动本地 Vite 服务和 `hb-sdk` 内置 mock runtime host,适合本地调试 SDK 能力;调试页内可点击按钮在 Mac 版 APP 中启动同一页面。
18
+ - `npm run build`:先执行 TypeScript 检查,再构建生产产物。
19
+
20
+ ## 更多能力
21
+
22
+ 模板页面只保留最小接入示例。其他常用能力可以直接从 `@heybox/hb-sdk` 调用:
23
+
24
+ ```ts
25
+ import hbSDK from '@heybox/hb-sdk';
26
+
27
+ await hbSDK.share.showShareMenu({
28
+ title: '<%= projectName %>',
29
+ desc: '分享描述',
30
+ url: window.location.href,
31
+ });
32
+
33
+ await hbSDK.storage.setStorage({
34
+ key: 'settings',
35
+ data: { theme: 'dark' },
36
+ });
37
+
38
+ const response = await hbSDK.network.request({
39
+ method: 'GET',
40
+ url: 'https://jsonplaceholder.typicode.com/todos/1',
41
+ });
42
+ ```
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title><%= projectName %></title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "<%= packageName %>",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "hb-sdk dev",
8
+ "build": "vue-tsc --noEmit && vite build",
9
+ "preview": "vite preview",
10
+ "typecheck": "vue-tsc --noEmit",
11
+ "test:unit": "vitest run",
12
+ "test:unit:watch": "vitest"
13
+ },
14
+ "dependencies": {
15
+ "@heybox/hb-sdk": "^<%= sdkVersion %>",
16
+ "vue": "^3.5.34"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^25.7.0",
20
+ "@vitejs/plugin-vue": "^6.0.6",
21
+ "@vue/test-utils": "^2.4.10",
22
+ "happy-dom": "^20.9.0",
23
+ "typescript": "^6.0.3",
24
+ "vite": "^8.0.12",
25
+ "vitest": "^4.1.6",
26
+ "vue-tsc": "^3.2.9"
27
+ }
28
+ }
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <main class="page">
3
+ <section class="card">
4
+ <p class="eyebrow">Heybox Mini Program</p>
5
+ <h1><%= projectName %></h1>
6
+ <p class="intro">
7
+ 这个模板只保留最小接入示例:等待 SDK ready、读取用户信息,并在需要时唤起登录。
8
+ </p>
9
+
10
+ <p class="status" data-testid="status">{{ status }}</p>
11
+
12
+ <button type="button" :disabled="loading" data-testid="login" @click="login">
13
+ 登录并刷新用户信息
14
+ </button>
15
+
16
+ <pre data-testid="output">{{ output }}</pre>
17
+ </section>
18
+ </main>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import hbSDK from '@heybox/hb-sdk';
23
+ import { onMounted, ref } from 'vue';
24
+
25
+ const loading = ref(false);
26
+ const status = ref('正在连接 SDK...');
27
+ const output = ref('等待 user.getInfo 返回结果。');
28
+
29
+ onMounted(() => {
30
+ void loadUserInfo();
31
+ });
32
+
33
+ async function loadUserInfo() {
34
+ await runSdkAction('user.getInfo', async () => {
35
+ await hbSDK.ready();
36
+ return hbSDK.user.getInfo();
37
+ });
38
+ }
39
+
40
+ async function login() {
41
+ await runSdkAction('auth.login', () => hbSDK.auth.login());
42
+ }
43
+
44
+ async function runSdkAction(label: string, action: () => Promise<unknown>) {
45
+ loading.value = true;
46
+ status.value = `${label} 调用中...`;
47
+
48
+ try {
49
+ const result = await action();
50
+ status.value = `${label} 调用成功`;
51
+ output.value = JSON.stringify(result, null, 2);
52
+ } catch (error) {
53
+ status.value = `${label} 调用失败`;
54
+ output.value = readErrorMessage(error);
55
+ } finally {
56
+ loading.value = false;
57
+ }
58
+ }
59
+
60
+ function readErrorMessage(error: unknown) {
61
+ return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
62
+ }
63
+ </script>
@@ -0,0 +1,67 @@
1
+ import { flushPromises, mount } from '@vue/test-utils';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import App from '../App.vue';
4
+
5
+ const sdkMock = vi.hoisted(() => ({
6
+ authLogin: vi.fn(),
7
+ ready: vi.fn(),
8
+ userGetInfo: vi.fn(),
9
+ }));
10
+
11
+ vi.mock('@heybox/hb-sdk', () => ({
12
+ default: {
13
+ auth: {
14
+ login: sdkMock.authLogin,
15
+ },
16
+ ready: sdkMock.ready,
17
+ user: {
18
+ getInfo: sdkMock.userGetInfo,
19
+ },
20
+ },
21
+ }));
22
+
23
+ describe('App', () => {
24
+ beforeEach(() => {
25
+ vi.resetAllMocks();
26
+
27
+ sdkMock.ready.mockResolvedValue(undefined);
28
+ sdkMock.userGetInfo.mockResolvedValue({
29
+ isLogin: true,
30
+ userInfo: {
31
+ avatar: '',
32
+ heybox_id: '10001',
33
+ nickname: '模板用户',
34
+ },
35
+ });
36
+ sdkMock.authLogin.mockResolvedValue({
37
+ isLogin: true,
38
+ userInfo: {
39
+ avatar: '',
40
+ heybox_id: '10002',
41
+ nickname: '登录用户',
42
+ },
43
+ });
44
+ });
45
+
46
+ it('connects to hb-sdk and renders user info', async () => {
47
+ const wrapper = mount(App);
48
+ await flushPromises();
49
+
50
+ expect(sdkMock.ready).toHaveBeenCalledTimes(1);
51
+ expect(sdkMock.userGetInfo).toHaveBeenCalledTimes(1);
52
+ expect(wrapper.get('[data-testid="status"]').text()).toBe('user.getInfo 调用成功');
53
+ expect(wrapper.get('[data-testid="output"]').text()).toContain('模板用户');
54
+ });
55
+
56
+ it('logs in and refreshes the output', async () => {
57
+ const wrapper = mount(App);
58
+ await flushPromises();
59
+
60
+ await wrapper.get('[data-testid="login"]').trigger('click');
61
+ await flushPromises();
62
+
63
+ expect(sdkMock.authLogin).toHaveBeenCalledTimes(1);
64
+ expect(wrapper.get('[data-testid="status"]').text()).toBe('auth.login 调用成功');
65
+ expect(wrapper.get('[data-testid="output"]').text()).toContain('登录用户');
66
+ });
67
+ });
@@ -0,0 +1,5 @@
1
+ import { createApp } from 'vue';
2
+ import App from './App.vue';
3
+ import './styles.css';
4
+
5
+ createApp(App).mount('#app');
@@ -0,0 +1,60 @@
1
+ :root {
2
+ color: #1f2933;
3
+ background: #f4f6f8;
4
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
5
+ line-height: 1.5;
6
+ }
7
+ * {
8
+ box-sizing: border-box;
9
+ }
10
+ body {
11
+ margin: 0; min-width: 320px;
12
+ }
13
+ .page {
14
+ width: min(100%, 720px);
15
+ margin: 0 auto;
16
+ padding: 40px 20px;
17
+ }
18
+ .card {
19
+ border: 1px solid #d8dee6;
20
+ border-radius: 8px;
21
+ background: #fff;
22
+ padding: 24px;
23
+ }
24
+ .eyebrow {
25
+ margin: 0 0 8px;
26
+ color: #287a4b;
27
+ font-size: 13px;
28
+ font-weight: 800;
29
+ }
30
+ h1 {
31
+ margin: 0; font-size: 36px; line-height: 1.1;
32
+ }
33
+ .intro,
34
+ .status {
35
+ color: #52616f;
36
+ }
37
+ button {
38
+ width: 100%;
39
+ min-height: 42px;
40
+ border: 0;
41
+ border-radius: 6px;
42
+ background: #1f2933;
43
+ color: #fff;
44
+ cursor: pointer;
45
+ font: inherit;
46
+ font-weight: 700;
47
+ }
48
+ button:disabled {
49
+ cursor: not-allowed; opacity: 0.6;
50
+ }
51
+ pre {
52
+ margin: 16px 0 0;
53
+ overflow: auto;
54
+ border-radius: 6px;
55
+ background: #101820;
56
+ color: #f7fbff;
57
+ padding: 14px;
58
+ white-space: pre-wrap;
59
+ word-break: break-word;
60
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "strict": true,
8
+ "jsx": "preserve",
9
+ "sourceMap": true,
10
+ "resolveJsonModule": true,
11
+ "isolatedModules": true,
12
+ "noEmit": true,
13
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
14
+ "types": ["vite/client"]
15
+ },
16
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
17
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ {
5
+ "path": "./tsconfig.app.json"
6
+ },
7
+ {
8
+ "path": "./tsconfig.node.json"
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "types": ["node"]
9
+ },
10
+ "include": ["vite.config.ts", "vitest.config.ts"]
11
+ }
@@ -0,0 +1,6 @@
1
+ import vue from '@vitejs/plugin-vue';
2
+ import { defineConfig } from 'vite';
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()],
6
+ });
@@ -0,0 +1,10 @@
1
+ import vue from '@vitejs/plugin-vue';
2
+ import { defineConfig } from 'vitest/config';
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()],
6
+ test: {
7
+ environment: 'happy-dom',
8
+ include: ['src/**/*.spec.ts'],
9
+ },
10
+ });