@heybox/hb-sdk 0.6.9 → 0.7.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.
- package/CHANGELOG.md +41 -7
- package/README.md +83 -20
- package/dist/cli-chunks/{build-BtGxhrcV.cjs → build-CWBksuZX.cjs} +2 -2
- package/dist/cli-chunks/{context-DX1KG-8A.cjs → context-B3_TJIaj.cjs} +1 -1
- package/dist/cli-chunks/{create-DAD0OF1r.cjs → create-BdDwp1Qx.cjs} +1 -1
- package/dist/cli-chunks/{dev-Dh4HY0I8.cjs → dev-PCY_9q8y.cjs} +40 -8
- package/dist/cli-chunks/{doctor-BfKdiD3H.cjs → doctor-DzEMXA58.cjs} +1 -1
- package/dist/cli-chunks/{index-tCURDEx8.cjs → index-CjT2V5RD.cjs} +15 -14
- package/dist/cli-chunks/{index-DB6Q8HaC.cjs → index-DmgF1ROG.cjs} +1 -1
- package/dist/cli-chunks/{login-D60EJLgs.cjs → login-D775i7Ub.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-DAUaP0_3.cjs → project-vite-ty-N0y8F.cjs} +1 -1
- package/dist/cli-chunks/{remote-nQDniAUY.cjs → remote-3Zmtshjq.cjs} +4 -4
- package/dist/cli-chunks/{session-D6jOKXeu.cjs → session-piLyMOuV.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/mock-host/index.html +9 -2
- package/dist/devtools/mock-host/main.js +390 -801
- package/dist/index.cjs.js +46 -90
- package/dist/index.esm.js +46 -90
- package/dist/protocol.cjs.js +98 -42
- package/dist/protocol.esm.js +93 -39
- package/dist/templates/vue3-vite-ts/README.md.ejs +1 -1
- package/dist/templates/vue3-vite-ts/src/App.vue +22 -17
- package/dist/templates/vue3-vite-ts/src/__tests__/App.spec.ts +37 -29
- package/dist/templates/vue3-vite-ts/src/auth-handoff.ts +46 -0
- package/dist/vite.cjs.js +2 -2
- package/dist/vite.esm.js +2 -2
- package/package.json +2 -2
- package/skill/SKILL.md +16 -12
- package/skill/references/api-protocol.md +22 -28
- package/skill/references/api-root.md +127 -112
- package/skill/references/cli.md +15 -20
- package/skill/references/examples.md +29 -17
- package/skill/references/recipes.md +180 -134
- package/skill/references/safety-boundaries.md +9 -3
- package/skill/scripts/sync-references.mjs +235 -139
- package/skill/scripts/validate-skill.mjs +18 -14
- package/skill/skill.json +4 -4
- package/types/index.d.ts +4 -4
- package/types/modules/auth/index.d.ts +16 -28
- package/types/modules/cloud/index.d.ts +2 -2
- package/types/modules/network/index.d.ts +1 -2
- package/types/modules/user/get-info.d.ts +4 -4
- package/types/modules/user/get-local-identity.d.ts +9 -0
- package/types/modules/user/index.d.ts +13 -41
- package/types/modules/user/revoke-authorization.d.ts +7 -0
- package/types/modules/user/steam-game-list.d.ts +72 -0
- package/types/modules/user/types.d.ts +37 -302
- package/types/protocol/capabilities.d.ts +24 -49
- package/types/protocol/constants.d.ts +1 -1
- package/types/protocol/network-policy.d.ts +10 -0
- package/types/protocol/types.d.ts +10 -6
- package/types/protocol.d.ts +4 -3
- package/types/modules/user/get-current-user-detail.d.ts +0 -9
- package/types/modules/user/get-current-user-profile.d.ts +0 -9
- package/types/modules/user/get-platform-account-info.d.ts +0 -12
- package/types/modules/user/get-platform-account-overview.d.ts +0 -9
- package/types/modules/user/get-steam-game-list.d.ts +0 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** 小程序沙盒通信命名空间,父容器与 iframe 内 SDK 必须保持一致。 */
|
|
2
2
|
const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
|
|
3
3
|
/** 小程序沙盒通信协议版本。 */
|
|
4
|
-
const MINI_PROGRAM_MESSAGE_VERSION =
|
|
4
|
+
const MINI_PROGRAM_MESSAGE_VERSION = 2;
|
|
5
5
|
/** 父容器注入到小程序 URL 上的通信 nonce 参数名。 */
|
|
6
6
|
const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
7
7
|
/** SDK 向 Runtime 报告 CSP 违规的内部方法名。 */
|
|
@@ -59,29 +59,97 @@ function isRecord$3(value) {
|
|
|
59
59
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
const OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS = [
|
|
63
|
+
'xiaoheihe.cn',
|
|
64
|
+
'max-c.com',
|
|
65
|
+
'debugmode.cn',
|
|
66
|
+
'maxjia.com',
|
|
67
|
+
];
|
|
68
|
+
/** Runtime / Host 在 `network.request` 分发前拒绝的小程序服务端私有路径前缀。 */
|
|
69
|
+
const MINI_PROGRAM_NETWORK_PROTECTED_PATHS = [
|
|
70
|
+
'/user_miniprogram/runtime/auth',
|
|
71
|
+
'/user_miniprogram/openapi/v1',
|
|
72
|
+
];
|
|
73
|
+
const NETWORK_REQUEST_PATHNAME_MAX_DECODE_ROUNDS = 4;
|
|
74
|
+
/** 使用 URL 解析后的 hostname 判断黑盒官方根域及其子域。 */
|
|
75
|
+
function isOfficialMiniProgramNetworkUrl(value) {
|
|
76
|
+
try {
|
|
77
|
+
const url = new URL(value);
|
|
78
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
|
|
82
|
+
return OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS.some(rootDomain => hostname === rootDomain || hostname.endsWith(`.${rootDomain}`));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
62
88
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* @remarks
|
|
66
|
-
* 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
|
|
89
|
+
* 规范化并判断 pathname 是否命中 Runtime Auth / OpenAPI 保护前缀。
|
|
90
|
+
* 解码失败或仍可继续解码时视为不安全路径。
|
|
67
91
|
*/
|
|
68
|
-
|
|
92
|
+
function isProtectedMiniProgramNetworkPath(pathname) {
|
|
93
|
+
const normalized = normalizeMiniProgramNetworkPathname(pathname);
|
|
94
|
+
if (normalized === null) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return MINI_PROGRAM_NETWORK_PROTECTED_PATHS.some(prefix => normalized === prefix || normalized.startsWith(`${prefix}/`));
|
|
98
|
+
}
|
|
99
|
+
function normalizeMiniProgramNetworkPathname(pathname) {
|
|
100
|
+
let decoded = pathname;
|
|
101
|
+
for (let round = 0; round < NETWORK_REQUEST_PATHNAME_MAX_DECODE_ROUNDS; round += 1) {
|
|
102
|
+
let next;
|
|
103
|
+
try {
|
|
104
|
+
next = decodeURIComponent(decoded);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (next === decoded) {
|
|
110
|
+
return normalizeDecodedNetworkPathname(decoded);
|
|
111
|
+
}
|
|
112
|
+
decoded = next;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
if (decodeURIComponent(decoded) !== decoded) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return normalizeDecodedNetworkPathname(decoded);
|
|
123
|
+
}
|
|
124
|
+
function normalizeDecodedNetworkPathname(decoded) {
|
|
125
|
+
const segments = [];
|
|
126
|
+
for (const segment of decoded.replace(/\\/g, '/').split('/')) {
|
|
127
|
+
if (!segment || segment === '.') {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (segment === '..') {
|
|
131
|
+
segments.pop();
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
segments.push(segment);
|
|
135
|
+
}
|
|
136
|
+
return `/${segments.join('/')}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
69
139
|
/**
|
|
70
|
-
*
|
|
140
|
+
* 登录授权能力方法名。
|
|
71
141
|
*
|
|
72
142
|
* @remarks
|
|
73
143
|
* 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
|
|
74
144
|
*/
|
|
145
|
+
const AUTH_LOGIN_METHOD = 'auth.login';
|
|
146
|
+
/** 读取当前登录态、授权状态和已获准披露资料的 bridge method 标识。 */
|
|
75
147
|
const USER_GET_INFO_METHOD = 'user.getInfo';
|
|
76
|
-
/**
|
|
77
|
-
const
|
|
78
|
-
/**
|
|
79
|
-
const
|
|
80
|
-
/**
|
|
81
|
-
const USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD = 'user.getPlatformAccountOverview';
|
|
82
|
-
/** 当前用户指定平台账号详情能力方法名。 */
|
|
83
|
-
const USER_GET_PLATFORM_ACCOUNT_INFO_METHOD = 'user.getPlatformAccountInfo';
|
|
84
|
-
/** 当前用户 Steam 游戏库能力方法名。 */
|
|
148
|
+
/** 读取当前用户小程序隔离身份的 bridge method 标识。 */
|
|
149
|
+
const USER_GET_LOCAL_IDENTITY_METHOD = 'user.getLocalIdentity';
|
|
150
|
+
/** 撤销当前用户授予本小程序授权的 bridge method 标识。 */
|
|
151
|
+
const USER_REVOKE_AUTHORIZATION_METHOD = 'user.revokeAuthorization';
|
|
152
|
+
/** 读取当前已登录用户 Steam 游戏库的 bridge method 标识。 */
|
|
85
153
|
const USER_GET_STEAM_GAME_LIST_METHOD = 'user.getSteamGameList';
|
|
86
154
|
/**
|
|
87
155
|
* 展示分享面板能力方法名。
|
|
@@ -180,38 +248,24 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
|
180
248
|
risk: 'medium',
|
|
181
249
|
},
|
|
182
250
|
{
|
|
183
|
-
method:
|
|
251
|
+
method: USER_GET_LOCAL_IDENTITY_METHOD,
|
|
184
252
|
module: 'user',
|
|
185
|
-
capability:
|
|
186
|
-
permission: 'user.
|
|
187
|
-
risk: 'high',
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
method: USER_GET_CURRENT_USER_PROFILE_METHOD,
|
|
191
|
-
module: 'user',
|
|
192
|
-
capability: USER_GET_CURRENT_USER_PROFILE_METHOD,
|
|
193
|
-
permission: 'user.currentUserProfile',
|
|
194
|
-
risk: 'high',
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
method: USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD,
|
|
198
|
-
module: 'user',
|
|
199
|
-
capability: USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD,
|
|
200
|
-
permission: 'user.platformAccount.overview',
|
|
253
|
+
capability: USER_GET_LOCAL_IDENTITY_METHOD,
|
|
254
|
+
permission: 'user.getLocalIdentity',
|
|
201
255
|
risk: 'medium',
|
|
202
256
|
},
|
|
203
257
|
{
|
|
204
|
-
method:
|
|
258
|
+
method: USER_REVOKE_AUTHORIZATION_METHOD,
|
|
205
259
|
module: 'user',
|
|
206
|
-
capability:
|
|
207
|
-
permission: 'user.
|
|
260
|
+
capability: USER_REVOKE_AUTHORIZATION_METHOD,
|
|
261
|
+
permission: 'user.revokeAuthorization',
|
|
208
262
|
risk: 'high',
|
|
209
263
|
},
|
|
210
264
|
{
|
|
211
265
|
method: USER_GET_STEAM_GAME_LIST_METHOD,
|
|
212
266
|
module: 'user',
|
|
213
267
|
capability: USER_GET_STEAM_GAME_LIST_METHOD,
|
|
214
|
-
permission: 'user.
|
|
268
|
+
permission: 'user.platformAccount.steam',
|
|
215
269
|
risk: 'high',
|
|
216
270
|
},
|
|
217
271
|
{
|
|
@@ -3587,35 +3641,6 @@ function cloneDefaultPermissionState() {
|
|
|
3587
3641
|
};
|
|
3588
3642
|
}
|
|
3589
3643
|
|
|
3590
|
-
async function getMiniProgramRuntimeUserInfo(platformAdapter) {
|
|
3591
|
-
const userId = await resolveCurrentUserId(platformAdapter);
|
|
3592
|
-
if (!userId) {
|
|
3593
|
-
return {
|
|
3594
|
-
isLogin: false,
|
|
3595
|
-
userInfo: null,
|
|
3596
|
-
};
|
|
3597
|
-
}
|
|
3598
|
-
const result = await platformAdapter.user.getUserBasicInfo(userId);
|
|
3599
|
-
return {
|
|
3600
|
-
isLogin: true,
|
|
3601
|
-
userInfo: {
|
|
3602
|
-
heybox_id: userId,
|
|
3603
|
-
nickname: result?.nickname || '',
|
|
3604
|
-
avatar: result?.avatar || '',
|
|
3605
|
-
},
|
|
3606
|
-
};
|
|
3607
|
-
}
|
|
3608
|
-
function resolveCurrentUserId(platformAdapter) {
|
|
3609
|
-
return platformAdapter.auth.getCurrentUserId()
|
|
3610
|
-
.then(userId => (userId ? String(userId) : ''))
|
|
3611
|
-
.catch(() => '');
|
|
3612
|
-
}
|
|
3613
|
-
|
|
3614
|
-
async function loginAndGetMiniProgramRuntimeUserInfo(platformAdapter) {
|
|
3615
|
-
await platformAdapter.auth.login();
|
|
3616
|
-
return getMiniProgramRuntimeUserInfo(platformAdapter);
|
|
3617
|
-
}
|
|
3618
|
-
|
|
3619
3644
|
const DEFAULT_STORAGE_KEY_MAX_LENGTH = 128;
|
|
3620
3645
|
const STORAGE_KEY_CHAR_RE = /^[A-Za-z0-9_-]+$/;
|
|
3621
3646
|
function createMiniProgramRuntimeBridgeError(code, message, data) {
|
|
@@ -3929,7 +3954,7 @@ function createLeaderboardReadStore() {
|
|
|
3929
3954
|
});
|
|
3930
3955
|
}
|
|
3931
3956
|
function isBucketEmpty(bucketKey, bucket) {
|
|
3932
|
-
return !hasInflight(bucketKey) && Object.values(bucket.caches).every(cache => cache.size === 0);
|
|
3957
|
+
return !hasInflight(bucketKey) && Object.values(bucket.caches).every((cache) => cache.size === 0);
|
|
3933
3958
|
}
|
|
3934
3959
|
function hasInflight(bucketKey) {
|
|
3935
3960
|
for (const entry of inflight.values()) {
|
|
@@ -3983,7 +4008,7 @@ function readLeaderboardPayload(payload, methodName, shape) {
|
|
|
3983
4008
|
}
|
|
3984
4009
|
function assertLeaderboardEntry(value, methodName) {
|
|
3985
4010
|
assertMiniProgramRuntimeRecord(value, `${methodName} 返回的 entry 必须是对象`);
|
|
3986
|
-
const { rank, ranked,
|
|
4011
|
+
const { rank, ranked, appUserId, score, extra, createdAt, updatedAt } = value;
|
|
3987
4012
|
if (typeof ranked !== 'boolean') {
|
|
3988
4013
|
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 ranked 必须是布尔值`);
|
|
3989
4014
|
}
|
|
@@ -3991,8 +4016,8 @@ function assertLeaderboardEntry(value, methodName) {
|
|
|
3991
4016
|
if (!validRank) {
|
|
3992
4017
|
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 rank/ranked 不合法`);
|
|
3993
4018
|
}
|
|
3994
|
-
if (typeof
|
|
3995
|
-
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的
|
|
4019
|
+
if (typeof appUserId !== 'string' || !appUserId) {
|
|
4020
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 appUserId 必须是非空字符串`);
|
|
3996
4021
|
}
|
|
3997
4022
|
if (typeof score !== 'number' || !Number.isFinite(score)) {
|
|
3998
4023
|
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 score 必须是有限数字`);
|
|
@@ -4058,6 +4083,57 @@ function readLeaderboardCursor(value) {
|
|
|
4058
4083
|
return value;
|
|
4059
4084
|
}
|
|
4060
4085
|
|
|
4086
|
+
const VALID_LOGIN_SCOPES = new Set(['identity', 'profile']);
|
|
4087
|
+
async function loginMiniProgramRuntime(payload, platformAdapter, identity) {
|
|
4088
|
+
const scopes = readLoginScopes(payload);
|
|
4089
|
+
const miniProgramId = identity?.miniProgramId?.trim();
|
|
4090
|
+
if (!miniProgramId) {
|
|
4091
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_STATE', 'auth.login 缺少可信小程序身份');
|
|
4092
|
+
}
|
|
4093
|
+
const result = await platformAdapter.auth.login({
|
|
4094
|
+
miniProgramId,
|
|
4095
|
+
scopes,
|
|
4096
|
+
trustedUserGesture: Boolean(await platformAdapter.auth.hasTrustedUserGesture?.()),
|
|
4097
|
+
});
|
|
4098
|
+
assertAuthLoginResult(result);
|
|
4099
|
+
return { code: result.code, expiresIn: result.expiresIn, scopes: [...result.scopes] };
|
|
4100
|
+
}
|
|
4101
|
+
function readLoginScopes(payload) {
|
|
4102
|
+
if (payload === undefined) {
|
|
4103
|
+
return ['identity'];
|
|
4104
|
+
}
|
|
4105
|
+
if (!isMiniProgramRuntimeRecord(payload) || Object.keys(payload).some((key) => key !== 'scopes')) {
|
|
4106
|
+
throw createInvalidLoginPayloadError();
|
|
4107
|
+
}
|
|
4108
|
+
if (payload.scopes === undefined) {
|
|
4109
|
+
return ['identity'];
|
|
4110
|
+
}
|
|
4111
|
+
if (!Array.isArray(payload.scopes) ||
|
|
4112
|
+
payload.scopes.some((scope) => typeof scope !== 'string' || !VALID_LOGIN_SCOPES.has(scope)) ||
|
|
4113
|
+
new Set(payload.scopes).size !== payload.scopes.length) {
|
|
4114
|
+
throw createInvalidLoginPayloadError();
|
|
4115
|
+
}
|
|
4116
|
+
return [...payload.scopes];
|
|
4117
|
+
}
|
|
4118
|
+
function assertAuthLoginResult(result) {
|
|
4119
|
+
if (!isMiniProgramRuntimeRecord(result) ||
|
|
4120
|
+
typeof result.code !== 'string' ||
|
|
4121
|
+
!result.code ||
|
|
4122
|
+
typeof result.expiresIn !== 'number' ||
|
|
4123
|
+
!Number.isInteger(result.expiresIn) ||
|
|
4124
|
+
result.expiresIn <= 0 ||
|
|
4125
|
+
!Array.isArray(result.scopes) ||
|
|
4126
|
+
!result.scopes.includes('identity') ||
|
|
4127
|
+
result.scopes.some((scope) => typeof scope !== 'string') ||
|
|
4128
|
+
result.scopes.some((scope) => !VALID_LOGIN_SCOPES.has(scope)) ||
|
|
4129
|
+
new Set(result.scopes).size !== result.scopes.length) {
|
|
4130
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'Host auth.login 返回无效');
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
function createInvalidLoginPayloadError() {
|
|
4134
|
+
return createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'auth.login 参数不合法');
|
|
4135
|
+
}
|
|
4136
|
+
|
|
4061
4137
|
const GAME_DETAIL_GAME_TYPES = ['pc', 'console', 'mobile'];
|
|
4062
4138
|
const GAME_DETAIL_PAGES = ['game', 'wiki'];
|
|
4063
4139
|
function toOpenAppPagePlatformOptions(payload) {
|
|
@@ -4619,6 +4695,9 @@ function assertNetworkRequestUrl(requestUrl) {
|
|
|
4619
4695
|
if (url.username || url.password) {
|
|
4620
4696
|
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'network.request url 不允许包含用户信息');
|
|
4621
4697
|
}
|
|
4698
|
+
if (isProtectedMiniProgramNetworkPath(url.pathname)) {
|
|
4699
|
+
throw createMiniProgramRuntimeBridgeError('PERMISSION_DENIED', 'network.request 不允许访问 Runtime 后端接口');
|
|
4700
|
+
}
|
|
4622
4701
|
}
|
|
4623
4702
|
function readNetworkRequestHeaders(value) {
|
|
4624
4703
|
const headers = readStringRecord(value, 'network.request headers 必须是 Record<string, string>');
|
|
@@ -5091,31 +5170,45 @@ function hashString(value) {
|
|
|
5091
5170
|
return (hash >>> 0).toString(36);
|
|
5092
5171
|
}
|
|
5093
5172
|
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
'switch',
|
|
5100
|
-
'pc_hardware',
|
|
5101
|
-
'mobile',
|
|
5102
|
-
];
|
|
5103
|
-
const SUPPORTED_PLATFORM_ACCOUNT_TYPE_SET = new Set(SUPPORTED_PLATFORM_ACCOUNT_TYPES);
|
|
5104
|
-
function isPlatformAccountType(value) {
|
|
5105
|
-
return typeof value === 'string' && SUPPORTED_PLATFORM_ACCOUNT_TYPE_SET.has(value);
|
|
5173
|
+
async function getMiniProgramRuntimeUserInfo(platformAdapter, identity) {
|
|
5174
|
+
const miniProgramId = identity?.miniProgramId?.trim();
|
|
5175
|
+
return miniProgramId
|
|
5176
|
+
? platformAdapter.user.getUserInfo({ miniProgramId })
|
|
5177
|
+
: platformAdapter.user.getUserInfo();
|
|
5106
5178
|
}
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5179
|
+
|
|
5180
|
+
async function getMiniProgramRuntimeLocalIdentity(payload, platformAdapter, identity) {
|
|
5181
|
+
if (payload !== undefined) {
|
|
5182
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'user.getLocalIdentity 不接收入参');
|
|
5110
5183
|
}
|
|
5111
|
-
const
|
|
5112
|
-
if (
|
|
5113
|
-
throw createMiniProgramRuntimeBridgeError('
|
|
5184
|
+
const miniProgramId = identity?.miniProgramId?.trim();
|
|
5185
|
+
if (!miniProgramId) {
|
|
5186
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_STATE', 'user.getLocalIdentity 缺少可信小程序身份');
|
|
5114
5187
|
}
|
|
5115
|
-
|
|
5116
|
-
|
|
5188
|
+
const result = await platformAdapter.user.getLocalIdentity({
|
|
5189
|
+
miniProgramId,
|
|
5190
|
+
trustedUserGesture: Boolean(await platformAdapter.auth.hasTrustedUserGesture?.()),
|
|
5191
|
+
});
|
|
5192
|
+
if (!isMiniProgramRuntimeRecord(result)
|
|
5193
|
+
|| typeof result.appUserId !== 'string'
|
|
5194
|
+
|| !result.appUserId.trim()) {
|
|
5195
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'Host user.getLocalIdentity 返回无效');
|
|
5117
5196
|
}
|
|
5118
|
-
return
|
|
5197
|
+
return { appUserId: result.appUserId };
|
|
5198
|
+
}
|
|
5199
|
+
|
|
5200
|
+
async function revokeMiniProgramRuntimeAuthorization(payload, platformAdapter, identity) {
|
|
5201
|
+
if (payload !== undefined) {
|
|
5202
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'user.revokeAuthorization 不接收入参');
|
|
5203
|
+
}
|
|
5204
|
+
const miniProgramId = identity?.miniProgramId?.trim();
|
|
5205
|
+
if (!miniProgramId) {
|
|
5206
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_STATE', 'user.revokeAuthorization 缺少可信小程序身份');
|
|
5207
|
+
}
|
|
5208
|
+
await platformAdapter.user.revokeAuthorization({
|
|
5209
|
+
miniProgramId,
|
|
5210
|
+
trustedUserGesture: Boolean(await platformAdapter.auth.hasTrustedUserGesture?.()),
|
|
5211
|
+
});
|
|
5119
5212
|
}
|
|
5120
5213
|
|
|
5121
5214
|
const DEFAULT_LIMIT = 20;
|
|
@@ -5136,14 +5229,7 @@ function readSteamGameListRequest(payload) {
|
|
|
5136
5229
|
if (!isMiniProgramRuntimeRecord(payload)) {
|
|
5137
5230
|
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'user.getSteamGameList 参数必须是对象');
|
|
5138
5231
|
}
|
|
5139
|
-
const invalidKeys = Object.keys(payload).filter(key => ![
|
|
5140
|
-
'steamId',
|
|
5141
|
-
'limit',
|
|
5142
|
-
'offset',
|
|
5143
|
-
'sort',
|
|
5144
|
-
'q',
|
|
5145
|
-
'includeHidden',
|
|
5146
|
-
].includes(key));
|
|
5232
|
+
const invalidKeys = Object.keys(payload).filter((key) => !['steamId', 'limit', 'offset', 'sort', 'q', 'includeHidden'].includes(key));
|
|
5147
5233
|
if (invalidKeys.length > 0) {
|
|
5148
5234
|
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `user.getSteamGameList 不支持字段: ${invalidKeys.join(', ')}`);
|
|
5149
5235
|
}
|
|
@@ -5153,7 +5239,7 @@ function readSteamGameListRequest(payload) {
|
|
|
5153
5239
|
offset: readOffset(payload.offset),
|
|
5154
5240
|
sort: readSort(payload.sort),
|
|
5155
5241
|
...readOptionalQuery(payload.q),
|
|
5156
|
-
includeHidden:
|
|
5242
|
+
includeHidden: readIncludeHidden(payload.includeHidden),
|
|
5157
5243
|
};
|
|
5158
5244
|
}
|
|
5159
5245
|
async function getMiniProgramRuntimeSteamGameList(payload, platformAdapter, home) {
|
|
@@ -5165,7 +5251,7 @@ async function getMiniProgramRuntimeSteamGameList(payload, platformAdapter, home
|
|
|
5165
5251
|
data: null,
|
|
5166
5252
|
};
|
|
5167
5253
|
}
|
|
5168
|
-
const steamInfo = readRecord
|
|
5254
|
+
const steamInfo = readRecord(home.steam_id_info);
|
|
5169
5255
|
if (Object.keys(steamInfo).length === 0) {
|
|
5170
5256
|
return {
|
|
5171
5257
|
isLogin: true,
|
|
@@ -5187,8 +5273,7 @@ function requestSteamGameListEnvelope(platformAdapter, request) {
|
|
|
5187
5273
|
if (!platformAdapter.user.getCurrentUserSteamGameListEnvelope) {
|
|
5188
5274
|
throw createMiniProgramRuntimeBridgeError('UNSUPPORTED', '当前平台不支持读取 Steam 游戏库');
|
|
5189
5275
|
}
|
|
5190
|
-
return platformAdapter.user.getCurrentUserSteamGameListEnvelope(request)
|
|
5191
|
-
.catch((error) => {
|
|
5276
|
+
return platformAdapter.user.getCurrentUserSteamGameListEnvelope(request).catch((error) => {
|
|
5192
5277
|
throw createMiniProgramRuntimeBridgeError('REQUEST_FAILED', error instanceof Error ? error.message : 'Steam 游戏库请求失败', error);
|
|
5193
5278
|
});
|
|
5194
5279
|
}
|
|
@@ -5198,13 +5283,13 @@ function mapSteamGameListData(envelope, request) {
|
|
|
5198
5283
|
}
|
|
5199
5284
|
const data = envelope;
|
|
5200
5285
|
if (data.status !== 'ok') {
|
|
5201
|
-
throw createMiniProgramRuntimeBridgeError('REQUEST_FAILED', readString
|
|
5286
|
+
throw createMiniProgramRuntimeBridgeError('REQUEST_FAILED', readString(data.msg) || 'Steam 游戏库请求失败', data);
|
|
5202
5287
|
}
|
|
5203
5288
|
if (!isMiniProgramRuntimeRecord(data.result)) {
|
|
5204
5289
|
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'Steam 游戏库 result 必须是对象');
|
|
5205
5290
|
}
|
|
5206
5291
|
const result = data.result;
|
|
5207
|
-
const gameList = readArray
|
|
5292
|
+
const gameList = readArray(result.game_list)
|
|
5208
5293
|
.map(mapSteamGameListItem)
|
|
5209
5294
|
.filter((item) => Boolean(item));
|
|
5210
5295
|
const total = readOptionalNumber(result.total);
|
|
@@ -5225,10 +5310,10 @@ function mapSteamGameListData(envelope, request) {
|
|
|
5225
5310
|
return response;
|
|
5226
5311
|
}
|
|
5227
5312
|
function mapSteamGameListItem(value) {
|
|
5228
|
-
const source = readRecord
|
|
5313
|
+
const source = readRecord(value);
|
|
5229
5314
|
const appid = readOptionalNumber(source.appid);
|
|
5230
5315
|
const steamAppid = readOptionalNumber(source.steam_appid) ?? appid;
|
|
5231
|
-
const name = readString
|
|
5316
|
+
const name = readString(source.name);
|
|
5232
5317
|
if (appid === undefined || steamAppid === undefined || !name) {
|
|
5233
5318
|
return undefined;
|
|
5234
5319
|
}
|
|
@@ -5237,21 +5322,21 @@ function mapSteamGameListItem(value) {
|
|
|
5237
5322
|
steamAppid,
|
|
5238
5323
|
name,
|
|
5239
5324
|
};
|
|
5240
|
-
copyStringField
|
|
5241
|
-
copyStringField
|
|
5242
|
-
copyStringField
|
|
5243
|
-
copyStringField
|
|
5325
|
+
copyStringField(result, 'image', source.image);
|
|
5326
|
+
copyStringField(result, 'icon', source.appicon ?? source.icon);
|
|
5327
|
+
copyStringField(result, 'background', source.background);
|
|
5328
|
+
copyStringField(result, 'verticalImage', source.vertical_head_image);
|
|
5244
5329
|
copyNumberField(result, 'playtimeForever', source.playtime_forever);
|
|
5245
5330
|
copyNumberField(result, 'playtime2Weeks', source.playtime_2weeks);
|
|
5246
5331
|
copyNumberField(result, 'playtimePercent', source.playtime_percent);
|
|
5247
5332
|
copyNumberField(result, 'achievedCount', source.achieved_count);
|
|
5248
5333
|
copyNumberField(result, 'achievementCount', source.achivement_count ?? source.achievement_count);
|
|
5249
5334
|
copyBooleanField(result, 'isCleared', source.is_cleared);
|
|
5250
|
-
copyStringField
|
|
5335
|
+
copyStringField(result, 'score', source.score);
|
|
5251
5336
|
copyBooleanField(result, 'isFree', source.is_free);
|
|
5252
5337
|
copyBooleanField(result, 'notOwned', source.not_owned);
|
|
5253
5338
|
copyBooleanField(result, 'hidden', source.hidden ?? source.is_hidden);
|
|
5254
|
-
copyStringField
|
|
5339
|
+
copyStringField(result, 'onlinePlayer', source.online_player);
|
|
5255
5340
|
copyStringArray(result, 'tags', source.external_tags ?? source.tags);
|
|
5256
5341
|
copyStringArray(result, 'genres', source.genres);
|
|
5257
5342
|
const price = mapSteamGamePrice(source.price);
|
|
@@ -5261,22 +5346,27 @@ function mapSteamGameListItem(value) {
|
|
|
5261
5346
|
return result;
|
|
5262
5347
|
}
|
|
5263
5348
|
function mapSteamGamePrice(value) {
|
|
5264
|
-
const source = readRecord
|
|
5349
|
+
const source = readRecord(value);
|
|
5265
5350
|
const result = {};
|
|
5266
|
-
copyStringField
|
|
5267
|
-
copyStringField
|
|
5351
|
+
copyStringField(result, 'current', source.current);
|
|
5352
|
+
copyStringField(result, 'initial', source.initial);
|
|
5268
5353
|
copyNumberField(result, 'discount', source.discount);
|
|
5269
5354
|
copyNumberField(result, 'lowestPrice', source.lowest_price);
|
|
5270
|
-
copyStringField
|
|
5355
|
+
copyStringField(result, 'deadlineDate', source.deadline_date);
|
|
5271
5356
|
copyNumberField(result, 'deadlineTimestamp', source.deadline_timestamp);
|
|
5272
5357
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
5273
5358
|
}
|
|
5274
5359
|
function readOptionalSteamId(value) {
|
|
5275
|
-
|
|
5276
|
-
|
|
5360
|
+
if (value === undefined) {
|
|
5361
|
+
return {};
|
|
5362
|
+
}
|
|
5363
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
5364
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'user.getSteamGameList steamId 必须是非空字符串');
|
|
5365
|
+
}
|
|
5366
|
+
return { steamId: value.trim() };
|
|
5277
5367
|
}
|
|
5278
5368
|
function readOptionalQuery(value) {
|
|
5279
|
-
const q = readString
|
|
5369
|
+
const q = readString(value);
|
|
5280
5370
|
return q ? { q: q.slice(0, MAX_QUERY_LENGTH) } : {};
|
|
5281
5371
|
}
|
|
5282
5372
|
function readLimit(value) {
|
|
@@ -5314,8 +5404,8 @@ function readInteger(value) {
|
|
|
5314
5404
|
}
|
|
5315
5405
|
return undefined;
|
|
5316
5406
|
}
|
|
5317
|
-
function copyStringField
|
|
5318
|
-
const stringValue = readString
|
|
5407
|
+
function copyStringField(target, key, value) {
|
|
5408
|
+
const stringValue = readString(value);
|
|
5319
5409
|
if (stringValue) {
|
|
5320
5410
|
Object.assign(target, { [key]: stringValue });
|
|
5321
5411
|
}
|
|
@@ -5333,20 +5423,18 @@ function copyBooleanField(target, key, value) {
|
|
|
5333
5423
|
}
|
|
5334
5424
|
}
|
|
5335
5425
|
function copyStringArray(target, key, value) {
|
|
5336
|
-
const items = readArray
|
|
5337
|
-
.map(readString$1)
|
|
5338
|
-
.filter(Boolean);
|
|
5426
|
+
const items = readArray(value).map(readString).filter(Boolean);
|
|
5339
5427
|
if (items.length > 0) {
|
|
5340
5428
|
Object.assign(target, { [key]: items });
|
|
5341
5429
|
}
|
|
5342
5430
|
}
|
|
5343
|
-
function readRecord
|
|
5431
|
+
function readRecord(value) {
|
|
5344
5432
|
return isMiniProgramRuntimeRecord(value) ? value : {};
|
|
5345
5433
|
}
|
|
5346
|
-
function readArray
|
|
5434
|
+
function readArray(value) {
|
|
5347
5435
|
return Array.isArray(value) ? value : [];
|
|
5348
5436
|
}
|
|
5349
|
-
function readString
|
|
5437
|
+
function readString(value) {
|
|
5350
5438
|
if (typeof value === 'string') {
|
|
5351
5439
|
return value.trim() ? value.trim() : '';
|
|
5352
5440
|
}
|
|
@@ -5365,376 +5453,16 @@ function readOptionalNumber(value) {
|
|
|
5365
5453
|
}
|
|
5366
5454
|
return undefined;
|
|
5367
5455
|
}
|
|
5368
|
-
function
|
|
5369
|
-
|
|
5370
|
-
}
|
|
5371
|
-
function readOptionalBoolean(value) {
|
|
5372
|
-
if (value === undefined || value === null || value === '') {
|
|
5373
|
-
return undefined;
|
|
5374
|
-
}
|
|
5375
|
-
if (value === true || value === 1 || value === '1') {
|
|
5376
|
-
return true;
|
|
5377
|
-
}
|
|
5378
|
-
if (value === false || value === 0 || value === '0') {
|
|
5456
|
+
function readIncludeHidden(value) {
|
|
5457
|
+
if (value === undefined) {
|
|
5379
5458
|
return false;
|
|
5380
5459
|
}
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
const PLATFORM_SOURCE_KEY_BY_TYPE = {
|
|
5385
|
-
steam: 'steam_id_info',
|
|
5386
|
-
epic: 'epic_account_info',
|
|
5387
|
-
xbox: 'xbox_account_info',
|
|
5388
|
-
psn: 'psn_account_info',
|
|
5389
|
-
switch: 'switch_account_info',
|
|
5390
|
-
pc_hardware: 'hardware_info',
|
|
5391
|
-
mobile: 'mobile_account_info',
|
|
5392
|
-
};
|
|
5393
|
-
const PLATFORM_HIDDEN_KEY_BY_TYPE = {
|
|
5394
|
-
steam: ['steam_is_hidden', 'is_steam_hidden'],
|
|
5395
|
-
epic: ['epic_is_hidden', 'is_epic_hidden'],
|
|
5396
|
-
xbox: ['xbox_is_hidden', 'is_xbox_hidden'],
|
|
5397
|
-
psn: ['psn_is_hidden', 'is_psn_hidden'],
|
|
5398
|
-
switch: ['switch_is_hidden', 'is_switch_hidden'],
|
|
5399
|
-
pc_hardware: ['hardware_is_hidden', 'pc_hardware_is_hidden', 'is_hardware_hidden'],
|
|
5400
|
-
mobile: ['mobile_is_hidden', 'is_mobile_hidden'],
|
|
5401
|
-
};
|
|
5402
|
-
function mapCurrentUserDetail(home) {
|
|
5403
|
-
const accountDetail = readRecord(home.account_detail);
|
|
5404
|
-
const result = {
|
|
5405
|
-
heybox_id: readString(accountDetail.userid) || '',
|
|
5406
|
-
nickname: readString(accountDetail.username) || '',
|
|
5407
|
-
avatar: readString(accountDetail.avartar) || readString(accountDetail.avatar) || '',
|
|
5408
|
-
};
|
|
5409
|
-
copyStringFields(result, accountDetail, [
|
|
5410
|
-
'sex',
|
|
5411
|
-
'signature',
|
|
5412
|
-
'ip_location',
|
|
5413
|
-
'friends_count',
|
|
5414
|
-
]);
|
|
5415
|
-
copyRecord(result, 'level_info', mapCurrentUserLevelInfo(accountDetail.level_info));
|
|
5416
|
-
copyArray(result, 'medals', mapCurrentUserMedals(accountDetail.bbs_medal ?? accountDetail.medal ?? accountDetail.medals));
|
|
5417
|
-
copyRecord(result, 'avatar_decoration', mapCurrentUserAvatarDecoration(accountDetail.avatar_decoration));
|
|
5418
|
-
copyRecord(result, 'avatar_config', mapCurrentUserAvatarConfig(accountDetail.avatar_config));
|
|
5419
|
-
copyRecord(result, 'bbs_info', mapCurrentUserBbsInfo(accountDetail.bbs_info));
|
|
5420
|
-
return result;
|
|
5421
|
-
}
|
|
5422
|
-
function mapCurrentUserProfile(home) {
|
|
5423
|
-
const profile = readRecord(home.profile);
|
|
5424
|
-
const result = {
|
|
5425
|
-
heybox_id: readString(profile.heybox_id) || readString(profile.userid) || '',
|
|
5426
|
-
};
|
|
5427
|
-
copyStringFields(result, profile, [
|
|
5428
|
-
'nickname',
|
|
5429
|
-
'avatar',
|
|
5430
|
-
'gender',
|
|
5431
|
-
'birthday',
|
|
5432
|
-
'email',
|
|
5433
|
-
'education',
|
|
5434
|
-
'career',
|
|
5435
|
-
'signature',
|
|
5436
|
-
]);
|
|
5437
|
-
return result;
|
|
5438
|
-
}
|
|
5439
|
-
function mapPlatformAccountOverview(home) {
|
|
5440
|
-
return {
|
|
5441
|
-
platforms: SUPPORTED_PLATFORM_ACCOUNT_TYPES.map(platform => ({
|
|
5442
|
-
platform,
|
|
5443
|
-
is_bound: isPlatformBound(home, platform),
|
|
5444
|
-
is_hidden: readPlatformHidden(home, platform),
|
|
5445
|
-
})),
|
|
5446
|
-
display_order: readPlatformDisplayOrder(home.platform_list),
|
|
5447
|
-
};
|
|
5448
|
-
}
|
|
5449
|
-
function mapPlatformAccountResult(home, platform) {
|
|
5450
|
-
const isLogin = true;
|
|
5451
|
-
const isHidden = readPlatformHidden(home, platform);
|
|
5452
|
-
const accountInfo = mapPlatformAccountInfo(home, platform);
|
|
5453
|
-
const isBound = isPlatformInfoBound(accountInfo);
|
|
5454
|
-
return {
|
|
5455
|
-
isLogin,
|
|
5456
|
-
platform,
|
|
5457
|
-
is_bound: isBound,
|
|
5458
|
-
is_hidden: isHidden,
|
|
5459
|
-
account_info: isBound ? accountInfo : null,
|
|
5460
|
-
};
|
|
5461
|
-
}
|
|
5462
|
-
function createUnauthenticatedPlatformAccountResult(platform) {
|
|
5463
|
-
return {
|
|
5464
|
-
isLogin: false,
|
|
5465
|
-
platform,
|
|
5466
|
-
is_bound: false,
|
|
5467
|
-
is_hidden: false,
|
|
5468
|
-
account_info: null,
|
|
5469
|
-
};
|
|
5470
|
-
}
|
|
5471
|
-
function mapPlatformAccountInfo(home, platform) {
|
|
5472
|
-
const source = readRecord(home[PLATFORM_SOURCE_KEY_BY_TYPE[platform]]);
|
|
5473
|
-
switch (platform) {
|
|
5474
|
-
case 'steam':
|
|
5475
|
-
return mapSteamPlatformAccount(source);
|
|
5476
|
-
case 'epic':
|
|
5477
|
-
return mapEpicPlatformAccount(source);
|
|
5478
|
-
case 'xbox':
|
|
5479
|
-
return mapXboxPlatformAccount(source);
|
|
5480
|
-
case 'psn':
|
|
5481
|
-
return mapPsnPlatformAccount(source);
|
|
5482
|
-
case 'switch':
|
|
5483
|
-
return mapSwitchPlatformAccount(source);
|
|
5484
|
-
case 'pc_hardware':
|
|
5485
|
-
return mapPcHardwareAccount(source);
|
|
5486
|
-
case 'mobile':
|
|
5487
|
-
return mapMobilePlatformAccount(source);
|
|
5488
|
-
default:
|
|
5489
|
-
return {};
|
|
5490
|
-
}
|
|
5491
|
-
}
|
|
5492
|
-
function mapSteamPlatformAccount(source) {
|
|
5493
|
-
const result = mapBasePlatformAccountInfo(source);
|
|
5494
|
-
const steamid = readString(source.steamid);
|
|
5495
|
-
if (steamid) {
|
|
5496
|
-
result.steamid = steamid;
|
|
5497
|
-
}
|
|
5498
|
-
copyStringFields(result, source, [
|
|
5499
|
-
'short_id',
|
|
5500
|
-
'level',
|
|
5501
|
-
'city',
|
|
5502
|
-
'province',
|
|
5503
|
-
'country',
|
|
5504
|
-
'country_code',
|
|
5505
|
-
'country_flag',
|
|
5506
|
-
'avatar_frame',
|
|
5507
|
-
'head_image',
|
|
5508
|
-
'head_video',
|
|
5509
|
-
'update_time',
|
|
5510
|
-
'account_year',
|
|
5511
|
-
'individuality_signature',
|
|
5512
|
-
'level_icon',
|
|
5513
|
-
'total_badge_count',
|
|
5514
|
-
'total_dlcs_count',
|
|
5515
|
-
'total_game_count',
|
|
5516
|
-
'total_game_price',
|
|
5517
|
-
'total_player_time',
|
|
5518
|
-
]);
|
|
5519
|
-
copyOptionalBoolean(result, 'friend_code_show', source.friend_code_show);
|
|
5520
|
-
copyOptionalBoolean(result, 'inventory_open', source.inventory_open);
|
|
5521
|
-
copyOptionalBoolean(result, 'personal_information_open', source.personal_infomation_open ?? source.personal_information_open);
|
|
5522
|
-
copyOptionalBoolean(result, 'has_history', source.has_history);
|
|
5523
|
-
if (result.friend_code_show === true) {
|
|
5524
|
-
copyStringField(result, 'friend_code', source.friend_code);
|
|
5525
|
-
}
|
|
5526
|
-
return result;
|
|
5527
|
-
}
|
|
5528
|
-
function mapEpicPlatformAccount(source) {
|
|
5529
|
-
const result = mapBasePlatformAccountInfo(source);
|
|
5530
|
-
copyStringFields(result, source, ['xuid', 'head_color', 'desc']);
|
|
5531
|
-
return result;
|
|
5532
|
-
}
|
|
5533
|
-
function mapXboxPlatformAccount(source) {
|
|
5534
|
-
const result = mapBasePlatformAccountInfo(source);
|
|
5535
|
-
copyStringFields(result, source, ['xuid', 'desc']);
|
|
5536
|
-
return result;
|
|
5537
|
-
}
|
|
5538
|
-
function mapPsnPlatformAccount(source) {
|
|
5539
|
-
const result = mapBasePlatformAccountInfo(source);
|
|
5540
|
-
copyStringField(result, 'background_url', source.bg_img ?? source.background_url);
|
|
5541
|
-
copyStringFields(result, source, ['name', 'level', 'description']);
|
|
5542
|
-
copyOptionalBoolean(result, 'is_open', source.is_open);
|
|
5543
|
-
const pairStats = mapPsnPairStats(source);
|
|
5544
|
-
if (pairStats.length > 0) {
|
|
5545
|
-
result.stats = [...readArray(result.stats), ...pairStats];
|
|
5546
|
-
}
|
|
5547
|
-
return result;
|
|
5548
|
-
}
|
|
5549
|
-
function mapSwitchPlatformAccount(source) {
|
|
5550
|
-
const result = mapBasePlatformAccountInfo(source);
|
|
5551
|
-
copyStringFields(result, source, [
|
|
5552
|
-
'heybox_id',
|
|
5553
|
-
'friend_code',
|
|
5554
|
-
'friends_total',
|
|
5555
|
-
'games_total',
|
|
5556
|
-
'server_name',
|
|
5557
|
-
'type',
|
|
5558
|
-
'desc',
|
|
5559
|
-
]);
|
|
5560
|
-
return result;
|
|
5561
|
-
}
|
|
5562
|
-
function mapPcHardwareAccount(source) {
|
|
5563
|
-
const result = {};
|
|
5564
|
-
copyStringFields(result, source, ['cpu', 'gpu', 'board', 'perf_level']);
|
|
5565
|
-
return result;
|
|
5566
|
-
}
|
|
5567
|
-
function mapMobilePlatformAccount(source) {
|
|
5568
|
-
const currentDevice = mapMobileCurrentDevice(readRecord(source.current_device));
|
|
5569
|
-
return Object.keys(currentDevice).length > 0
|
|
5570
|
-
? {
|
|
5571
|
-
current_device: currentDevice,
|
|
5572
|
-
}
|
|
5573
|
-
: {};
|
|
5574
|
-
}
|
|
5575
|
-
function mapBasePlatformAccountInfo(source) {
|
|
5576
|
-
const result = {};
|
|
5577
|
-
copyStringFields(result, source, ['nickname', 'avatar']);
|
|
5578
|
-
copyStringField(result, 'background_url', source.background_url ?? source.bg_img);
|
|
5579
|
-
const stats = mapPlatformStats(source.stats ?? source.stat ?? source.cards ?? source.card_list);
|
|
5580
|
-
if (stats.length > 0) {
|
|
5581
|
-
result.stats = stats;
|
|
5582
|
-
}
|
|
5583
|
-
return result;
|
|
5584
|
-
}
|
|
5585
|
-
function mapMobileCurrentDevice(source) {
|
|
5586
|
-
const result = {};
|
|
5587
|
-
copyStringFields(result, source, ['id', 'name', 'desc', 'model', 'img_url', 'update_time']);
|
|
5588
|
-
const stats = mapPlatformStats(source.stats ?? source.stat ?? source.cards ?? source.card_list);
|
|
5589
|
-
if (stats.length > 0) {
|
|
5590
|
-
result.stats = stats;
|
|
5591
|
-
}
|
|
5592
|
-
return result;
|
|
5593
|
-
}
|
|
5594
|
-
function mapCurrentUserLevelInfo(value) {
|
|
5595
|
-
const source = readRecord(value);
|
|
5596
|
-
const result = {};
|
|
5597
|
-
copyStringFields(result, source, ['level', 'level_name', 'exp', 'next_exp', 'icon']);
|
|
5598
|
-
return result;
|
|
5599
|
-
}
|
|
5600
|
-
function mapCurrentUserMedals(value) {
|
|
5601
|
-
return readArray(value)
|
|
5602
|
-
.map(item => {
|
|
5603
|
-
const source = readRecord(item);
|
|
5604
|
-
const result = {};
|
|
5605
|
-
copyStringFields(result, source, ['id', 'name', 'desc', 'icon', 'image']);
|
|
5606
|
-
copyOptionalBoolean(result, 'is_wearing', source.is_wearing);
|
|
5607
|
-
return result;
|
|
5608
|
-
})
|
|
5609
|
-
.filter(item => Object.keys(item).length > 0);
|
|
5610
|
-
}
|
|
5611
|
-
function mapCurrentUserAvatarDecoration(value) {
|
|
5612
|
-
const source = readRecord(value);
|
|
5613
|
-
const result = {};
|
|
5614
|
-
copyStringFields(result, source, ['id', 'name', 'image', 'frame_url']);
|
|
5615
|
-
copyOptionalBoolean(result, 'is_using', source.is_using);
|
|
5616
|
-
return result;
|
|
5617
|
-
}
|
|
5618
|
-
function mapCurrentUserAvatarConfig(value) {
|
|
5619
|
-
const source = readRecord(value);
|
|
5620
|
-
const result = {};
|
|
5621
|
-
copyStringFields(result, source, ['avatar', 'frame_url', 'pendant_url']);
|
|
5622
|
-
return result;
|
|
5623
|
-
}
|
|
5624
|
-
function mapCurrentUserBbsInfo(value) {
|
|
5625
|
-
const source = readRecord(value);
|
|
5626
|
-
const result = {};
|
|
5627
|
-
copyStringFields(result, source, ['uid', 'nickname', 'avatar', 'level']);
|
|
5628
|
-
return result;
|
|
5629
|
-
}
|
|
5630
|
-
function mapPlatformStats(value) {
|
|
5631
|
-
return readArray(value)
|
|
5632
|
-
.map(item => {
|
|
5633
|
-
const source = readRecord(item);
|
|
5634
|
-
const result = {};
|
|
5635
|
-
copyStringField(result, 'key', source.key ?? source.k);
|
|
5636
|
-
copyStringField(result, 'name', source.name);
|
|
5637
|
-
copyStringField(result, 'desc', source.desc ?? source.description);
|
|
5638
|
-
copyStringField(result, 'value', source.value ?? source.timestamp);
|
|
5639
|
-
copyStringField(result, 'num', source.num);
|
|
5640
|
-
copyStringField(result, 'icon', source.icon);
|
|
5641
|
-
copyStringField(result, 'img', source.img ?? source.img_url ?? source.right_img);
|
|
5642
|
-
copyStringField(result, 'color', source.color);
|
|
5643
|
-
return result;
|
|
5644
|
-
})
|
|
5645
|
-
.filter(item => Object.keys(item).length > 0);
|
|
5646
|
-
}
|
|
5647
|
-
function mapPsnPairStats(source) {
|
|
5648
|
-
const stats = [];
|
|
5649
|
-
for (let index = 1; index <= 4; index += 1) {
|
|
5650
|
-
const name = readString(source[`key${index}`]);
|
|
5651
|
-
const value = readString(source[`value${index}`]);
|
|
5652
|
-
if (name || value) {
|
|
5653
|
-
stats.push({
|
|
5654
|
-
...(name ? { name } : {}),
|
|
5655
|
-
...(value ? { value } : {}),
|
|
5656
|
-
});
|
|
5657
|
-
}
|
|
5658
|
-
}
|
|
5659
|
-
return stats;
|
|
5660
|
-
}
|
|
5661
|
-
function isPlatformBound(home, platform) {
|
|
5662
|
-
return isPlatformInfoBound(mapPlatformAccountInfo(home, platform));
|
|
5663
|
-
}
|
|
5664
|
-
function isPlatformInfoBound(accountInfo) {
|
|
5665
|
-
return Object.keys(accountInfo).length > 0;
|
|
5666
|
-
}
|
|
5667
|
-
function readPlatformHidden(home, platform) {
|
|
5668
|
-
for (const key of PLATFORM_HIDDEN_KEY_BY_TYPE[platform]) {
|
|
5669
|
-
const value = readOptionalRuntimeBoolean(home[key]);
|
|
5670
|
-
if (value !== undefined) {
|
|
5671
|
-
return value;
|
|
5672
|
-
}
|
|
5673
|
-
}
|
|
5674
|
-
const hidden = readRecord(home.hidden);
|
|
5675
|
-
const hiddenAccounts = readRecord(home.hidden_accounts);
|
|
5676
|
-
return readOptionalRuntimeBoolean(hidden[platform]) ?? readOptionalRuntimeBoolean(hiddenAccounts[platform]) ?? false;
|
|
5677
|
-
}
|
|
5678
|
-
function readPlatformDisplayOrder(value) {
|
|
5679
|
-
return readArray(value)
|
|
5680
|
-
.map(item => {
|
|
5681
|
-
if (isPlatformAccountType(item)) {
|
|
5682
|
-
return item;
|
|
5683
|
-
}
|
|
5684
|
-
if (!isMiniProgramRuntimeRecord(item)) {
|
|
5685
|
-
return undefined;
|
|
5686
|
-
}
|
|
5687
|
-
return readPlatformFromRecord(item);
|
|
5688
|
-
})
|
|
5689
|
-
.filter((platform) => Boolean(platform));
|
|
5690
|
-
}
|
|
5691
|
-
function readPlatformFromRecord(value) {
|
|
5692
|
-
const platform = value.platform ?? value.type ?? value.key ?? value.name;
|
|
5693
|
-
return isPlatformAccountType(platform) ? platform : undefined;
|
|
5694
|
-
}
|
|
5695
|
-
function readRecord(value) {
|
|
5696
|
-
return isMiniProgramRuntimeRecord(value) ? value : {};
|
|
5697
|
-
}
|
|
5698
|
-
function readArray(value) {
|
|
5699
|
-
return Array.isArray(value) ? value : [];
|
|
5700
|
-
}
|
|
5701
|
-
function copyRecord(target, key, value) {
|
|
5702
|
-
if (Object.keys(value).length > 0) {
|
|
5703
|
-
target[key] = value;
|
|
5704
|
-
}
|
|
5705
|
-
}
|
|
5706
|
-
function copyArray(target, key, value) {
|
|
5707
|
-
if (value.length > 0) {
|
|
5708
|
-
target[key] = value;
|
|
5709
|
-
}
|
|
5710
|
-
}
|
|
5711
|
-
function copyStringFields(target, source, keys) {
|
|
5712
|
-
for (const key of keys) {
|
|
5713
|
-
copyStringField(target, key, source[key]);
|
|
5714
|
-
}
|
|
5715
|
-
}
|
|
5716
|
-
function copyStringField(target, key, value) {
|
|
5717
|
-
const stringValue = readString(value);
|
|
5718
|
-
if (stringValue) {
|
|
5719
|
-
target[key] = stringValue;
|
|
5720
|
-
}
|
|
5721
|
-
}
|
|
5722
|
-
function copyOptionalBoolean(target, key, value) {
|
|
5723
|
-
const booleanValue = readOptionalRuntimeBoolean(value);
|
|
5724
|
-
if (booleanValue !== undefined) {
|
|
5725
|
-
target[key] = booleanValue;
|
|
5726
|
-
}
|
|
5727
|
-
}
|
|
5728
|
-
function readString(value) {
|
|
5729
|
-
if (typeof value === 'string') {
|
|
5730
|
-
return value.trim() ? value : '';
|
|
5731
|
-
}
|
|
5732
|
-
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
5733
|
-
return String(value);
|
|
5460
|
+
if (typeof value !== 'boolean') {
|
|
5461
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'user.getSteamGameList includeHidden 必须是 boolean');
|
|
5734
5462
|
}
|
|
5735
|
-
return
|
|
5463
|
+
return value;
|
|
5736
5464
|
}
|
|
5737
|
-
function
|
|
5465
|
+
function readOptionalBoolean(value) {
|
|
5738
5466
|
if (value === undefined || value === null || value === '') {
|
|
5739
5467
|
return undefined;
|
|
5740
5468
|
}
|
|
@@ -5765,27 +5493,14 @@ function isNavigationBarForegroundStyle(value) {
|
|
|
5765
5493
|
return value === 'light' || value === 'dark';
|
|
5766
5494
|
}
|
|
5767
5495
|
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
function isOfficialMiniProgramNetworkUrl(value) {
|
|
5776
|
-
try {
|
|
5777
|
-
const url = new URL(value);
|
|
5778
|
-
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
5779
|
-
return false;
|
|
5780
|
-
}
|
|
5781
|
-
const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
|
|
5782
|
-
return OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS.some(rootDomain => hostname === rootDomain || hostname.endsWith(`.${rootDomain}`));
|
|
5783
|
-
}
|
|
5784
|
-
catch {
|
|
5785
|
-
return false;
|
|
5786
|
-
}
|
|
5496
|
+
/**
|
|
5497
|
+
* 当前小程序是否开通了 `network.request`。
|
|
5498
|
+
* 公开详情关网时只下发合法空 `entries`,没有 enabled 条目视为未开通。
|
|
5499
|
+
*/
|
|
5500
|
+
function hasMiniProgramNetworkRequestPermission(snapshot) {
|
|
5501
|
+
const parsed = parseMiniProgramRuntimePermissions(snapshot);
|
|
5502
|
+
return parsed.valid && parsed.permissions['network.request']?.status === 'enabled';
|
|
5787
5503
|
}
|
|
5788
|
-
|
|
5789
5504
|
/** 创建只依赖启动快照的纯权限 evaluator,供正式 Runtime 与 Mock Host 共用。 */
|
|
5790
5505
|
function createMiniProgramRuntimePermissionEvaluator(snapshot) {
|
|
5791
5506
|
const parsed = parseMiniProgramRuntimePermissions(snapshot);
|
|
@@ -5823,13 +5538,11 @@ const MOCK_DEFAULT_LEADERBOARD_KEY = 'default';
|
|
|
5823
5538
|
const MOCK_LEADERBOARD_CACHE_MAX_ENTRIES = 500;
|
|
5824
5539
|
const MOCK_LEADERBOARD_CURRENT_ENTRY_RANK_COUNT_LIMIT = 5000;
|
|
5825
5540
|
const MINI_PROGRAM_MOCK_RUNTIME_METHODS = MINI_PROGRAM_PROTOCOL_CAPABILITIES.map((capability) => capability.method);
|
|
5826
|
-
const
|
|
5827
|
-
[AUTH_LOGIN_METHOD]: (runtime) => runtime.login(),
|
|
5541
|
+
const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLER_MAP = {
|
|
5542
|
+
[AUTH_LOGIN_METHOD]: (runtime, payload) => runtime.login(payload),
|
|
5828
5543
|
[USER_GET_INFO_METHOD]: (runtime) => runtime.getUserInfo(),
|
|
5829
|
-
[
|
|
5830
|
-
[
|
|
5831
|
-
[USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD]: (runtime) => runtime.getPlatformAccountOverview(),
|
|
5832
|
-
[USER_GET_PLATFORM_ACCOUNT_INFO_METHOD]: (runtime, payload) => runtime.getPlatformAccountInfo(payload),
|
|
5544
|
+
[USER_GET_LOCAL_IDENTITY_METHOD]: (runtime, payload) => runtime.getLocalIdentity(payload),
|
|
5545
|
+
[USER_REVOKE_AUTHORIZATION_METHOD]: (runtime, payload) => runtime.revokeAuthorization(payload),
|
|
5833
5546
|
[USER_GET_STEAM_GAME_LIST_METHOD]: (runtime, payload) => runtime.getSteamGameList(payload),
|
|
5834
5547
|
[SHARE_SHOW_SHARE_MENU_METHOD]: (runtime, payload) => runtime.showShareMenu(payload),
|
|
5835
5548
|
[SHARE_SCREENSHOT_METHOD]: (runtime, payload) => runtime.shareScreenshot(payload),
|
|
@@ -5852,6 +5565,7 @@ const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS = {
|
|
|
5852
5565
|
[NAVIGATION_RELOAD_METHOD]: (runtime, payload) => runtime.reload(payload),
|
|
5853
5566
|
[NAVIGATION_OPEN_APP_PAGE_METHOD]: (runtime, payload) => runtime.openAppPage(payload),
|
|
5854
5567
|
};
|
|
5568
|
+
const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS = Object.fromEntries(MINI_PROGRAM_MOCK_RUNTIME_METHODS.map((method) => [method, MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLER_MAP[method]]));
|
|
5855
5569
|
function createBrowserMockRuntimePlatformAdapter(options) {
|
|
5856
5570
|
const leaderboards = createMockLeaderboardBoards(options.leaderboards);
|
|
5857
5571
|
return {
|
|
@@ -5870,16 +5584,56 @@ function createBrowserMockRuntimePlatformAdapter(options) {
|
|
|
5870
5584
|
},
|
|
5871
5585
|
auth: {
|
|
5872
5586
|
async getCurrentUserId() {
|
|
5873
|
-
|
|
5587
|
+
const user = options.getCurrentUser();
|
|
5588
|
+
return user?.app_user_id;
|
|
5874
5589
|
},
|
|
5875
|
-
|
|
5876
|
-
|
|
5590
|
+
hasTrustedUserGesture: options.hasTrustedUserGesture,
|
|
5591
|
+
async login(request) {
|
|
5592
|
+
if (!options.getCurrentUser()) {
|
|
5593
|
+
if (!request.trustedUserGesture) {
|
|
5594
|
+
throw createMockBridgeError('USER_GESTURE_REQUIRED', 'Mock 登录需要可信用户手势');
|
|
5595
|
+
}
|
|
5596
|
+
await options.login();
|
|
5597
|
+
}
|
|
5598
|
+
return {
|
|
5599
|
+
code: options.authorizationCode || `mock-code-${request.miniProgramId || 'local'}`,
|
|
5600
|
+
expiresIn: 300,
|
|
5601
|
+
scopes: Array.from(new Set(['identity', ...request.scopes])),
|
|
5602
|
+
};
|
|
5877
5603
|
},
|
|
5878
5604
|
},
|
|
5879
5605
|
user: {
|
|
5606
|
+
async getUserInfo() {
|
|
5607
|
+
const currentUser = options.getCurrentUser();
|
|
5608
|
+
const appUserId = currentUser?.app_user_id;
|
|
5609
|
+
return appUserId
|
|
5610
|
+
? {
|
|
5611
|
+
isHeyboxAppLoggedIn: true,
|
|
5612
|
+
authorization: { identity: 'granted', profile: 'not_requested' },
|
|
5613
|
+
userInfo: {
|
|
5614
|
+
app_user_id: appUserId,
|
|
5615
|
+
profile: { nickname: currentUser?.nickname || '', avatar: currentUser?.avatar || '' },
|
|
5616
|
+
},
|
|
5617
|
+
}
|
|
5618
|
+
: { isHeyboxAppLoggedIn: false, authorization: null, userInfo: null };
|
|
5619
|
+
},
|
|
5620
|
+
async getLocalIdentity() {
|
|
5621
|
+
const appUserId = options.getCurrentUser()?.app_user_id;
|
|
5622
|
+
if (!appUserId) {
|
|
5623
|
+
throw createMockBridgeError('NOT_LOGGED_IN', '当前 Mock 用户未登录');
|
|
5624
|
+
}
|
|
5625
|
+
return { appUserId };
|
|
5626
|
+
},
|
|
5627
|
+
async revokeAuthorization(request) {
|
|
5628
|
+
if (!request.trustedUserGesture) {
|
|
5629
|
+
throw createMockBridgeError('USER_GESTURE_REQUIRED', 'Mock 撤销授权需要可信用户手势');
|
|
5630
|
+
}
|
|
5631
|
+
await options.revokeAuthorization?.();
|
|
5632
|
+
},
|
|
5880
5633
|
async getUserBasicInfo(userId) {
|
|
5881
5634
|
const currentUser = options.getCurrentUser();
|
|
5882
|
-
|
|
5635
|
+
const appUserId = currentUser?.app_user_id || currentUser?.heybox_id;
|
|
5636
|
+
if (!currentUser || appUserId !== userId) {
|
|
5883
5637
|
return undefined;
|
|
5884
5638
|
}
|
|
5885
5639
|
return {
|
|
@@ -5887,9 +5641,6 @@ function createBrowserMockRuntimePlatformAdapter(options) {
|
|
|
5887
5641
|
avatar: currentUser.avatar,
|
|
5888
5642
|
};
|
|
5889
5643
|
},
|
|
5890
|
-
async getCurrentUserSteamGameListEnvelope(request) {
|
|
5891
|
-
return createMockSteamGameListEnvelope(request.limit, request.offset);
|
|
5892
|
-
},
|
|
5893
5644
|
},
|
|
5894
5645
|
share: {
|
|
5895
5646
|
async showShareMenu(params) {
|
|
@@ -6013,7 +5764,11 @@ class MiniProgramMockRuntime {
|
|
|
6013
5764
|
this.toasts = [];
|
|
6014
5765
|
this.vibrates = [];
|
|
6015
5766
|
this.openAppPages = [];
|
|
6016
|
-
|
|
5767
|
+
const runtimePermissions = resolveMockRuntimePermissions(options);
|
|
5768
|
+
this.evaluateRuntimePermission = createMiniProgramRuntimePermissionEvaluator(runtimePermissions);
|
|
5769
|
+
this.authLoginEnabled = isMockAuthLoginEnabled(runtimePermissions);
|
|
5770
|
+
this.hostUserApiEnabled = isMockHostUserApiEnabled(runtimePermissions);
|
|
5771
|
+
this.authorizationMutationEnabled = isMockAuthorizationMutationEnabled(runtimePermissions);
|
|
6017
5772
|
this.leaderboardHandlers = createMiniProgramRuntimeLeaderboardHandlers({
|
|
6018
5773
|
identity: options.identity,
|
|
6019
5774
|
platformAdapter: adapter,
|
|
@@ -6026,7 +5781,7 @@ class MiniProgramMockRuntime {
|
|
|
6026
5781
|
if (this.closed && method !== NAVIGATION_CLOSE_METHOD) {
|
|
6027
5782
|
throw createMockBridgeError('INVALID_STATE', '当前小程序容器已关闭');
|
|
6028
5783
|
}
|
|
6029
|
-
const capability = MINI_PROGRAM_PROTOCOL_CAPABILITIES.find(item => item.method === method);
|
|
5784
|
+
const capability = MINI_PROGRAM_PROTOCOL_CAPABILITIES.find((item) => item.method === method);
|
|
6030
5785
|
if (capability) {
|
|
6031
5786
|
const policyResult = this.evaluateRuntimePermission(capability.permission, payload);
|
|
6032
5787
|
if (!policyResult.allowed) {
|
|
@@ -6047,72 +5802,38 @@ class MiniProgramMockRuntime {
|
|
|
6047
5802
|
vibrates: [...this.vibrates],
|
|
6048
5803
|
};
|
|
6049
5804
|
}
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
async getUserInfo() {
|
|
6056
|
-
return getMiniProgramRuntimeUserInfo(this.adapter);
|
|
6057
|
-
}
|
|
6058
|
-
async getCurrentUserDetail() {
|
|
6059
|
-
const home = await this.getMockUserHomeV2Result();
|
|
6060
|
-
return home
|
|
6061
|
-
? {
|
|
6062
|
-
isLogin: true,
|
|
6063
|
-
data: mapCurrentUserDetail(home),
|
|
6064
|
-
}
|
|
6065
|
-
: {
|
|
6066
|
-
isLogin: false,
|
|
6067
|
-
data: null,
|
|
6068
|
-
};
|
|
5805
|
+
login(payload) {
|
|
5806
|
+
if (!this.authLoginEnabled) {
|
|
5807
|
+
throw createMockBridgeError('SERVER_API_REQUIRED', '当前模式要求由小程序服务端完成 auth.login');
|
|
5808
|
+
}
|
|
5809
|
+
return loginMiniProgramRuntime(payload, this.adapter, this.options.identity);
|
|
6069
5810
|
}
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
return
|
|
6073
|
-
? {
|
|
6074
|
-
isLogin: true,
|
|
6075
|
-
data: mapCurrentUserProfile(home),
|
|
6076
|
-
}
|
|
6077
|
-
: {
|
|
6078
|
-
isLogin: false,
|
|
6079
|
-
data: null,
|
|
6080
|
-
};
|
|
5811
|
+
getUserInfo() {
|
|
5812
|
+
this.assertMockHostUserApiEnabled();
|
|
5813
|
+
return getMiniProgramRuntimeUserInfo(this.adapter, this.options.identity);
|
|
6081
5814
|
}
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
return
|
|
6085
|
-
? {
|
|
6086
|
-
isLogin: true,
|
|
6087
|
-
data: mapPlatformAccountOverview(home),
|
|
6088
|
-
}
|
|
6089
|
-
: {
|
|
6090
|
-
isLogin: false,
|
|
6091
|
-
data: null,
|
|
6092
|
-
};
|
|
5815
|
+
getLocalIdentity(payload) {
|
|
5816
|
+
this.assertMockHostUserApiEnabled();
|
|
5817
|
+
return getMiniProgramRuntimeLocalIdentity(payload, this.adapter, this.options.identity);
|
|
6093
5818
|
}
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
: createUnauthenticatedPlatformAccountResult(platform);
|
|
5819
|
+
revokeAuthorization(payload) {
|
|
5820
|
+
if (!this.authorizationMutationEnabled) {
|
|
5821
|
+
throw createMockBridgeError('SERVER_API_REQUIRED', '当前模式缺少有效的小程序权限快照');
|
|
5822
|
+
}
|
|
5823
|
+
return revokeMiniProgramRuntimeAuthorization(payload, this.adapter, this.options.identity);
|
|
6100
5824
|
}
|
|
6101
5825
|
async getSteamGameList(payload) {
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
user: {
|
|
6106
|
-
...this.adapter.user,
|
|
6107
|
-
getCurrentUserSteamGameListEnvelope: request => Promise.resolve(createMockSteamGameListEnvelope(request.limit, request.offset)),
|
|
6108
|
-
},
|
|
6109
|
-
}, home);
|
|
5826
|
+
this.assertMockHostUserApiEnabled();
|
|
5827
|
+
const info = await getMiniProgramRuntimeUserInfo(this.adapter, this.options.identity);
|
|
5828
|
+
return getMiniProgramRuntimeSteamGameList(payload, this.adapter, info.isHeyboxAppLoggedIn ? { steam_id_info: { steamid: '76561198000000000' } } : undefined);
|
|
6110
5829
|
}
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
5830
|
+
assertMockHostUserApiEnabled() {
|
|
5831
|
+
if (!this.hostUserApiEnabled) {
|
|
5832
|
+
throw createMockBridgeError('SERVER_API_REQUIRED', '当前模式要求由小程序服务端调用用户 API');
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5835
|
+
invoke(method, payload) {
|
|
5836
|
+
return this.runMethod(method, payload);
|
|
6116
5837
|
}
|
|
6117
5838
|
showShareMenu(payload) {
|
|
6118
5839
|
return showMiniProgramRuntimeShareMenu(payload, this.adapter, this.getShareContext());
|
|
@@ -6252,6 +5973,31 @@ class MiniProgramMockRuntime {
|
|
|
6252
5973
|
this.records.splice(30);
|
|
6253
5974
|
}
|
|
6254
5975
|
}
|
|
5976
|
+
function resolveMockRuntimePermissions(options) {
|
|
5977
|
+
if (options.networkMode === 'disabled') {
|
|
5978
|
+
return {
|
|
5979
|
+
schema_version: 1,
|
|
5980
|
+
entries: [{ key: 'network.request', status: 'disabled', config: { useOfficialDomain: false } }],
|
|
5981
|
+
};
|
|
5982
|
+
}
|
|
5983
|
+
if (options.networkMode === 'enabled') {
|
|
5984
|
+
return {
|
|
5985
|
+
schema_version: 1,
|
|
5986
|
+
entries: [{ key: 'network.request', status: 'enabled', config: { useOfficialDomain: true } }],
|
|
5987
|
+
};
|
|
5988
|
+
}
|
|
5989
|
+
return options.runtimePermissions;
|
|
5990
|
+
}
|
|
5991
|
+
function isMockHostUserApiEnabled(runtimePermissions) {
|
|
5992
|
+
const parsed = parseMiniProgramRuntimePermissions(runtimePermissions);
|
|
5993
|
+
return parsed.valid && !hasMiniProgramNetworkRequestPermission(runtimePermissions);
|
|
5994
|
+
}
|
|
5995
|
+
function isMockAuthLoginEnabled(runtimePermissions) {
|
|
5996
|
+
return hasMiniProgramNetworkRequestPermission(runtimePermissions);
|
|
5997
|
+
}
|
|
5998
|
+
function isMockAuthorizationMutationEnabled(runtimePermissions) {
|
|
5999
|
+
return parseMiniProgramRuntimePermissions(runtimePermissions).valid;
|
|
6000
|
+
}
|
|
6255
6001
|
/**
|
|
6256
6002
|
* 读取 mock host 自身 URL 上的启动参数作为可信分享上下文。
|
|
6257
6003
|
* 与生产 runtime 不同:生产只信任启动详情回填的 canonical ID;
|
|
@@ -6270,58 +6016,6 @@ function readMockHostShareContext(href) {
|
|
|
6270
6016
|
return {};
|
|
6271
6017
|
}
|
|
6272
6018
|
}
|
|
6273
|
-
function createMockSteamGameListEnvelope(limit, offset) {
|
|
6274
|
-
const gameList = [
|
|
6275
|
-
{
|
|
6276
|
-
appid: 814380,
|
|
6277
|
-
steam_appid: 814380,
|
|
6278
|
-
name: 'Sekiro: Shadows Die Twice',
|
|
6279
|
-
image: 'https://imgheybox.max-c.com/heybox/game/header/814380_lliLB.jpg',
|
|
6280
|
-
appicon: 'https://imgheybox.max-c.com/web/2024/02/14/f072e95b0026fa2efba35f737dbc2aab.webp',
|
|
6281
|
-
background: 'https://cdn.max-c.com/heybox/game/background/814380_cHOHF.jpg',
|
|
6282
|
-
playtime_forever: 2940,
|
|
6283
|
-
playtime_2weeks: 120,
|
|
6284
|
-
playtime_percent: '0.12',
|
|
6285
|
-
achieved_count: 3,
|
|
6286
|
-
achivement_count: 34,
|
|
6287
|
-
score: '9.8',
|
|
6288
|
-
is_free: false,
|
|
6289
|
-
not_owned: false,
|
|
6290
|
-
online_player: '4314',
|
|
6291
|
-
external_tags: ['Action', 'Adventure'],
|
|
6292
|
-
genres: ['Action'],
|
|
6293
|
-
price: {
|
|
6294
|
-
initial: '268',
|
|
6295
|
-
current: '134',
|
|
6296
|
-
discount: 50,
|
|
6297
|
-
lowest_price: 134,
|
|
6298
|
-
},
|
|
6299
|
-
},
|
|
6300
|
-
{
|
|
6301
|
-
appid: 230410,
|
|
6302
|
-
steam_appid: 230410,
|
|
6303
|
-
name: 'Warframe',
|
|
6304
|
-
image: 'https://imgheybox.max-c.com/heybox/game/header/230410_tamuE.jpg',
|
|
6305
|
-
playtime_forever: 1024140,
|
|
6306
|
-
playtime_2weeks: 1500,
|
|
6307
|
-
achieved_count: 142,
|
|
6308
|
-
achivement_count: 193,
|
|
6309
|
-
is_free: true,
|
|
6310
|
-
not_owned: false,
|
|
6311
|
-
online_player: '5万',
|
|
6312
|
-
external_tags: ['MMO', 'Sci-Fi'],
|
|
6313
|
-
genres: ['Action', 'Free To Play'],
|
|
6314
|
-
},
|
|
6315
|
-
].slice(offset, offset + limit);
|
|
6316
|
-
return {
|
|
6317
|
-
status: 'ok',
|
|
6318
|
-
result: {
|
|
6319
|
-
game_list: gameList,
|
|
6320
|
-
total: 2,
|
|
6321
|
-
is_me: true,
|
|
6322
|
-
},
|
|
6323
|
-
};
|
|
6324
|
-
}
|
|
6325
6019
|
function isMiniProgramMockRuntimeMethod(method) {
|
|
6326
6020
|
return MINI_PROGRAM_MOCK_RUNTIME_METHODS.includes(method);
|
|
6327
6021
|
}
|
|
@@ -6489,10 +6183,10 @@ class MockLeaderboardBoard {
|
|
|
6489
6183
|
this.key = config.key;
|
|
6490
6184
|
this.order = readMockLeaderboardOrder(config.order);
|
|
6491
6185
|
this.rankLimit = readMockLeaderboardRankLimit(config.rankLimit);
|
|
6492
|
-
config.entries?.forEach(entry => {
|
|
6186
|
+
config.entries?.forEach((entry) => {
|
|
6493
6187
|
const now = Date.now();
|
|
6494
|
-
this.entries.set(entry.
|
|
6495
|
-
|
|
6188
|
+
this.entries.set(entry.appUserId, {
|
|
6189
|
+
appUserId: readMockLeaderboardAppUserId(entry.appUserId),
|
|
6496
6190
|
score: readMockLeaderboardScore(entry.score),
|
|
6497
6191
|
extra: cloneMockRecord(entry.extra || {}),
|
|
6498
6192
|
createdAt: entry.createdAt ?? now,
|
|
@@ -6507,7 +6201,7 @@ class MockLeaderboardBoard {
|
|
|
6507
6201
|
return this.getCurrentUserEntry(userId);
|
|
6508
6202
|
}
|
|
6509
6203
|
this.entries.set(userId, {
|
|
6510
|
-
userId,
|
|
6204
|
+
appUserId: userId,
|
|
6511
6205
|
score,
|
|
6512
6206
|
extra: extra === undefined ? cloneMockRecord(existing?.extra || {}) : cloneMockRecord(extra),
|
|
6513
6207
|
createdAt: existing?.createdAt ?? now,
|
|
@@ -6536,15 +6230,14 @@ class MockLeaderboardBoard {
|
|
|
6536
6230
|
return freshCachedResponse;
|
|
6537
6231
|
}
|
|
6538
6232
|
}
|
|
6539
|
-
const entriesAfterCursor = this.getListEntries()
|
|
6540
|
-
.filter(entry => listCursor ? this.isAfterCursor(entry, listCursor) : true);
|
|
6233
|
+
const entriesAfterCursor = this.getListEntries().filter((entry) => (listCursor ? this.isAfterCursor(entry, listCursor) : true));
|
|
6541
6234
|
const entries = entriesAfterCursor.slice(0, limit);
|
|
6542
6235
|
const hasMore = entries.length < entriesAfterCursor.length;
|
|
6543
6236
|
return this.createListResponse(entries, startRank, hasMore);
|
|
6544
6237
|
}
|
|
6545
6238
|
getCurrentUserEntry(userId) {
|
|
6546
6239
|
const orderedEntries = this.getOrderedEntries();
|
|
6547
|
-
const index = orderedEntries.findIndex(entry => entry.
|
|
6240
|
+
const index = orderedEntries.findIndex((entry) => entry.appUserId === userId);
|
|
6548
6241
|
if (index < 0) {
|
|
6549
6242
|
return undefined;
|
|
6550
6243
|
}
|
|
@@ -6574,9 +6267,7 @@ class MockLeaderboardBoard {
|
|
|
6574
6267
|
let start = 0;
|
|
6575
6268
|
let startRank = 1;
|
|
6576
6269
|
if (cursor) {
|
|
6577
|
-
const cursorIndex = this.cachedList.entries.findIndex(entry =>
|
|
6578
|
-
entry.updatedAt === cursor.update_time &&
|
|
6579
|
-
entry.userId === cursor.user_id));
|
|
6270
|
+
const cursorIndex = this.cachedList.entries.findIndex((entry) => entry.score === cursor.score && entry.updatedAt === cursor.update_time && entry.appUserId === cursor.user_id);
|
|
6580
6271
|
if (cursorIndex < 0) {
|
|
6581
6272
|
return undefined;
|
|
6582
6273
|
}
|
|
@@ -6596,12 +6287,10 @@ class MockLeaderboardBoard {
|
|
|
6596
6287
|
return this.createListResponse(this.cachedList.entries.slice(start, end), startRank, hasMore);
|
|
6597
6288
|
}
|
|
6598
6289
|
refreshListCache() {
|
|
6599
|
-
const cacheLimit = this.rankLimit > 0 && this.rankLimit < MOCK_LEADERBOARD_CACHE_MAX_ENTRIES
|
|
6600
|
-
? this.rankLimit
|
|
6601
|
-
: MOCK_LEADERBOARD_CACHE_MAX_ENTRIES;
|
|
6290
|
+
const cacheLimit = this.rankLimit > 0 && this.rankLimit < MOCK_LEADERBOARD_CACHE_MAX_ENTRIES ? this.rankLimit : MOCK_LEADERBOARD_CACHE_MAX_ENTRIES;
|
|
6602
6291
|
const rankedEntries = this.getListEntries();
|
|
6603
6292
|
this.cachedList = {
|
|
6604
|
-
entries: rankedEntries.slice(0, cacheLimit).map(entry => ({
|
|
6293
|
+
entries: rankedEntries.slice(0, cacheLimit).map((entry) => ({
|
|
6605
6294
|
...entry,
|
|
6606
6295
|
extra: cloneMockRecord(entry.extra),
|
|
6607
6296
|
})),
|
|
@@ -6633,9 +6322,9 @@ class MockLeaderboardBoard {
|
|
|
6633
6322
|
if (left.updatedAt !== right.updatedAt) {
|
|
6634
6323
|
return left.updatedAt - right.updatedAt;
|
|
6635
6324
|
}
|
|
6636
|
-
return left.
|
|
6325
|
+
return left.appUserId.localeCompare(right.appUserId);
|
|
6637
6326
|
})
|
|
6638
|
-
.map(entry => ({
|
|
6327
|
+
.map((entry) => ({
|
|
6639
6328
|
...entry,
|
|
6640
6329
|
extra: cloneMockRecord(entry.extra),
|
|
6641
6330
|
}));
|
|
@@ -6682,7 +6371,7 @@ class MockLeaderboardBoard {
|
|
|
6682
6371
|
rank: entry.rank,
|
|
6683
6372
|
score: entry.score,
|
|
6684
6373
|
update_time: entry.updatedAt,
|
|
6685
|
-
user_id: entry.
|
|
6374
|
+
user_id: entry.appUserId,
|
|
6686
6375
|
}));
|
|
6687
6376
|
}
|
|
6688
6377
|
isAfterCursor(entry, cursor) {
|
|
@@ -6692,7 +6381,7 @@ class MockLeaderboardBoard {
|
|
|
6692
6381
|
if (entry.updatedAt !== cursor.update_time) {
|
|
6693
6382
|
return entry.updatedAt > cursor.update_time;
|
|
6694
6383
|
}
|
|
6695
|
-
return entry.
|
|
6384
|
+
return entry.appUserId.localeCompare(cursor.user_id) > 0;
|
|
6696
6385
|
}
|
|
6697
6386
|
isWithinRankLimit(rank) {
|
|
6698
6387
|
return this.rankLimit <= 0 || rank <= this.rankLimit;
|
|
@@ -6724,9 +6413,9 @@ function readMockLeaderboardRankLimit(value) {
|
|
|
6724
6413
|
}
|
|
6725
6414
|
return value;
|
|
6726
6415
|
}
|
|
6727
|
-
function
|
|
6416
|
+
function readMockLeaderboardAppUserId(value) {
|
|
6728
6417
|
if (typeof value !== 'string' || !value) {
|
|
6729
|
-
throw createMockBridgeError('INVALID_PARAMS', 'mock leaderboard entry
|
|
6418
|
+
throw createMockBridgeError('INVALID_PARAMS', 'mock leaderboard entry appUserId 必须是非空字符串');
|
|
6730
6419
|
}
|
|
6731
6420
|
return value;
|
|
6732
6421
|
}
|
|
@@ -6763,125 +6452,9 @@ function decodeMockLeaderboardCursor(value) {
|
|
|
6763
6452
|
throw createMockBridgeError('INVALID_PARAMS', 'cloud.leaderboard.getList cursor 不合法');
|
|
6764
6453
|
}
|
|
6765
6454
|
const binary = atob(padded);
|
|
6766
|
-
const bytes = Uint8Array.from(binary, char => char.charCodeAt(0));
|
|
6455
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
6767
6456
|
return new TextDecoder().decode(bytes);
|
|
6768
6457
|
}
|
|
6769
|
-
function createMockRuntimeUserHomeV2Result(userInfo) {
|
|
6770
|
-
const heyboxId = userInfo.heybox_id || 'debug_user';
|
|
6771
|
-
const nickname = userInfo.nickname || 'PC Debug User';
|
|
6772
|
-
const avatar = userInfo.avatar || '';
|
|
6773
|
-
return {
|
|
6774
|
-
account_detail: {
|
|
6775
|
-
userid: heyboxId,
|
|
6776
|
-
username: nickname,
|
|
6777
|
-
avartar: avatar,
|
|
6778
|
-
signature: '来自 hb-sdk mock runtime 的用户资料',
|
|
6779
|
-
ip_location: 'Mock',
|
|
6780
|
-
friends_count: '128',
|
|
6781
|
-
level_info: {
|
|
6782
|
-
level: '12',
|
|
6783
|
-
level_name: 'Mock Lv.12',
|
|
6784
|
-
exp: '2400',
|
|
6785
|
-
next_exp: '3000',
|
|
6786
|
-
},
|
|
6787
|
-
bbs_medal: [
|
|
6788
|
-
{
|
|
6789
|
-
id: 'mock-medal',
|
|
6790
|
-
name: 'Mock Medal',
|
|
6791
|
-
desc: '本地 mock 勋章',
|
|
6792
|
-
is_wearing: true,
|
|
6793
|
-
},
|
|
6794
|
-
],
|
|
6795
|
-
},
|
|
6796
|
-
profile: {
|
|
6797
|
-
heybox_id: heyboxId,
|
|
6798
|
-
nickname,
|
|
6799
|
-
avatar,
|
|
6800
|
-
gender: 'unknown',
|
|
6801
|
-
signature: '来自 hb-sdk mock runtime 的个人档案',
|
|
6802
|
-
email: 'mock-user@example.com',
|
|
6803
|
-
},
|
|
6804
|
-
platform_list: ['steam', 'epic', 'xbox', 'psn', 'switch', 'pc_hardware', 'mobile'],
|
|
6805
|
-
steam_is_hidden: false,
|
|
6806
|
-
steam_id_info: {
|
|
6807
|
-
steamid: '76561198000000000',
|
|
6808
|
-
short_id: 'mock-steam',
|
|
6809
|
-
nickname: `${nickname} Steam`,
|
|
6810
|
-
avatar,
|
|
6811
|
-
level: '12',
|
|
6812
|
-
friend_code_show: true,
|
|
6813
|
-
friend_code: '123456789',
|
|
6814
|
-
inventory_open: true,
|
|
6815
|
-
personal_information_open: true,
|
|
6816
|
-
has_history: true,
|
|
6817
|
-
stats: [
|
|
6818
|
-
{
|
|
6819
|
-
key: 'total_game_count',
|
|
6820
|
-
name: '游戏数',
|
|
6821
|
-
value: '42',
|
|
6822
|
-
},
|
|
6823
|
-
{
|
|
6824
|
-
key: 'total_player_time',
|
|
6825
|
-
name: '总时长',
|
|
6826
|
-
value: '128h',
|
|
6827
|
-
},
|
|
6828
|
-
],
|
|
6829
|
-
},
|
|
6830
|
-
epic_account_info: {
|
|
6831
|
-
nickname: `${nickname} Epic`,
|
|
6832
|
-
avatar,
|
|
6833
|
-
xuid: 'mock-epic-xuid',
|
|
6834
|
-
desc: 'Mock Epic account',
|
|
6835
|
-
},
|
|
6836
|
-
xbox_account_info: {
|
|
6837
|
-
nickname: `${nickname} Xbox`,
|
|
6838
|
-
avatar,
|
|
6839
|
-
xuid: 'mock-xbox-xuid',
|
|
6840
|
-
desc: 'Mock Xbox account',
|
|
6841
|
-
},
|
|
6842
|
-
psn_account_info: {
|
|
6843
|
-
name: `${nickname} PSN`,
|
|
6844
|
-
avatar,
|
|
6845
|
-
level: '8',
|
|
6846
|
-
description: 'Mock PSN account',
|
|
6847
|
-
is_open: true,
|
|
6848
|
-
key1: '白金',
|
|
6849
|
-
value1: '3',
|
|
6850
|
-
},
|
|
6851
|
-
switch_account_info: {
|
|
6852
|
-
heybox_id: heyboxId,
|
|
6853
|
-
nickname: `${nickname} Switch`,
|
|
6854
|
-
avatar,
|
|
6855
|
-
friend_code: 'SW-1234-5678-9012',
|
|
6856
|
-
friends_total: '16',
|
|
6857
|
-
games_total: '9',
|
|
6858
|
-
server_name: 'Mock Server',
|
|
6859
|
-
type: 'switch',
|
|
6860
|
-
},
|
|
6861
|
-
hardware_info: {
|
|
6862
|
-
cpu: 'Mock CPU',
|
|
6863
|
-
gpu: 'Mock GPU',
|
|
6864
|
-
board: 'Mock Board',
|
|
6865
|
-
perf_level: 'A',
|
|
6866
|
-
},
|
|
6867
|
-
mobile_account_info: {
|
|
6868
|
-
current_device: {
|
|
6869
|
-
id: 'mock-device',
|
|
6870
|
-
name: 'Mock Phone',
|
|
6871
|
-
desc: 'hb-sdk mock runtime device',
|
|
6872
|
-
model: 'MockPhone 1',
|
|
6873
|
-
update_time: '2026-06-04',
|
|
6874
|
-
stats: [
|
|
6875
|
-
{
|
|
6876
|
-
key: 'score',
|
|
6877
|
-
name: '跑分',
|
|
6878
|
-
value: '999999',
|
|
6879
|
-
},
|
|
6880
|
-
],
|
|
6881
|
-
},
|
|
6882
|
-
},
|
|
6883
|
-
};
|
|
6884
|
-
}
|
|
6885
6458
|
function renderMockToast(toast) {
|
|
6886
6459
|
const document = readDocument();
|
|
6887
6460
|
if (!document) {
|
|
@@ -7014,12 +6587,7 @@ function readWindow() {
|
|
|
7014
6587
|
return typeof window === 'undefined' ? undefined : window;
|
|
7015
6588
|
}
|
|
7016
6589
|
function escapeMockHtml(value) {
|
|
7017
|
-
return String(value)
|
|
7018
|
-
.replace(/&/g, '&')
|
|
7019
|
-
.replace(/</g, '<')
|
|
7020
|
-
.replace(/>/g, '>')
|
|
7021
|
-
.replace(/"/g, '"')
|
|
7022
|
-
.replace(/'/g, ''');
|
|
6590
|
+
return String(value).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
7023
6591
|
}
|
|
7024
6592
|
function isPlainRecord(value) {
|
|
7025
6593
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -7244,6 +6812,8 @@ const NETWORK_PROXY_PATH = '/__hb_sdk_mock_network__';
|
|
|
7244
6812
|
const MOCK_HOST_BOOTSTRAP_PATH = '/__hb_sdk_bootstrap__';
|
|
7245
6813
|
const DEV_PERMISSIONS_PATH = '/__hb_sdk__/dev-permissions';
|
|
7246
6814
|
const RUNTIME_GATE_PROBE_METHOD = 'runtime.gate.probe';
|
|
6815
|
+
/** 长 heybox:// 协议需要更大展示面,便于测试端扫码。 */
|
|
6816
|
+
const MOBILE_QR_IMAGE_SIZE = 320;
|
|
7247
6817
|
const params = new URL(location.href).searchParams;
|
|
7248
6818
|
const miniUrl = params.get('mini_url');
|
|
7249
6819
|
let lanAddresses = [];
|
|
@@ -7259,7 +6829,7 @@ let mobileQrGeneration = 0;
|
|
|
7259
6829
|
let mobileQrRefreshTimer;
|
|
7260
6830
|
let mobileDevSessionCreation = Promise.resolve();
|
|
7261
6831
|
let currentUser = {
|
|
7262
|
-
|
|
6832
|
+
app_user_id: 'au_debug_user',
|
|
7263
6833
|
nickname: 'PC Debug User',
|
|
7264
6834
|
avatar: '',
|
|
7265
6835
|
};
|
|
@@ -7342,7 +6912,7 @@ queryElement('#login-button').addEventListener('click', () => {
|
|
|
7342
6912
|
queryElement('#logout-button').addEventListener('click', () => {
|
|
7343
6913
|
currentUser = null;
|
|
7344
6914
|
updateUserStatus();
|
|
7345
|
-
postEvent('
|
|
6915
|
+
postEvent('heybox_app_login_change', { isHeyboxAppLoggedIn: false });
|
|
7346
6916
|
});
|
|
7347
6917
|
queryElement('#show-button').addEventListener('click', () => {
|
|
7348
6918
|
postEvent('show', { timestamp: Date.now(), source: 'mock-host' });
|
|
@@ -7368,7 +6938,8 @@ elements.resetPermissionsButton.addEventListener('click', () => {
|
|
|
7368
6938
|
const mobileAppQrReady = setupMobileAppQr();
|
|
7369
6939
|
function createRuntime() {
|
|
7370
6940
|
return new MiniProgramMockRuntime(platformAdapter, {
|
|
7371
|
-
|
|
6941
|
+
onHeyboxAppLoginChange: payload => postEvent('heybox_app_login_change', payload),
|
|
6942
|
+
onUserInfoAuthorizationChange: authorization => postEvent('user_info_authorization_change', authorization),
|
|
7372
6943
|
runtimePermissions: createMiniProgramMockRuntimePermissions(permissionState),
|
|
7373
6944
|
});
|
|
7374
6945
|
}
|
|
@@ -7723,15 +7294,9 @@ function isBridgeMessage(message) {
|
|
|
7723
7294
|
message.version === MINI_PROGRAM_MESSAGE_VERSION &&
|
|
7724
7295
|
message.nonce === nonce);
|
|
7725
7296
|
}
|
|
7726
|
-
function createUserInfoResult() {
|
|
7727
|
-
return {
|
|
7728
|
-
isLogin: Boolean(currentUser),
|
|
7729
|
-
userInfo: currentUser ? { ...currentUser } : null,
|
|
7730
|
-
};
|
|
7731
|
-
}
|
|
7732
7297
|
function createUserFromForm() {
|
|
7733
7298
|
return {
|
|
7734
|
-
|
|
7299
|
+
app_user_id: 'au_debug_user',
|
|
7735
7300
|
nickname: elements.nicknameInput.value.trim() || 'PC Debug User',
|
|
7736
7301
|
avatar: '',
|
|
7737
7302
|
};
|
|
@@ -7875,27 +7440,29 @@ async function updateMobileQrCode() {
|
|
|
7875
7440
|
if (generation !== mobileQrGeneration) {
|
|
7876
7441
|
return;
|
|
7877
7442
|
}
|
|
7443
|
+
// 测试端扫一扫暂不支持打开网页,二维码必须直接编码 heybox:// 协议(不能走 HTTP 短链)。
|
|
7878
7444
|
const payload = applyDevContextToMobileQrPayload(basePayload, devSession.url);
|
|
7879
7445
|
const imageUrl = await browserExports.toDataURL(payload, {
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7446
|
+
// 内容很长时用 L 可降低模块密度,配合更大尺寸更容易扫上。
|
|
7447
|
+
errorCorrectionLevel: 'L',
|
|
7448
|
+
margin: 2,
|
|
7449
|
+
width: MOBILE_QR_IMAGE_SIZE,
|
|
7883
7450
|
});
|
|
7884
7451
|
if (generation !== mobileQrGeneration) {
|
|
7885
7452
|
return;
|
|
7886
7453
|
}
|
|
7887
7454
|
elements.mobileQrImage.src = imageUrl;
|
|
7888
7455
|
elements.mobileQrImage.hidden = false;
|
|
7889
|
-
elements.mobileQrStatus.textContent =
|
|
7456
|
+
elements.mobileQrStatus.textContent = `协议约 ${payload.length} 字符 · 请用测试端扫一扫`;
|
|
7890
7457
|
scheduleMobileQrRefresh(devSession.expiresAt, generation);
|
|
7891
7458
|
}
|
|
7892
|
-
catch {
|
|
7459
|
+
catch (error) {
|
|
7893
7460
|
if (generation !== mobileQrGeneration) {
|
|
7894
7461
|
return;
|
|
7895
7462
|
}
|
|
7896
7463
|
elements.mobileQrImage.removeAttribute('src');
|
|
7897
7464
|
elements.mobileQrImage.hidden = true;
|
|
7898
|
-
elements.mobileQrStatus.textContent =
|
|
7465
|
+
elements.mobileQrStatus.textContent = `二维码生成失败:${readErrorMessage(error)}`;
|
|
7899
7466
|
}
|
|
7900
7467
|
}
|
|
7901
7468
|
async function createMobileDevSession(selected) {
|
|
@@ -7957,7 +7524,7 @@ function clearMobileQrRefreshTimer() {
|
|
|
7957
7524
|
mobileQrRefreshTimer = undefined;
|
|
7958
7525
|
}
|
|
7959
7526
|
function applyDevContextToMobileQrPayload(payload, devContextUrl) {
|
|
7960
|
-
const protocolMarker = '#heybox://';
|
|
7527
|
+
const protocolMarker = payload.includes('#heybox://') ? '#heybox://' : 'heybox://';
|
|
7961
7528
|
const protocolIndex = payload.indexOf(protocolMarker);
|
|
7962
7529
|
if (protocolIndex < 0) {
|
|
7963
7530
|
throw new Error('Invalid mobile App QR payload');
|
|
@@ -7974,9 +7541,31 @@ function applyDevContextToMobileQrPayload(payload, devContextUrl) {
|
|
|
7974
7541
|
const devShellUrl = new URL(devShellUrlValue);
|
|
7975
7542
|
devShellUrl.searchParams.delete('dev_network_request');
|
|
7976
7543
|
devShellUrl.searchParams.set(MINI_PROGRAM_DEV_CONTEXT_QUERY_PARAM, devContextUrl);
|
|
7544
|
+
// 与 mini_url 相同:客户端打开 runtime_url 时往往不会合并 sandbox query,
|
|
7545
|
+
// 需要把 dev_context_url 写进 runtime_url 本身。
|
|
7546
|
+
const runtimeUrl = devShellUrl.searchParams.get('runtime_url');
|
|
7547
|
+
if (runtimeUrl) {
|
|
7548
|
+
try {
|
|
7549
|
+
const hostUrl = new URL(runtimeUrl);
|
|
7550
|
+
if (hostUrl.protocol === 'http:' || hostUrl.protocol === 'https:') {
|
|
7551
|
+
hostUrl.searchParams.set(MINI_PROGRAM_DEV_CONTEXT_QUERY_PARAM, devContextUrl);
|
|
7552
|
+
const miniUrl = devShellUrl.searchParams.get('mini_url');
|
|
7553
|
+
if (miniUrl && !hostUrl.searchParams.get('mini_url')) {
|
|
7554
|
+
hostUrl.searchParams.set('mini_url', miniUrl);
|
|
7555
|
+
}
|
|
7556
|
+
devShellUrl.searchParams.set('runtime_url', hostUrl.toString());
|
|
7557
|
+
}
|
|
7558
|
+
}
|
|
7559
|
+
catch {
|
|
7560
|
+
// runtime_url 非法时保留原值,避免阻断二维码生成。
|
|
7561
|
+
}
|
|
7562
|
+
}
|
|
7977
7563
|
openWindowPayload.webview.url = devShellUrl.toString();
|
|
7978
7564
|
return `${payload.slice(0, protocolIndex)}${protocolMarker}${encodeURIComponent(JSON.stringify(openWindowPayload))}`;
|
|
7979
7565
|
}
|
|
7566
|
+
function readErrorMessage(error) {
|
|
7567
|
+
return error instanceof Error && error.message ? error.message : '未知错误';
|
|
7568
|
+
}
|
|
7980
7569
|
function updateNativeAppLaunchAvailability() {
|
|
7981
7570
|
const isUnavailable = Boolean(nativeAppLaunchUnavailableReason);
|
|
7982
7571
|
elements.macAppButton.disabled = isUnavailable;
|