@heybox/hb-sdk 0.5.9 → 0.5.11
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/README.md +94 -51
- package/dist/cli-chunks/{create-zJEmBOvP.cjs → create-BaqN3jeW.cjs} +1 -1
- package/dist/cli-chunks/{dev-DsqPSgeJ.cjs → dev-DUZgcgYE.cjs} +1 -1
- package/dist/cli-chunks/{doctor-DZkEwLT9.cjs → doctor-DUjQij7b.cjs} +1 -1
- package/dist/cli-chunks/{index-CU-8dVKo.cjs → index-6W5M4MLF.cjs} +2 -2
- package/dist/cli-chunks/{index-BpVGoWHu.cjs → index-BROIiSNN.cjs} +57 -13
- package/dist/cli-chunks/{login-DhKNZpl4.cjs → login-BejyA1mX.cjs} +2 -2
- package/dist/cli-chunks/{remote-D0izsd5c.cjs → remote-ChGmIJC1.cjs} +117 -3
- package/dist/cli-chunks/{session-O_NdH1Z2.cjs → session-jHF9KWBo.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/mock-host/main.js +590 -0
- package/dist/index.cjs.js +37 -0
- package/dist/index.esm.js +37 -1
- package/dist/miniapp-publish.cjs.js +8 -0
- package/dist/miniapp-publish.esm.js +5 -1
- package/dist/protocol.cjs.js +50 -0
- package/dist/protocol.esm.js +46 -1
- package/package.json +3 -1
- package/skill/SKILL.md +5 -3
- package/skill/references/api-protocol.md +20 -0
- package/skill/references/api-root.md +63 -3
- package/skill/references/llms-index.md +1 -0
- package/skill/scripts/sync-agent-skills-payload.mjs +359 -0
- package/skill/skill.json +4 -4
- package/types/core/client.d.ts +1 -1
- package/types/core/sdk.d.ts +3 -0
- package/types/core/singleton.d.ts +3 -0
- package/types/index.d.ts +3 -1
- package/types/miniapp-publish/index.d.ts +4 -0
- package/types/modules/cloud/index.d.ts +100 -0
- package/types/protocol/capabilities.d.ts +55 -2
- package/types/protocol.d.ts +3 -2
|
@@ -94,6 +94,16 @@ const NAVIGATION_CLOSE_METHOD = 'navigation.close';
|
|
|
94
94
|
const NAVIGATION_RELOAD_METHOD = 'navigation.reload';
|
|
95
95
|
/** 打开黑盒 App 页面能力方法名。 */
|
|
96
96
|
const NAVIGATION_OPEN_APP_PAGE_METHOD = 'navigation.openAppPage';
|
|
97
|
+
/** 提交当前用户排行榜分数能力方法名。 */
|
|
98
|
+
const CLOUD_LEADERBOARD_SUBMIT_METHOD = 'cloud.leaderboard.submit';
|
|
99
|
+
/** 读取排行榜列表能力方法名。 */
|
|
100
|
+
const CLOUD_LEADERBOARD_GET_LIST_METHOD = 'cloud.leaderboard.getList';
|
|
101
|
+
/** 读取当前用户排行榜记录能力方法名。 */
|
|
102
|
+
const CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD = 'cloud.leaderboard.getCurrentUserEntry';
|
|
103
|
+
/** 删除当前用户排行榜记录能力方法名。 */
|
|
104
|
+
const CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD = 'cloud.leaderboard.deleteCurrentUserEntry';
|
|
105
|
+
/** 读取排行榜基础信息能力方法名。 */
|
|
106
|
+
const CLOUD_LEADERBOARD_GET_INFO_METHOD = 'cloud.leaderboard.getInfo';
|
|
97
107
|
/**
|
|
98
108
|
* 小程序开放能力目录。
|
|
99
109
|
*
|
|
@@ -255,6 +265,41 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
|
255
265
|
permission: 'navigation.openAppPage',
|
|
256
266
|
risk: 'medium',
|
|
257
267
|
},
|
|
268
|
+
{
|
|
269
|
+
method: CLOUD_LEADERBOARD_SUBMIT_METHOD,
|
|
270
|
+
module: 'cloud',
|
|
271
|
+
capability: CLOUD_LEADERBOARD_SUBMIT_METHOD,
|
|
272
|
+
permission: 'cloud.leaderboard.write',
|
|
273
|
+
risk: 'medium',
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
method: CLOUD_LEADERBOARD_GET_LIST_METHOD,
|
|
277
|
+
module: 'cloud',
|
|
278
|
+
capability: CLOUD_LEADERBOARD_GET_LIST_METHOD,
|
|
279
|
+
permission: 'cloud.leaderboard.read',
|
|
280
|
+
risk: 'low',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
method: CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD,
|
|
284
|
+
module: 'cloud',
|
|
285
|
+
capability: CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD,
|
|
286
|
+
permission: 'cloud.leaderboard.currentUserEntry.read',
|
|
287
|
+
risk: 'medium',
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
method: CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD,
|
|
291
|
+
module: 'cloud',
|
|
292
|
+
capability: CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD,
|
|
293
|
+
permission: 'cloud.leaderboard.currentUserEntry.delete',
|
|
294
|
+
risk: 'medium',
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
method: CLOUD_LEADERBOARD_GET_INFO_METHOD,
|
|
298
|
+
module: 'cloud',
|
|
299
|
+
capability: CLOUD_LEADERBOARD_GET_INFO_METHOD,
|
|
300
|
+
permission: 'cloud.leaderboard.info.read',
|
|
301
|
+
risk: 'low',
|
|
302
|
+
},
|
|
258
303
|
];
|
|
259
304
|
|
|
260
305
|
var browser = {};
|
|
@@ -3447,6 +3492,196 @@ function stringifyMiniProgramRuntimeJsonData(data, options) {
|
|
|
3447
3492
|
return value;
|
|
3448
3493
|
}
|
|
3449
3494
|
|
|
3495
|
+
const LEADERBOARD_KEY_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
3496
|
+
const DEFAULT_LEADERBOARD_LIST_LIMIT = 20;
|
|
3497
|
+
const MAX_LEADERBOARD_LIST_LIMIT = 100;
|
|
3498
|
+
const MAX_LEADERBOARD_EXTRA_BYTES = 2048;
|
|
3499
|
+
const MAX_LEADERBOARD_SAFE_SCORE = Number.MAX_SAFE_INTEGER;
|
|
3500
|
+
/** 提交当前用户排行榜分数。 */
|
|
3501
|
+
async function submitMiniProgramRuntimeLeaderboardEntry(payload, options) {
|
|
3502
|
+
const request = readLeaderboardPayload(payload, 'cloud.leaderboard.submit', 'submit');
|
|
3503
|
+
const userId = await readCurrentUserId(options.platformAdapter);
|
|
3504
|
+
const entry = await options.platformAdapter.cloud.leaderboard.submit({
|
|
3505
|
+
...request,
|
|
3506
|
+
miniProgramId: options.identity?.miniProgramId,
|
|
3507
|
+
userId,
|
|
3508
|
+
});
|
|
3509
|
+
assertLeaderboardEntry(entry, 'cloud.leaderboard.submit');
|
|
3510
|
+
return entry;
|
|
3511
|
+
}
|
|
3512
|
+
/** 读取排行榜列表。 */
|
|
3513
|
+
async function getMiniProgramRuntimeLeaderboardList(payload, options) {
|
|
3514
|
+
const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getList', 'list');
|
|
3515
|
+
const result = await options.platformAdapter.cloud.leaderboard.getList({
|
|
3516
|
+
...request,
|
|
3517
|
+
miniProgramId: options.identity?.miniProgramId,
|
|
3518
|
+
});
|
|
3519
|
+
assertMiniProgramRuntimeRecord(result, 'cloud.leaderboard.getList 返回值必须是对象');
|
|
3520
|
+
if (!Array.isArray(result.entries)) {
|
|
3521
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.getList 返回的 entries 必须是数组');
|
|
3522
|
+
}
|
|
3523
|
+
result.entries.forEach((entry) => assertLeaderboardEntry(entry, 'cloud.leaderboard.getList'));
|
|
3524
|
+
if (result.cursor !== undefined && typeof result.cursor !== 'string') {
|
|
3525
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.getList 返回的 cursor 必须是字符串');
|
|
3526
|
+
}
|
|
3527
|
+
if (typeof result.hasMore !== 'boolean') {
|
|
3528
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.getList 返回的 hasMore 必须是布尔值');
|
|
3529
|
+
}
|
|
3530
|
+
return result;
|
|
3531
|
+
}
|
|
3532
|
+
/** 读取当前用户在排行榜中的记录。 */
|
|
3533
|
+
async function getMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, options) {
|
|
3534
|
+
const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getCurrentUserEntry', 'keyOnly');
|
|
3535
|
+
const userId = await readCurrentUserId(options.platformAdapter);
|
|
3536
|
+
const entry = await options.platformAdapter.cloud.leaderboard.getCurrentUserEntry({
|
|
3537
|
+
...request,
|
|
3538
|
+
miniProgramId: options.identity?.miniProgramId,
|
|
3539
|
+
userId,
|
|
3540
|
+
});
|
|
3541
|
+
if (entry !== undefined) {
|
|
3542
|
+
assertLeaderboardEntry(entry, 'cloud.leaderboard.getCurrentUserEntry');
|
|
3543
|
+
}
|
|
3544
|
+
return entry;
|
|
3545
|
+
}
|
|
3546
|
+
/** 删除当前用户在排行榜中的记录。 */
|
|
3547
|
+
async function deleteMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, options) {
|
|
3548
|
+
const request = readLeaderboardPayload(payload, 'cloud.leaderboard.deleteCurrentUserEntry', 'keyOnly');
|
|
3549
|
+
const userId = await readCurrentUserId(options.platformAdapter);
|
|
3550
|
+
const result = await options.platformAdapter.cloud.leaderboard.deleteCurrentUserEntry({
|
|
3551
|
+
...request,
|
|
3552
|
+
miniProgramId: options.identity?.miniProgramId,
|
|
3553
|
+
userId,
|
|
3554
|
+
});
|
|
3555
|
+
assertMiniProgramRuntimeRecord(result, 'cloud.leaderboard.deleteCurrentUserEntry 返回值必须是对象');
|
|
3556
|
+
if (typeof result.deleted !== 'boolean') {
|
|
3557
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.deleteCurrentUserEntry 返回的 deleted 必须是布尔值');
|
|
3558
|
+
}
|
|
3559
|
+
return result;
|
|
3560
|
+
}
|
|
3561
|
+
/** 读取排行榜基础信息。 */
|
|
3562
|
+
async function getMiniProgramRuntimeLeaderboardInfo(payload, options) {
|
|
3563
|
+
const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getInfo', 'keyOnly');
|
|
3564
|
+
const info = await options.platformAdapter.cloud.leaderboard.getInfo({
|
|
3565
|
+
...request,
|
|
3566
|
+
miniProgramId: options.identity?.miniProgramId,
|
|
3567
|
+
});
|
|
3568
|
+
assertMiniProgramRuntimeRecord(info, 'cloud.leaderboard.getInfo 返回值必须是对象');
|
|
3569
|
+
if (request.key !== undefined && info.key !== request.key) {
|
|
3570
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.getInfo 返回的 key 与请求不一致');
|
|
3571
|
+
}
|
|
3572
|
+
if (info.order !== 'asc' && info.order !== 'desc') {
|
|
3573
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.getInfo 返回的 order 必须是 asc 或 desc');
|
|
3574
|
+
}
|
|
3575
|
+
if (!Number.isInteger(info.rankLimit) || info.rankLimit < 0) {
|
|
3576
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.getInfo 返回的 rankLimit 必须是非负整数');
|
|
3577
|
+
}
|
|
3578
|
+
return info;
|
|
3579
|
+
}
|
|
3580
|
+
function readLeaderboardPayload(payload, methodName, shape) {
|
|
3581
|
+
if (payload === undefined && shape !== 'submit') {
|
|
3582
|
+
return shape === 'list' ? { limit: DEFAULT_LEADERBOARD_LIST_LIMIT } : {};
|
|
3583
|
+
}
|
|
3584
|
+
assertMiniProgramRuntimeRecord(payload, `${methodName} 参数必须是对象`);
|
|
3585
|
+
const allowedKeys = shape === 'submit' ? ['key', 'score', 'extra'] : shape === 'list' ? ['key', 'limit', 'cursor'] : ['key'];
|
|
3586
|
+
const unknownKey = Object.keys(payload).find((key) => !allowedKeys.includes(key));
|
|
3587
|
+
if (unknownKey) {
|
|
3588
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `${methodName} 不支持参数 ${unknownKey}`);
|
|
3589
|
+
}
|
|
3590
|
+
const key = payload.key === undefined ? undefined : readLeaderboardKey(payload.key, methodName);
|
|
3591
|
+
if (shape === 'keyOnly') {
|
|
3592
|
+
return key === undefined ? {} : { key };
|
|
3593
|
+
}
|
|
3594
|
+
if (shape === 'list') {
|
|
3595
|
+
return {
|
|
3596
|
+
...(key === undefined ? {} : { key }),
|
|
3597
|
+
limit: readLeaderboardListLimit(payload.limit),
|
|
3598
|
+
...(payload.cursor === undefined ? {} : { cursor: readLeaderboardCursor(payload.cursor) }),
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
const score = readLeaderboardScore(payload.score);
|
|
3602
|
+
return {
|
|
3603
|
+
...(key === undefined ? {} : { key }),
|
|
3604
|
+
score,
|
|
3605
|
+
...(payload.extra === undefined ? {} : { extra: readLeaderboardExtra(payload.extra) }),
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
function assertLeaderboardEntry(value, methodName) {
|
|
3609
|
+
assertMiniProgramRuntimeRecord(value, `${methodName} 返回的 entry 必须是对象`);
|
|
3610
|
+
const { rank, ranked, userId, score, extra, createdAt, updatedAt } = value;
|
|
3611
|
+
if (typeof ranked !== 'boolean') {
|
|
3612
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 ranked 必须是布尔值`);
|
|
3613
|
+
}
|
|
3614
|
+
const validRank = typeof rank === 'number' && Number.isInteger(rank) && (ranked ? rank > 0 : rank === 0);
|
|
3615
|
+
if (!validRank) {
|
|
3616
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 rank/ranked 不合法`);
|
|
3617
|
+
}
|
|
3618
|
+
if (typeof userId !== 'string' || !userId) {
|
|
3619
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 userId 必须是非空字符串`);
|
|
3620
|
+
}
|
|
3621
|
+
if (typeof score !== 'number' || !Number.isFinite(score)) {
|
|
3622
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 score 必须是有限数字`);
|
|
3623
|
+
}
|
|
3624
|
+
if (!isMiniProgramRuntimeRecord(extra)) {
|
|
3625
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的 extra 必须是对象`);
|
|
3626
|
+
}
|
|
3627
|
+
if (typeof createdAt !== 'number' || typeof updatedAt !== 'number' || !Number.isFinite(createdAt) || !Number.isFinite(updatedAt)) {
|
|
3628
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', `${methodName} 返回的时间字段必须是有限数字`);
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
async function readCurrentUserId(platformAdapter) {
|
|
3632
|
+
const userId = await platformAdapter.auth.getCurrentUserId();
|
|
3633
|
+
if (userId === undefined || userId === null || `${userId}`.trim() === '') {
|
|
3634
|
+
throw createMiniProgramRuntimeBridgeError('UNAUTHORIZED', '当前用户未登录');
|
|
3635
|
+
}
|
|
3636
|
+
return `${userId}`;
|
|
3637
|
+
}
|
|
3638
|
+
function readLeaderboardKey(value, methodName) {
|
|
3639
|
+
if (typeof value !== 'string' || !LEADERBOARD_KEY_RE.test(value)) {
|
|
3640
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `${methodName} key 仅支持 1-64 位字母、数字、下划线和连字符`);
|
|
3641
|
+
}
|
|
3642
|
+
return value;
|
|
3643
|
+
}
|
|
3644
|
+
function readLeaderboardExtra(value) {
|
|
3645
|
+
if (!isMiniProgramRuntimeRecord(value)) {
|
|
3646
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'cloud.leaderboard.submit extra 必须是对象');
|
|
3647
|
+
}
|
|
3648
|
+
let serialized;
|
|
3649
|
+
try {
|
|
3650
|
+
serialized = JSON.stringify(value);
|
|
3651
|
+
}
|
|
3652
|
+
catch {
|
|
3653
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'cloud.leaderboard.submit extra 必须可序列化');
|
|
3654
|
+
}
|
|
3655
|
+
if (typeof serialized !== 'string') {
|
|
3656
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'cloud.leaderboard.submit extra 必须可序列化');
|
|
3657
|
+
}
|
|
3658
|
+
if (new TextEncoder().encode(serialized).length > MAX_LEADERBOARD_EXTRA_BYTES) {
|
|
3659
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `cloud.leaderboard.submit extra 不能超过 ${MAX_LEADERBOARD_EXTRA_BYTES} 字节`);
|
|
3660
|
+
}
|
|
3661
|
+
return value;
|
|
3662
|
+
}
|
|
3663
|
+
function readLeaderboardScore(value) {
|
|
3664
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || Math.abs(value) > MAX_LEADERBOARD_SAFE_SCORE) {
|
|
3665
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'cloud.leaderboard.submit score 必须是有限安全数字');
|
|
3666
|
+
}
|
|
3667
|
+
return value;
|
|
3668
|
+
}
|
|
3669
|
+
function readLeaderboardListLimit(value) {
|
|
3670
|
+
if (value === undefined) {
|
|
3671
|
+
return DEFAULT_LEADERBOARD_LIST_LIMIT;
|
|
3672
|
+
}
|
|
3673
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0 || value > MAX_LEADERBOARD_LIST_LIMIT) {
|
|
3674
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'cloud.leaderboard.getList limit 必须是 1-100 的整数');
|
|
3675
|
+
}
|
|
3676
|
+
return value;
|
|
3677
|
+
}
|
|
3678
|
+
function readLeaderboardCursor(value) {
|
|
3679
|
+
if (typeof value !== 'string') {
|
|
3680
|
+
throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'cloud.leaderboard.getList cursor 必须是字符串');
|
|
3681
|
+
}
|
|
3682
|
+
return value;
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3450
3685
|
const GAME_DETAIL_GAME_TYPES = ['pc', 'console', 'mobile'];
|
|
3451
3686
|
const GAME_DETAIL_PAGES = ['game', 'wiki'];
|
|
3452
3687
|
function toOpenAppPagePlatformOptions(payload) {
|
|
@@ -4703,6 +4938,9 @@ function isNavigationBarForegroundStyle(value) {
|
|
|
4703
4938
|
return value === 'light' || value === 'dark';
|
|
4704
4939
|
}
|
|
4705
4940
|
|
|
4941
|
+
const MOCK_DEFAULT_LEADERBOARD_KEY = 'default';
|
|
4942
|
+
const MOCK_LEADERBOARD_CACHE_MAX_ENTRIES = 500;
|
|
4943
|
+
const MOCK_LEADERBOARD_CURRENT_ENTRY_RANK_COUNT_LIMIT = 5000;
|
|
4706
4944
|
const MINI_PROGRAM_MOCK_RUNTIME_METHODS = MINI_PROGRAM_PROTOCOL_CAPABILITIES.map((capability) => capability.method);
|
|
4707
4945
|
const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS = {
|
|
4708
4946
|
[AUTH_LOGIN_METHOD]: (runtime) => runtime.login(),
|
|
@@ -4719,6 +4957,11 @@ const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS = {
|
|
|
4719
4957
|
[STORAGE_GET_STORAGE_METHOD]: (runtime, payload) => runtime.getStorage(payload),
|
|
4720
4958
|
[STORAGE_SET_STORAGE_METHOD]: (runtime, payload) => runtime.setStorage(payload),
|
|
4721
4959
|
[NETWORK_REQUEST_METHOD]: (runtime, payload) => runtime.requestNetwork(payload),
|
|
4960
|
+
[CLOUD_LEADERBOARD_SUBMIT_METHOD]: (runtime, payload) => runtime.submitLeaderboardEntry(payload),
|
|
4961
|
+
[CLOUD_LEADERBOARD_GET_LIST_METHOD]: (runtime, payload) => runtime.getLeaderboardList(payload),
|
|
4962
|
+
[CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD]: (runtime, payload) => runtime.getCurrentUserLeaderboardEntry(payload),
|
|
4963
|
+
[CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD]: (runtime, payload) => runtime.deleteCurrentUserLeaderboardEntry(payload),
|
|
4964
|
+
[CLOUD_LEADERBOARD_GET_INFO_METHOD]: (runtime, payload) => runtime.getLeaderboardInfo(payload),
|
|
4722
4965
|
[UI_SHOW_TOAST_METHOD]: (runtime, payload) => runtime.showToast(payload),
|
|
4723
4966
|
[UI_SHOW_LOADING_METHOD]: (runtime, payload) => runtime.showLoading(payload),
|
|
4724
4967
|
[UI_HIDE_LOADING_METHOD]: (runtime, payload) => runtime.hideLoading(payload),
|
|
@@ -4729,6 +4972,7 @@ const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS = {
|
|
|
4729
4972
|
[NAVIGATION_OPEN_APP_PAGE_METHOD]: (runtime, payload) => runtime.openAppPage(payload),
|
|
4730
4973
|
};
|
|
4731
4974
|
function createBrowserMockRuntimePlatformAdapter(options) {
|
|
4975
|
+
const leaderboards = createMockLeaderboardBoards(options.leaderboards);
|
|
4732
4976
|
return {
|
|
4733
4977
|
app: {
|
|
4734
4978
|
getCurrentHref: options.getCurrentHref,
|
|
@@ -4844,6 +5088,31 @@ function createBrowserMockRuntimePlatformAdapter(options) {
|
|
|
4844
5088
|
network: {
|
|
4845
5089
|
request: options.requestNetwork,
|
|
4846
5090
|
},
|
|
5091
|
+
cloud: {
|
|
5092
|
+
leaderboard: {
|
|
5093
|
+
async submit(request) {
|
|
5094
|
+
return getMockLeaderboardBoard(leaderboards, resolveMockLeaderboardKey(request.key)).submit(request.userId, request.score, request.extra);
|
|
5095
|
+
},
|
|
5096
|
+
async getList(request) {
|
|
5097
|
+
return getMockLeaderboardBoard(leaderboards, resolveMockLeaderboardKey(request.key)).getList(request.limit, request.cursor);
|
|
5098
|
+
},
|
|
5099
|
+
async getCurrentUserEntry(request) {
|
|
5100
|
+
return getMockLeaderboardBoard(leaderboards, resolveMockLeaderboardKey(request.key)).getCurrentUserEntry(request.userId);
|
|
5101
|
+
},
|
|
5102
|
+
async deleteCurrentUserEntry(request) {
|
|
5103
|
+
return getMockLeaderboardBoard(leaderboards, resolveMockLeaderboardKey(request.key)).deleteCurrentUserEntry(request.userId);
|
|
5104
|
+
},
|
|
5105
|
+
async getInfo(request) {
|
|
5106
|
+
const key = resolveMockLeaderboardKey(request.key);
|
|
5107
|
+
const board = getMockLeaderboardBoard(leaderboards, key);
|
|
5108
|
+
return {
|
|
5109
|
+
key,
|
|
5110
|
+
order: board.order,
|
|
5111
|
+
rankLimit: board.rankLimit,
|
|
5112
|
+
};
|
|
5113
|
+
},
|
|
5114
|
+
},
|
|
5115
|
+
},
|
|
4847
5116
|
};
|
|
4848
5117
|
}
|
|
4849
5118
|
class MiniProgramMockRuntime {
|
|
@@ -4970,6 +5239,21 @@ class MiniProgramMockRuntime {
|
|
|
4970
5239
|
requestNetwork(payload) {
|
|
4971
5240
|
return requestMiniProgramRuntimeNetwork(payload, this.adapter);
|
|
4972
5241
|
}
|
|
5242
|
+
submitLeaderboardEntry(payload) {
|
|
5243
|
+
return submitMiniProgramRuntimeLeaderboardEntry(payload, { platformAdapter: this.adapter });
|
|
5244
|
+
}
|
|
5245
|
+
getLeaderboardList(payload) {
|
|
5246
|
+
return getMiniProgramRuntimeLeaderboardList(payload, { platformAdapter: this.adapter });
|
|
5247
|
+
}
|
|
5248
|
+
getCurrentUserLeaderboardEntry(payload) {
|
|
5249
|
+
return getMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, { platformAdapter: this.adapter });
|
|
5250
|
+
}
|
|
5251
|
+
deleteCurrentUserLeaderboardEntry(payload) {
|
|
5252
|
+
return deleteMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, { platformAdapter: this.adapter });
|
|
5253
|
+
}
|
|
5254
|
+
getLeaderboardInfo(payload) {
|
|
5255
|
+
return getMiniProgramRuntimeLeaderboardInfo(payload, { platformAdapter: this.adapter });
|
|
5256
|
+
}
|
|
4973
5257
|
async showToast(payload) {
|
|
4974
5258
|
const toast = readShowToastPayload(payload);
|
|
4975
5259
|
this.toasts.unshift(toast);
|
|
@@ -5263,6 +5547,312 @@ function createVibratePattern(intensity) {
|
|
|
5263
5547
|
return 10;
|
|
5264
5548
|
}
|
|
5265
5549
|
}
|
|
5550
|
+
function resolveMockLeaderboardKey(key) {
|
|
5551
|
+
return key || MOCK_DEFAULT_LEADERBOARD_KEY;
|
|
5552
|
+
}
|
|
5553
|
+
function createMockLeaderboardBoards(configs = []) {
|
|
5554
|
+
return configs.reduce((boards, config) => {
|
|
5555
|
+
const key = resolveMockLeaderboardKey(config.key);
|
|
5556
|
+
boards.set(key, new MockLeaderboardBoard({
|
|
5557
|
+
...config,
|
|
5558
|
+
key,
|
|
5559
|
+
}));
|
|
5560
|
+
return boards;
|
|
5561
|
+
}, new Map());
|
|
5562
|
+
}
|
|
5563
|
+
function getMockLeaderboardBoard(boards, key) {
|
|
5564
|
+
const board = boards.get(key);
|
|
5565
|
+
if (board) {
|
|
5566
|
+
return board;
|
|
5567
|
+
}
|
|
5568
|
+
if (key === MOCK_DEFAULT_LEADERBOARD_KEY) {
|
|
5569
|
+
throw createMockBridgeError('LEADERBOARD_DEFAULT_NOT_FOUND', '默认排行榜不存在');
|
|
5570
|
+
}
|
|
5571
|
+
throw createMockBridgeError('NotFound', '排行榜不存在');
|
|
5572
|
+
}
|
|
5573
|
+
class MockLeaderboardBoard {
|
|
5574
|
+
constructor(config) {
|
|
5575
|
+
this.entries = new Map();
|
|
5576
|
+
this.key = config.key;
|
|
5577
|
+
this.order = readMockLeaderboardOrder(config.order);
|
|
5578
|
+
this.rankLimit = readMockLeaderboardRankLimit(config.rankLimit);
|
|
5579
|
+
config.entries?.forEach(entry => {
|
|
5580
|
+
const now = Date.now();
|
|
5581
|
+
this.entries.set(entry.userId, {
|
|
5582
|
+
userId: readMockLeaderboardUserId(entry.userId),
|
|
5583
|
+
score: readMockLeaderboardScore(entry.score),
|
|
5584
|
+
extra: cloneMockRecord(entry.extra || {}),
|
|
5585
|
+
createdAt: entry.createdAt ?? now,
|
|
5586
|
+
updatedAt: entry.updatedAt ?? entry.createdAt ?? now,
|
|
5587
|
+
});
|
|
5588
|
+
});
|
|
5589
|
+
}
|
|
5590
|
+
submit(userId, score, extra) {
|
|
5591
|
+
const now = Date.now();
|
|
5592
|
+
const existing = this.entries.get(userId);
|
|
5593
|
+
if (existing && !this.isBetterScore(score, existing.score)) {
|
|
5594
|
+
return this.getCurrentUserEntry(userId);
|
|
5595
|
+
}
|
|
5596
|
+
this.entries.set(userId, {
|
|
5597
|
+
userId,
|
|
5598
|
+
score,
|
|
5599
|
+
extra: extra === undefined ? cloneMockRecord(existing?.extra || {}) : cloneMockRecord(extra),
|
|
5600
|
+
createdAt: existing?.createdAt ?? now,
|
|
5601
|
+
updatedAt: now,
|
|
5602
|
+
});
|
|
5603
|
+
this.clearListCache();
|
|
5604
|
+
return this.getCurrentUserEntry(userId);
|
|
5605
|
+
}
|
|
5606
|
+
getList(limit, cursor) {
|
|
5607
|
+
const listCursor = this.readListCursor(cursor);
|
|
5608
|
+
const startRank = listCursor ? listCursor.rank + 1 : 1;
|
|
5609
|
+
if (this.rankLimit > 0 && startRank > this.rankLimit) {
|
|
5610
|
+
return {
|
|
5611
|
+
entries: [],
|
|
5612
|
+
hasMore: false,
|
|
5613
|
+
};
|
|
5614
|
+
}
|
|
5615
|
+
const cachedResponse = this.getCachedListResponse(listCursor, limit);
|
|
5616
|
+
if (cachedResponse) {
|
|
5617
|
+
return cachedResponse;
|
|
5618
|
+
}
|
|
5619
|
+
if (!listCursor) {
|
|
5620
|
+
this.refreshListCache();
|
|
5621
|
+
const freshCachedResponse = this.getCachedListResponse(undefined, limit);
|
|
5622
|
+
if (freshCachedResponse) {
|
|
5623
|
+
return freshCachedResponse;
|
|
5624
|
+
}
|
|
5625
|
+
}
|
|
5626
|
+
const entriesAfterCursor = this.getListEntries()
|
|
5627
|
+
.filter(entry => listCursor ? this.isAfterCursor(entry, listCursor) : true);
|
|
5628
|
+
const entries = entriesAfterCursor.slice(0, limit);
|
|
5629
|
+
const hasMore = entries.length < entriesAfterCursor.length;
|
|
5630
|
+
return this.createListResponse(entries, startRank, hasMore);
|
|
5631
|
+
}
|
|
5632
|
+
getCurrentUserEntry(userId) {
|
|
5633
|
+
const orderedEntries = this.getOrderedEntries();
|
|
5634
|
+
const index = orderedEntries.findIndex(entry => entry.userId === userId);
|
|
5635
|
+
if (index < 0) {
|
|
5636
|
+
return undefined;
|
|
5637
|
+
}
|
|
5638
|
+
const rank = index + 1;
|
|
5639
|
+
const ranked = this.isCurrentUserRanked(rank);
|
|
5640
|
+
const entry = orderedEntries[index];
|
|
5641
|
+
return {
|
|
5642
|
+
rank: ranked ? rank : 0,
|
|
5643
|
+
ranked,
|
|
5644
|
+
...entry,
|
|
5645
|
+
extra: cloneMockRecord(entry.extra),
|
|
5646
|
+
};
|
|
5647
|
+
}
|
|
5648
|
+
deleteCurrentUserEntry(userId) {
|
|
5649
|
+
const deleted = this.entries.delete(userId);
|
|
5650
|
+
if (deleted) {
|
|
5651
|
+
this.clearListCache();
|
|
5652
|
+
}
|
|
5653
|
+
return {
|
|
5654
|
+
deleted,
|
|
5655
|
+
};
|
|
5656
|
+
}
|
|
5657
|
+
getCachedListResponse(cursor, limit) {
|
|
5658
|
+
if (!this.cachedList) {
|
|
5659
|
+
return undefined;
|
|
5660
|
+
}
|
|
5661
|
+
let start = 0;
|
|
5662
|
+
let startRank = 1;
|
|
5663
|
+
if (cursor) {
|
|
5664
|
+
const cursorIndex = this.cachedList.entries.findIndex(entry => (entry.score === cursor.score &&
|
|
5665
|
+
entry.updatedAt === cursor.update_time &&
|
|
5666
|
+
entry.userId === cursor.user_id));
|
|
5667
|
+
if (cursorIndex < 0) {
|
|
5668
|
+
return undefined;
|
|
5669
|
+
}
|
|
5670
|
+
start = cursorIndex + 1;
|
|
5671
|
+
startRank = cursor.rank + 1;
|
|
5672
|
+
}
|
|
5673
|
+
if (start >= this.cachedList.entries.length) {
|
|
5674
|
+
return this.cachedList.complete
|
|
5675
|
+
? {
|
|
5676
|
+
entries: [],
|
|
5677
|
+
hasMore: false,
|
|
5678
|
+
}
|
|
5679
|
+
: undefined;
|
|
5680
|
+
}
|
|
5681
|
+
const end = Math.min(start + limit, this.cachedList.entries.length);
|
|
5682
|
+
const hasMore = end < this.cachedList.entries.length || !this.cachedList.complete;
|
|
5683
|
+
return this.createListResponse(this.cachedList.entries.slice(start, end), startRank, hasMore);
|
|
5684
|
+
}
|
|
5685
|
+
refreshListCache() {
|
|
5686
|
+
const cacheLimit = this.rankLimit > 0 && this.rankLimit < MOCK_LEADERBOARD_CACHE_MAX_ENTRIES
|
|
5687
|
+
? this.rankLimit
|
|
5688
|
+
: MOCK_LEADERBOARD_CACHE_MAX_ENTRIES;
|
|
5689
|
+
const rankedEntries = this.getListEntries();
|
|
5690
|
+
this.cachedList = {
|
|
5691
|
+
entries: rankedEntries.slice(0, cacheLimit).map(entry => ({
|
|
5692
|
+
...entry,
|
|
5693
|
+
extra: cloneMockRecord(entry.extra),
|
|
5694
|
+
})),
|
|
5695
|
+
complete: rankedEntries.length <= cacheLimit,
|
|
5696
|
+
};
|
|
5697
|
+
}
|
|
5698
|
+
clearListCache() {
|
|
5699
|
+
this.cachedList = undefined;
|
|
5700
|
+
}
|
|
5701
|
+
createListResponse(entries, startRank, hasMore) {
|
|
5702
|
+
const pageEntries = entries.map((entry, index) => ({
|
|
5703
|
+
rank: startRank + index,
|
|
5704
|
+
ranked: true,
|
|
5705
|
+
...entry,
|
|
5706
|
+
extra: cloneMockRecord(entry.extra),
|
|
5707
|
+
}));
|
|
5708
|
+
return {
|
|
5709
|
+
entries: pageEntries,
|
|
5710
|
+
...(hasMore && pageEntries.length > 0 ? { cursor: this.createListCursor(pageEntries[pageEntries.length - 1]) } : {}),
|
|
5711
|
+
hasMore,
|
|
5712
|
+
};
|
|
5713
|
+
}
|
|
5714
|
+
getOrderedEntries() {
|
|
5715
|
+
return Array.from(this.entries.values())
|
|
5716
|
+
.sort((left, right) => {
|
|
5717
|
+
if (left.score !== right.score) {
|
|
5718
|
+
return this.order === 'asc' ? left.score - right.score : right.score - left.score;
|
|
5719
|
+
}
|
|
5720
|
+
if (left.updatedAt !== right.updatedAt) {
|
|
5721
|
+
return left.updatedAt - right.updatedAt;
|
|
5722
|
+
}
|
|
5723
|
+
return left.userId.localeCompare(right.userId);
|
|
5724
|
+
})
|
|
5725
|
+
.map(entry => ({
|
|
5726
|
+
...entry,
|
|
5727
|
+
extra: cloneMockRecord(entry.extra),
|
|
5728
|
+
}));
|
|
5729
|
+
}
|
|
5730
|
+
getListEntries() {
|
|
5731
|
+
return this.getOrderedEntries().filter((_entry, index) => this.isWithinRankLimit(index + 1));
|
|
5732
|
+
}
|
|
5733
|
+
isBetterScore(nextScore, currentScore) {
|
|
5734
|
+
return this.order === 'desc' ? nextScore > currentScore : nextScore < currentScore;
|
|
5735
|
+
}
|
|
5736
|
+
readListCursor(cursor) {
|
|
5737
|
+
const raw = cursor?.trim();
|
|
5738
|
+
if (!raw) {
|
|
5739
|
+
return undefined;
|
|
5740
|
+
}
|
|
5741
|
+
let parsed;
|
|
5742
|
+
try {
|
|
5743
|
+
parsed = JSON.parse(decodeMockLeaderboardCursor(raw));
|
|
5744
|
+
}
|
|
5745
|
+
catch {
|
|
5746
|
+
throw createMockBridgeError('INVALID_PARAMS', 'cloud.leaderboard.getList cursor 不合法');
|
|
5747
|
+
}
|
|
5748
|
+
if (!isPlainRecord(parsed)) {
|
|
5749
|
+
throw createMockBridgeError('INVALID_PARAMS', 'cloud.leaderboard.getList cursor 不合法');
|
|
5750
|
+
}
|
|
5751
|
+
const listCursor = parsed;
|
|
5752
|
+
const isValid = typeof listCursor.rank === 'number' &&
|
|
5753
|
+
Number.isSafeInteger(listCursor.rank) &&
|
|
5754
|
+
listCursor.rank >= 1 &&
|
|
5755
|
+
typeof listCursor.user_id === 'string' &&
|
|
5756
|
+
listCursor.user_id !== '' &&
|
|
5757
|
+
typeof listCursor.update_time === 'number' &&
|
|
5758
|
+
Number.isFinite(listCursor.update_time) &&
|
|
5759
|
+
listCursor.update_time > 0 &&
|
|
5760
|
+
typeof listCursor.score === 'number' &&
|
|
5761
|
+
Number.isFinite(listCursor.score);
|
|
5762
|
+
if (!isValid) {
|
|
5763
|
+
throw createMockBridgeError('INVALID_PARAMS', 'cloud.leaderboard.getList cursor 不合法');
|
|
5764
|
+
}
|
|
5765
|
+
return listCursor;
|
|
5766
|
+
}
|
|
5767
|
+
createListCursor(entry) {
|
|
5768
|
+
return encodeMockLeaderboardCursor(JSON.stringify({
|
|
5769
|
+
rank: entry.rank,
|
|
5770
|
+
score: entry.score,
|
|
5771
|
+
update_time: entry.updatedAt,
|
|
5772
|
+
user_id: entry.userId,
|
|
5773
|
+
}));
|
|
5774
|
+
}
|
|
5775
|
+
isAfterCursor(entry, cursor) {
|
|
5776
|
+
if (entry.score !== cursor.score) {
|
|
5777
|
+
return this.order === 'asc' ? entry.score > cursor.score : entry.score < cursor.score;
|
|
5778
|
+
}
|
|
5779
|
+
if (entry.updatedAt !== cursor.update_time) {
|
|
5780
|
+
return entry.updatedAt > cursor.update_time;
|
|
5781
|
+
}
|
|
5782
|
+
return entry.userId.localeCompare(cursor.user_id) > 0;
|
|
5783
|
+
}
|
|
5784
|
+
isWithinRankLimit(rank) {
|
|
5785
|
+
return this.rankLimit <= 0 || rank <= this.rankLimit;
|
|
5786
|
+
}
|
|
5787
|
+
isCurrentUserRanked(rank) {
|
|
5788
|
+
return rank <= this.getCurrentUserRankCountLimit();
|
|
5789
|
+
}
|
|
5790
|
+
getCurrentUserRankCountLimit() {
|
|
5791
|
+
return this.rankLimit > 0 && this.rankLimit < MOCK_LEADERBOARD_CURRENT_ENTRY_RANK_COUNT_LIMIT
|
|
5792
|
+
? this.rankLimit
|
|
5793
|
+
: MOCK_LEADERBOARD_CURRENT_ENTRY_RANK_COUNT_LIMIT;
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
function readMockLeaderboardOrder(value) {
|
|
5797
|
+
if (value === undefined) {
|
|
5798
|
+
return 'desc';
|
|
5799
|
+
}
|
|
5800
|
+
if (value !== 'asc' && value !== 'desc') {
|
|
5801
|
+
throw createMockBridgeError('INVALID_PARAMS', 'mock leaderboard order 必须是 asc 或 desc');
|
|
5802
|
+
}
|
|
5803
|
+
return value;
|
|
5804
|
+
}
|
|
5805
|
+
function readMockLeaderboardRankLimit(value) {
|
|
5806
|
+
if (value === undefined) {
|
|
5807
|
+
return 0;
|
|
5808
|
+
}
|
|
5809
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
|
|
5810
|
+
throw createMockBridgeError('INVALID_PARAMS', 'mock leaderboard rankLimit 必须是非负整数');
|
|
5811
|
+
}
|
|
5812
|
+
return value;
|
|
5813
|
+
}
|
|
5814
|
+
function readMockLeaderboardUserId(value) {
|
|
5815
|
+
if (typeof value !== 'string' || !value) {
|
|
5816
|
+
throw createMockBridgeError('INVALID_PARAMS', 'mock leaderboard entry userId 必须是非空字符串');
|
|
5817
|
+
}
|
|
5818
|
+
return value;
|
|
5819
|
+
}
|
|
5820
|
+
function readMockLeaderboardScore(value) {
|
|
5821
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
5822
|
+
throw createMockBridgeError('INVALID_PARAMS', 'mock leaderboard entry score 必须是有限数字');
|
|
5823
|
+
}
|
|
5824
|
+
return value;
|
|
5825
|
+
}
|
|
5826
|
+
function cloneMockRecord(record) {
|
|
5827
|
+
try {
|
|
5828
|
+
return JSON.parse(JSON.stringify(record));
|
|
5829
|
+
}
|
|
5830
|
+
catch {
|
|
5831
|
+
return { ...record };
|
|
5832
|
+
}
|
|
5833
|
+
}
|
|
5834
|
+
function encodeMockLeaderboardCursor(value) {
|
|
5835
|
+
const bytes = new TextEncoder().encode(value);
|
|
5836
|
+
let binary = '';
|
|
5837
|
+
bytes.forEach((byte) => {
|
|
5838
|
+
binary += String.fromCharCode(byte);
|
|
5839
|
+
});
|
|
5840
|
+
if (typeof btoa !== 'function') {
|
|
5841
|
+
throw createMockBridgeError('MOCK_RUNTIME_ERROR', '当前环境不支持排行榜 cursor 编码');
|
|
5842
|
+
}
|
|
5843
|
+
const base64 = btoa(binary);
|
|
5844
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
5845
|
+
}
|
|
5846
|
+
function decodeMockLeaderboardCursor(value) {
|
|
5847
|
+
const base64 = value.replace(/-/g, '+').replace(/_/g, '/');
|
|
5848
|
+
const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');
|
|
5849
|
+
if (typeof atob !== 'function') {
|
|
5850
|
+
throw createMockBridgeError('INVALID_PARAMS', 'cloud.leaderboard.getList cursor 不合法');
|
|
5851
|
+
}
|
|
5852
|
+
const binary = atob(padded);
|
|
5853
|
+
const bytes = Uint8Array.from(binary, char => char.charCodeAt(0));
|
|
5854
|
+
return new TextDecoder().decode(bytes);
|
|
5855
|
+
}
|
|
5266
5856
|
function createMockRuntimeUserHomeV2Result(userInfo) {
|
|
5267
5857
|
const heyboxId = userInfo.heybox_id || 'debug_user';
|
|
5268
5858
|
const nickname = userInfo.nickname || 'PC Debug User';
|