@heybox/hb-sdk 0.6.9 → 0.7.0-alpha.2
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 +51 -7
- package/README.md +83 -20
- package/dist/cli-chunks/{build-BtGxhrcV.cjs → build-DLIhlBQA.cjs} +4 -4
- package/dist/cli-chunks/{context-DX1KG-8A.cjs → context-Bl_Q4RKq.cjs} +15 -2
- package/dist/cli-chunks/{create-DAD0OF1r.cjs → create-C--pwY0l.cjs} +1 -1
- package/dist/cli-chunks/{dev-Dh4HY0I8.cjs → dev-B1tyT5ql.cjs} +197 -13
- package/dist/cli-chunks/{doctor-BfKdiD3H.cjs → doctor-D9M2scaJ.cjs} +1 -1
- package/dist/cli-chunks/{index-tCURDEx8.cjs → index-BnBeTd0B.cjs} +15 -14
- package/dist/cli-chunks/{index-DB6Q8HaC.cjs → index-HNB8i51C.cjs} +2 -2
- package/dist/cli-chunks/{login-D60EJLgs.cjs → login-DnX81kof.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-DAUaP0_3.cjs → project-vite-CgnCpNDZ.cjs} +1 -1
- package/dist/cli-chunks/{remote-nQDniAUY.cjs → remote-CETqg77E.cjs} +6 -6
- package/dist/cli-chunks/{runtime-gate-BWlU-R4h.cjs → runtime-gate-CtV7rWyX.cjs} +1 -1
- package/dist/cli-chunks/{runtime-permission-env-DKrhgVM3.cjs → runtime-permission-env-D-8_jPG3.cjs} +2 -0
- package/dist/cli-chunks/{session-D6jOKXeu.cjs → session-Davw2CMd.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 +426 -802
- package/dist/index.cjs.js +46 -90
- package/dist/index.esm.js +46 -90
- package/dist/miniapp-publish.cjs.js +2 -0
- package/dist/miniapp-publish.esm.js +2 -1
- 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/miniapp-publish/index.d.ts +1 -0
- 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
package/dist/protocol.cjs.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/** 小程序沙盒通信命名空间,父容器与 iframe 内 SDK 必须保持一致。 */
|
|
4
4
|
const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
|
|
5
5
|
/** 小程序沙盒通信协议版本。 */
|
|
6
|
-
const MINI_PROGRAM_MESSAGE_VERSION =
|
|
6
|
+
const MINI_PROGRAM_MESSAGE_VERSION = 2;
|
|
7
7
|
/** 父容器注入到小程序 URL 上的通信 nonce 参数名。 */
|
|
8
8
|
const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
9
9
|
/** SDK 初始化时发给父容器的握手方法名。 */
|
|
@@ -27,7 +27,7 @@ function isMiniProgramBridgeMessage(value) {
|
|
|
27
27
|
}
|
|
28
28
|
const message = value;
|
|
29
29
|
return (message.namespace === MINI_PROGRAM_MESSAGE_NAMESPACE &&
|
|
30
|
-
message.version ===
|
|
30
|
+
(message.version === 1 || message.version === 2) &&
|
|
31
31
|
typeof message.nonce === 'string' &&
|
|
32
32
|
typeof message.type === 'string');
|
|
33
33
|
}
|
|
@@ -84,29 +84,97 @@ function isRecord(value) {
|
|
|
84
84
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
const OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS = [
|
|
88
|
+
'xiaoheihe.cn',
|
|
89
|
+
'max-c.com',
|
|
90
|
+
'debugmode.cn',
|
|
91
|
+
'maxjia.com',
|
|
92
|
+
];
|
|
93
|
+
/** Runtime / Host 在 `network.request` 分发前拒绝的小程序服务端私有路径前缀。 */
|
|
94
|
+
const MINI_PROGRAM_NETWORK_PROTECTED_PATHS = [
|
|
95
|
+
'/user_miniprogram/runtime/auth',
|
|
96
|
+
'/user_miniprogram/openapi/v1',
|
|
97
|
+
];
|
|
98
|
+
const NETWORK_REQUEST_PATHNAME_MAX_DECODE_ROUNDS = 4;
|
|
99
|
+
/** 使用 URL 解析后的 hostname 判断黑盒官方根域及其子域。 */
|
|
100
|
+
function isOfficialMiniProgramNetworkUrl(value) {
|
|
101
|
+
try {
|
|
102
|
+
const url = new URL(value);
|
|
103
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
|
|
107
|
+
return OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS.some(rootDomain => hostname === rootDomain || hostname.endsWith(`.${rootDomain}`));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
87
113
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* @remarks
|
|
91
|
-
* 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
|
|
114
|
+
* 规范化并判断 pathname 是否命中 Runtime Auth / OpenAPI 保护前缀。
|
|
115
|
+
* 解码失败或仍可继续解码时视为不安全路径。
|
|
92
116
|
*/
|
|
93
|
-
|
|
117
|
+
function isProtectedMiniProgramNetworkPath(pathname) {
|
|
118
|
+
const normalized = normalizeMiniProgramNetworkPathname(pathname);
|
|
119
|
+
if (normalized === null) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return MINI_PROGRAM_NETWORK_PROTECTED_PATHS.some(prefix => normalized === prefix || normalized.startsWith(`${prefix}/`));
|
|
123
|
+
}
|
|
124
|
+
function normalizeMiniProgramNetworkPathname(pathname) {
|
|
125
|
+
let decoded = pathname;
|
|
126
|
+
for (let round = 0; round < NETWORK_REQUEST_PATHNAME_MAX_DECODE_ROUNDS; round += 1) {
|
|
127
|
+
let next;
|
|
128
|
+
try {
|
|
129
|
+
next = decodeURIComponent(decoded);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
if (next === decoded) {
|
|
135
|
+
return normalizeDecodedNetworkPathname(decoded);
|
|
136
|
+
}
|
|
137
|
+
decoded = next;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
if (decodeURIComponent(decoded) !== decoded) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return normalizeDecodedNetworkPathname(decoded);
|
|
148
|
+
}
|
|
149
|
+
function normalizeDecodedNetworkPathname(decoded) {
|
|
150
|
+
const segments = [];
|
|
151
|
+
for (const segment of decoded.replace(/\\/g, '/').split('/')) {
|
|
152
|
+
if (!segment || segment === '.') {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (segment === '..') {
|
|
156
|
+
segments.pop();
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
segments.push(segment);
|
|
160
|
+
}
|
|
161
|
+
return `/${segments.join('/')}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
94
164
|
/**
|
|
95
|
-
*
|
|
165
|
+
* 登录授权能力方法名。
|
|
96
166
|
*
|
|
97
167
|
* @remarks
|
|
98
168
|
* 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
|
|
99
169
|
*/
|
|
170
|
+
const AUTH_LOGIN_METHOD = 'auth.login';
|
|
171
|
+
/** 读取当前登录态、授权状态和已获准披露资料的 bridge method 标识。 */
|
|
100
172
|
const USER_GET_INFO_METHOD = 'user.getInfo';
|
|
101
|
-
/**
|
|
102
|
-
const
|
|
103
|
-
/**
|
|
104
|
-
const
|
|
105
|
-
/**
|
|
106
|
-
const USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD = 'user.getPlatformAccountOverview';
|
|
107
|
-
/** 当前用户指定平台账号详情能力方法名。 */
|
|
108
|
-
const USER_GET_PLATFORM_ACCOUNT_INFO_METHOD = 'user.getPlatformAccountInfo';
|
|
109
|
-
/** 当前用户 Steam 游戏库能力方法名。 */
|
|
173
|
+
/** 读取当前用户小程序隔离身份的 bridge method 标识。 */
|
|
174
|
+
const USER_GET_LOCAL_IDENTITY_METHOD = 'user.getLocalIdentity';
|
|
175
|
+
/** 撤销当前用户授予本小程序授权的 bridge method 标识。 */
|
|
176
|
+
const USER_REVOKE_AUTHORIZATION_METHOD = 'user.revokeAuthorization';
|
|
177
|
+
/** 读取当前已登录用户 Steam 游戏库的 bridge method 标识。 */
|
|
110
178
|
const USER_GET_STEAM_GAME_LIST_METHOD = 'user.getSteamGameList';
|
|
111
179
|
/**
|
|
112
180
|
* 展示分享面板能力方法名。
|
|
@@ -205,38 +273,24 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
|
205
273
|
risk: 'medium',
|
|
206
274
|
},
|
|
207
275
|
{
|
|
208
|
-
method:
|
|
209
|
-
module: 'user',
|
|
210
|
-
capability: USER_GET_CURRENT_USER_DETAIL_METHOD,
|
|
211
|
-
permission: 'user.currentUserDetail',
|
|
212
|
-
risk: 'high',
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
method: USER_GET_CURRENT_USER_PROFILE_METHOD,
|
|
216
|
-
module: 'user',
|
|
217
|
-
capability: USER_GET_CURRENT_USER_PROFILE_METHOD,
|
|
218
|
-
permission: 'user.currentUserProfile',
|
|
219
|
-
risk: 'high',
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
method: USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD,
|
|
276
|
+
method: USER_GET_LOCAL_IDENTITY_METHOD,
|
|
223
277
|
module: 'user',
|
|
224
|
-
capability:
|
|
225
|
-
permission: 'user.
|
|
278
|
+
capability: USER_GET_LOCAL_IDENTITY_METHOD,
|
|
279
|
+
permission: 'user.getLocalIdentity',
|
|
226
280
|
risk: 'medium',
|
|
227
281
|
},
|
|
228
282
|
{
|
|
229
|
-
method:
|
|
283
|
+
method: USER_REVOKE_AUTHORIZATION_METHOD,
|
|
230
284
|
module: 'user',
|
|
231
|
-
capability:
|
|
232
|
-
permission: 'user.
|
|
285
|
+
capability: USER_REVOKE_AUTHORIZATION_METHOD,
|
|
286
|
+
permission: 'user.revokeAuthorization',
|
|
233
287
|
risk: 'high',
|
|
234
288
|
},
|
|
235
289
|
{
|
|
236
290
|
method: USER_GET_STEAM_GAME_LIST_METHOD,
|
|
237
291
|
module: 'user',
|
|
238
292
|
capability: USER_GET_STEAM_GAME_LIST_METHOD,
|
|
239
|
-
permission: 'user.
|
|
293
|
+
permission: 'user.platformAccount.steam',
|
|
240
294
|
risk: 'high',
|
|
241
295
|
},
|
|
242
296
|
{
|
|
@@ -392,6 +446,7 @@ exports.DEVICE_VIBRATE_METHOD = DEVICE_VIBRATE_METHOD;
|
|
|
392
446
|
exports.MINI_PROGRAM_BRIDGE_NONCE_PARAM = MINI_PROGRAM_BRIDGE_NONCE_PARAM;
|
|
393
447
|
exports.MINI_PROGRAM_MESSAGE_NAMESPACE = MINI_PROGRAM_MESSAGE_NAMESPACE;
|
|
394
448
|
exports.MINI_PROGRAM_MESSAGE_VERSION = MINI_PROGRAM_MESSAGE_VERSION;
|
|
449
|
+
exports.MINI_PROGRAM_NETWORK_PROTECTED_PATHS = MINI_PROGRAM_NETWORK_PROTECTED_PATHS;
|
|
395
450
|
exports.MINI_PROGRAM_PROTOCOL_CAPABILITIES = MINI_PROGRAM_PROTOCOL_CAPABILITIES;
|
|
396
451
|
exports.NAVIGATION_CLOSE_METHOD = NAVIGATION_CLOSE_METHOD;
|
|
397
452
|
exports.NAVIGATION_OPEN_APP_PAGE_METHOD = NAVIGATION_OPEN_APP_PAGE_METHOD;
|
|
@@ -408,14 +463,15 @@ exports.STORAGE_SET_STORAGE_METHOD = STORAGE_SET_STORAGE_METHOD;
|
|
|
408
463
|
exports.UI_HIDE_LOADING_METHOD = UI_HIDE_LOADING_METHOD;
|
|
409
464
|
exports.UI_SHOW_LOADING_METHOD = UI_SHOW_LOADING_METHOD;
|
|
410
465
|
exports.UI_SHOW_TOAST_METHOD = UI_SHOW_TOAST_METHOD;
|
|
411
|
-
exports.USER_GET_CURRENT_USER_DETAIL_METHOD = USER_GET_CURRENT_USER_DETAIL_METHOD;
|
|
412
|
-
exports.USER_GET_CURRENT_USER_PROFILE_METHOD = USER_GET_CURRENT_USER_PROFILE_METHOD;
|
|
413
466
|
exports.USER_GET_INFO_METHOD = USER_GET_INFO_METHOD;
|
|
414
|
-
exports.
|
|
415
|
-
exports.USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD = USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD;
|
|
467
|
+
exports.USER_GET_LOCAL_IDENTITY_METHOD = USER_GET_LOCAL_IDENTITY_METHOD;
|
|
416
468
|
exports.USER_GET_STEAM_GAME_LIST_METHOD = USER_GET_STEAM_GAME_LIST_METHOD;
|
|
469
|
+
exports.USER_REVOKE_AUTHORIZATION_METHOD = USER_REVOKE_AUTHORIZATION_METHOD;
|
|
417
470
|
exports.VIEWPORT_GET_WINDOW_INFO_METHOD = VIEWPORT_GET_WINDOW_INFO_METHOD;
|
|
418
471
|
exports.VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD = VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD;
|
|
419
472
|
exports.isManagedMiniProgramRuntimePermissionKey = isManagedMiniProgramRuntimePermissionKey;
|
|
420
473
|
exports.isMiniProgramBridgeMessage = isMiniProgramBridgeMessage;
|
|
474
|
+
exports.isOfficialMiniProgramNetworkUrl = isOfficialMiniProgramNetworkUrl;
|
|
475
|
+
exports.isProtectedMiniProgramNetworkPath = isProtectedMiniProgramNetworkPath;
|
|
476
|
+
exports.normalizeMiniProgramNetworkPathname = normalizeMiniProgramNetworkPathname;
|
|
421
477
|
exports.parseMiniProgramRuntimePermissions = parseMiniProgramRuntimePermissions;
|
package/dist/protocol.esm.js
CHANGED
|
@@ -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 初始化时发给父容器的握手方法名。 */
|
|
@@ -25,7 +25,7 @@ function isMiniProgramBridgeMessage(value) {
|
|
|
25
25
|
}
|
|
26
26
|
const message = value;
|
|
27
27
|
return (message.namespace === MINI_PROGRAM_MESSAGE_NAMESPACE &&
|
|
28
|
-
message.version ===
|
|
28
|
+
(message.version === 1 || message.version === 2) &&
|
|
29
29
|
typeof message.nonce === 'string' &&
|
|
30
30
|
typeof message.type === 'string');
|
|
31
31
|
}
|
|
@@ -82,29 +82,97 @@ function isRecord(value) {
|
|
|
82
82
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
const OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS = [
|
|
86
|
+
'xiaoheihe.cn',
|
|
87
|
+
'max-c.com',
|
|
88
|
+
'debugmode.cn',
|
|
89
|
+
'maxjia.com',
|
|
90
|
+
];
|
|
91
|
+
/** Runtime / Host 在 `network.request` 分发前拒绝的小程序服务端私有路径前缀。 */
|
|
92
|
+
const MINI_PROGRAM_NETWORK_PROTECTED_PATHS = [
|
|
93
|
+
'/user_miniprogram/runtime/auth',
|
|
94
|
+
'/user_miniprogram/openapi/v1',
|
|
95
|
+
];
|
|
96
|
+
const NETWORK_REQUEST_PATHNAME_MAX_DECODE_ROUNDS = 4;
|
|
97
|
+
/** 使用 URL 解析后的 hostname 判断黑盒官方根域及其子域。 */
|
|
98
|
+
function isOfficialMiniProgramNetworkUrl(value) {
|
|
99
|
+
try {
|
|
100
|
+
const url = new URL(value);
|
|
101
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const hostname = url.hostname.toLowerCase().replace(/\.$/, '');
|
|
105
|
+
return OFFICIAL_MINI_PROGRAM_NETWORK_ROOT_DOMAINS.some(rootDomain => hostname === rootDomain || hostname.endsWith(`.${rootDomain}`));
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
85
111
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* @remarks
|
|
89
|
-
* 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
|
|
112
|
+
* 规范化并判断 pathname 是否命中 Runtime Auth / OpenAPI 保护前缀。
|
|
113
|
+
* 解码失败或仍可继续解码时视为不安全路径。
|
|
90
114
|
*/
|
|
91
|
-
|
|
115
|
+
function isProtectedMiniProgramNetworkPath(pathname) {
|
|
116
|
+
const normalized = normalizeMiniProgramNetworkPathname(pathname);
|
|
117
|
+
if (normalized === null) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return MINI_PROGRAM_NETWORK_PROTECTED_PATHS.some(prefix => normalized === prefix || normalized.startsWith(`${prefix}/`));
|
|
121
|
+
}
|
|
122
|
+
function normalizeMiniProgramNetworkPathname(pathname) {
|
|
123
|
+
let decoded = pathname;
|
|
124
|
+
for (let round = 0; round < NETWORK_REQUEST_PATHNAME_MAX_DECODE_ROUNDS; round += 1) {
|
|
125
|
+
let next;
|
|
126
|
+
try {
|
|
127
|
+
next = decodeURIComponent(decoded);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
if (next === decoded) {
|
|
133
|
+
return normalizeDecodedNetworkPathname(decoded);
|
|
134
|
+
}
|
|
135
|
+
decoded = next;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
if (decodeURIComponent(decoded) !== decoded) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
return normalizeDecodedNetworkPathname(decoded);
|
|
146
|
+
}
|
|
147
|
+
function normalizeDecodedNetworkPathname(decoded) {
|
|
148
|
+
const segments = [];
|
|
149
|
+
for (const segment of decoded.replace(/\\/g, '/').split('/')) {
|
|
150
|
+
if (!segment || segment === '.') {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (segment === '..') {
|
|
154
|
+
segments.pop();
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
segments.push(segment);
|
|
158
|
+
}
|
|
159
|
+
return `/${segments.join('/')}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
92
162
|
/**
|
|
93
|
-
*
|
|
163
|
+
* 登录授权能力方法名。
|
|
94
164
|
*
|
|
95
165
|
* @remarks
|
|
96
166
|
* 供 SDK 与父容器 runtime 共享同一 bridge method 标识。
|
|
97
167
|
*/
|
|
168
|
+
const AUTH_LOGIN_METHOD = 'auth.login';
|
|
169
|
+
/** 读取当前登录态、授权状态和已获准披露资料的 bridge method 标识。 */
|
|
98
170
|
const USER_GET_INFO_METHOD = 'user.getInfo';
|
|
99
|
-
/**
|
|
100
|
-
const
|
|
101
|
-
/**
|
|
102
|
-
const
|
|
103
|
-
/**
|
|
104
|
-
const USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD = 'user.getPlatformAccountOverview';
|
|
105
|
-
/** 当前用户指定平台账号详情能力方法名。 */
|
|
106
|
-
const USER_GET_PLATFORM_ACCOUNT_INFO_METHOD = 'user.getPlatformAccountInfo';
|
|
107
|
-
/** 当前用户 Steam 游戏库能力方法名。 */
|
|
171
|
+
/** 读取当前用户小程序隔离身份的 bridge method 标识。 */
|
|
172
|
+
const USER_GET_LOCAL_IDENTITY_METHOD = 'user.getLocalIdentity';
|
|
173
|
+
/** 撤销当前用户授予本小程序授权的 bridge method 标识。 */
|
|
174
|
+
const USER_REVOKE_AUTHORIZATION_METHOD = 'user.revokeAuthorization';
|
|
175
|
+
/** 读取当前已登录用户 Steam 游戏库的 bridge method 标识。 */
|
|
108
176
|
const USER_GET_STEAM_GAME_LIST_METHOD = 'user.getSteamGameList';
|
|
109
177
|
/**
|
|
110
178
|
* 展示分享面板能力方法名。
|
|
@@ -203,38 +271,24 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
|
203
271
|
risk: 'medium',
|
|
204
272
|
},
|
|
205
273
|
{
|
|
206
|
-
method:
|
|
207
|
-
module: 'user',
|
|
208
|
-
capability: USER_GET_CURRENT_USER_DETAIL_METHOD,
|
|
209
|
-
permission: 'user.currentUserDetail',
|
|
210
|
-
risk: 'high',
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
method: USER_GET_CURRENT_USER_PROFILE_METHOD,
|
|
214
|
-
module: 'user',
|
|
215
|
-
capability: USER_GET_CURRENT_USER_PROFILE_METHOD,
|
|
216
|
-
permission: 'user.currentUserProfile',
|
|
217
|
-
risk: 'high',
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
method: USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD,
|
|
274
|
+
method: USER_GET_LOCAL_IDENTITY_METHOD,
|
|
221
275
|
module: 'user',
|
|
222
|
-
capability:
|
|
223
|
-
permission: 'user.
|
|
276
|
+
capability: USER_GET_LOCAL_IDENTITY_METHOD,
|
|
277
|
+
permission: 'user.getLocalIdentity',
|
|
224
278
|
risk: 'medium',
|
|
225
279
|
},
|
|
226
280
|
{
|
|
227
|
-
method:
|
|
281
|
+
method: USER_REVOKE_AUTHORIZATION_METHOD,
|
|
228
282
|
module: 'user',
|
|
229
|
-
capability:
|
|
230
|
-
permission: 'user.
|
|
283
|
+
capability: USER_REVOKE_AUTHORIZATION_METHOD,
|
|
284
|
+
permission: 'user.revokeAuthorization',
|
|
231
285
|
risk: 'high',
|
|
232
286
|
},
|
|
233
287
|
{
|
|
234
288
|
method: USER_GET_STEAM_GAME_LIST_METHOD,
|
|
235
289
|
module: 'user',
|
|
236
290
|
capability: USER_GET_STEAM_GAME_LIST_METHOD,
|
|
237
|
-
permission: 'user.
|
|
291
|
+
permission: 'user.platformAccount.steam',
|
|
238
292
|
risk: 'high',
|
|
239
293
|
},
|
|
240
294
|
{
|
|
@@ -379,4 +433,4 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
|
379
433
|
},
|
|
380
434
|
];
|
|
381
435
|
|
|
382
|
-
export { AUTH_LOGIN_METHOD, CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_INFO_METHOD, CLOUD_LEADERBOARD_GET_LIST_METHOD, CLOUD_LEADERBOARD_SUBMIT_METHOD, DEVICE_SET_CLIPBOARD_METHOD, DEVICE_VIBRATE_METHOD, MINI_PROGRAM_BRIDGE_NONCE_PARAM, MINI_PROGRAM_MESSAGE_NAMESPACE, MINI_PROGRAM_MESSAGE_VERSION, MINI_PROGRAM_PROTOCOL_CAPABILITIES, NAVIGATION_CLOSE_METHOD, NAVIGATION_OPEN_APP_PAGE_METHOD, NAVIGATION_RELOAD_METHOD, NETWORK_REQUEST_METHOD, RUNTIME_LOCATION_PROBE_METHOD, SDK_CSP_VIOLATION_METHOD, SDK_HANDSHAKE_METHOD, SDK_LOCATION_REPORT_METHOD, SHARE_SCREENSHOT_METHOD, SHARE_SHOW_SHARE_MENU_METHOD, STORAGE_GET_STORAGE_METHOD, STORAGE_SET_STORAGE_METHOD, UI_HIDE_LOADING_METHOD, UI_SHOW_LOADING_METHOD, UI_SHOW_TOAST_METHOD,
|
|
436
|
+
export { AUTH_LOGIN_METHOD, CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_INFO_METHOD, CLOUD_LEADERBOARD_GET_LIST_METHOD, CLOUD_LEADERBOARD_SUBMIT_METHOD, DEVICE_SET_CLIPBOARD_METHOD, DEVICE_VIBRATE_METHOD, MINI_PROGRAM_BRIDGE_NONCE_PARAM, MINI_PROGRAM_MESSAGE_NAMESPACE, MINI_PROGRAM_MESSAGE_VERSION, MINI_PROGRAM_NETWORK_PROTECTED_PATHS, MINI_PROGRAM_PROTOCOL_CAPABILITIES, NAVIGATION_CLOSE_METHOD, NAVIGATION_OPEN_APP_PAGE_METHOD, NAVIGATION_RELOAD_METHOD, NETWORK_REQUEST_METHOD, RUNTIME_LOCATION_PROBE_METHOD, SDK_CSP_VIOLATION_METHOD, SDK_HANDSHAKE_METHOD, SDK_LOCATION_REPORT_METHOD, SHARE_SCREENSHOT_METHOD, SHARE_SHOW_SHARE_MENU_METHOD, STORAGE_GET_STORAGE_METHOD, STORAGE_SET_STORAGE_METHOD, UI_HIDE_LOADING_METHOD, UI_SHOW_LOADING_METHOD, UI_SHOW_TOAST_METHOD, USER_GET_INFO_METHOD, USER_GET_LOCAL_IDENTITY_METHOD, USER_GET_STEAM_GAME_LIST_METHOD, USER_REVOKE_AUTHORIZATION_METHOD, VIEWPORT_GET_WINDOW_INFO_METHOD, VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD, isManagedMiniProgramRuntimePermissionKey, isMiniProgramBridgeMessage, isOfficialMiniProgramNetworkUrl, isProtectedMiniProgramNetworkPath, normalizeMiniProgramNetworkPathname, parseMiniProgramRuntimePermissions };
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<p class="eyebrow">Heybox Mini Program</p>
|
|
5
5
|
<h1><%= projectName %></h1>
|
|
6
6
|
<p class="intro">
|
|
7
|
-
这个模板只保留最小接入示例:等待 SDK ready
|
|
7
|
+
这个模板只保留最小接入示例:等待 SDK ready,并由用户主动选择受支持的操作。
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
10
|
<p class="status" data-testid="status">{{ status }}</p>
|
|
11
11
|
|
|
12
12
|
<button type="button" :disabled="loading" data-testid="login" @click="login">
|
|
13
|
-
|
|
13
|
+
获取授权码
|
|
14
14
|
</button>
|
|
15
15
|
|
|
16
16
|
<pre data-testid="output">{{ output }}</pre>
|
|
@@ -21,36 +21,41 @@
|
|
|
21
21
|
<script setup lang="ts">
|
|
22
22
|
import hbSDK from '@heybox/hb-sdk';
|
|
23
23
|
import { onMounted, ref } from 'vue';
|
|
24
|
+
import { loginAndExchangeAuthorizationCode } from './auth-handoff';
|
|
24
25
|
|
|
25
26
|
const loading = ref(false);
|
|
26
27
|
const status = ref('正在连接 SDK...');
|
|
27
|
-
const output = ref('
|
|
28
|
+
const output = ref('连接后请选择操作。');
|
|
28
29
|
|
|
29
30
|
onMounted(() => {
|
|
30
|
-
void
|
|
31
|
+
void connectSDK();
|
|
31
32
|
});
|
|
32
33
|
|
|
33
|
-
async function
|
|
34
|
-
|
|
34
|
+
async function connectSDK() {
|
|
35
|
+
try {
|
|
35
36
|
await hbSDK.ready();
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
status.value = 'SDK 已连接';
|
|
38
|
+
output.value = '请选择操作。';
|
|
39
|
+
} catch (error) {
|
|
40
|
+
status.value = 'SDK 连接失败';
|
|
41
|
+
output.value = readErrorMessage(error);
|
|
42
|
+
}
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
async function login() {
|
|
41
|
-
await runSdkAction('auth.login', () => hbSDK.auth.login());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function runSdkAction(label: string, action: () => Promise<unknown>) {
|
|
45
46
|
loading.value = true;
|
|
46
|
-
status.value =
|
|
47
|
+
status.value = 'auth.login 调用中...';
|
|
47
48
|
|
|
48
49
|
try {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const { expiresIn, scopes } = await loginAndExchangeAuthorizationCode({
|
|
51
|
+
exchangeUrl: import.meta.env.VITE_HEYBOX_AUTH_EXCHANGE_URL,
|
|
52
|
+
login: () => hbSDK.auth.login(),
|
|
53
|
+
request: (config) => hbSDK.network.request(config),
|
|
54
|
+
});
|
|
55
|
+
status.value = 'auth.login 调用成功';
|
|
56
|
+
output.value = JSON.stringify({ expiresIn, scopes }, null, 2);
|
|
52
57
|
} catch (error) {
|
|
53
|
-
status.value =
|
|
58
|
+
status.value = 'auth.login 调用失败';
|
|
54
59
|
output.value = readErrorMessage(error);
|
|
55
60
|
} finally {
|
|
56
61
|
loading.value = false;
|
|
@@ -3,65 +3,73 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
3
3
|
import App from '../App.vue';
|
|
4
4
|
|
|
5
5
|
const sdkMock = vi.hoisted(() => ({
|
|
6
|
-
|
|
6
|
+
login: vi.fn(),
|
|
7
|
+
networkRequest: vi.fn(),
|
|
7
8
|
ready: vi.fn(),
|
|
8
|
-
userGetInfo: vi.fn(),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
11
|
vi.mock('@heybox/hb-sdk', () => ({
|
|
12
12
|
default: {
|
|
13
13
|
auth: {
|
|
14
|
-
login: sdkMock.
|
|
14
|
+
login: sdkMock.login,
|
|
15
15
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
getInfo: sdkMock.userGetInfo,
|
|
16
|
+
network: {
|
|
17
|
+
request: sdkMock.networkRequest,
|
|
19
18
|
},
|
|
19
|
+
ready: sdkMock.ready,
|
|
20
20
|
},
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
23
|
describe('App', () => {
|
|
24
24
|
beforeEach(() => {
|
|
25
25
|
vi.resetAllMocks();
|
|
26
|
+
vi.unstubAllEnvs();
|
|
26
27
|
|
|
27
28
|
sdkMock.ready.mockResolvedValue(undefined);
|
|
28
|
-
sdkMock.
|
|
29
|
-
|
|
30
|
-
userInfo: {
|
|
31
|
-
avatar: '',
|
|
32
|
-
heybox_id: '10001',
|
|
33
|
-
nickname: '模板用户',
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
sdkMock.authLogin.mockResolvedValue({
|
|
37
|
-
isLogin: true,
|
|
38
|
-
userInfo: {
|
|
39
|
-
avatar: '',
|
|
40
|
-
heybox_id: '10002',
|
|
41
|
-
nickname: '登录用户',
|
|
42
|
-
},
|
|
43
|
-
});
|
|
29
|
+
sdkMock.login.mockResolvedValue({ code: 'authorization_code', expiresIn: 300, scopes: ['identity'] });
|
|
30
|
+
sdkMock.networkRequest.mockResolvedValue({ status: 200 });
|
|
44
31
|
});
|
|
45
32
|
|
|
46
|
-
it('
|
|
33
|
+
it('only waits for SDK readiness during startup', async () => {
|
|
47
34
|
const wrapper = mount(App);
|
|
48
35
|
await flushPromises();
|
|
49
36
|
|
|
50
37
|
expect(sdkMock.ready).toHaveBeenCalledTimes(1);
|
|
51
|
-
expect(sdkMock.
|
|
52
|
-
expect(wrapper.get('[data-testid="status"]').text()).toBe('
|
|
53
|
-
expect(wrapper.get('[data-testid="output"]').text()).toContain('
|
|
38
|
+
expect(sdkMock.login).not.toHaveBeenCalled();
|
|
39
|
+
expect(wrapper.get('[data-testid="status"]').text()).toBe('SDK 已连接');
|
|
40
|
+
expect(wrapper.get('[data-testid="output"]').text()).toContain('请选择操作');
|
|
54
41
|
});
|
|
55
42
|
|
|
56
|
-
it('
|
|
43
|
+
it('hands the authorization code to the developer server without rendering it', async () => {
|
|
44
|
+
vi.stubEnv('VITE_HEYBOX_AUTH_EXCHANGE_URL', 'https://developer.example.com/api/heybox/exchange');
|
|
57
45
|
const wrapper = mount(App);
|
|
58
46
|
await flushPromises();
|
|
59
47
|
|
|
60
48
|
await wrapper.get('[data-testid="login"]').trigger('click');
|
|
61
49
|
await flushPromises();
|
|
62
50
|
|
|
63
|
-
expect(sdkMock.
|
|
51
|
+
expect(sdkMock.login).toHaveBeenCalledTimes(1);
|
|
52
|
+
expect(sdkMock.networkRequest).toHaveBeenCalledWith({
|
|
53
|
+
method: 'POST',
|
|
54
|
+
url: 'https://developer.example.com/api/heybox/exchange',
|
|
55
|
+
data: { code: 'authorization_code' },
|
|
56
|
+
});
|
|
64
57
|
expect(wrapper.get('[data-testid="status"]').text()).toBe('auth.login 调用成功');
|
|
65
|
-
expect(wrapper.get('[data-testid="output"]').text()).toContain('
|
|
58
|
+
expect(wrapper.get('[data-testid="output"]').text()).toContain('300');
|
|
59
|
+
expect(wrapper.get('[data-testid="output"]').text()).toContain('identity');
|
|
60
|
+
expect(wrapper.text()).not.toContain('authorization_code');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('does not request a code when the developer server is not configured', async () => {
|
|
64
|
+
const wrapper = mount(App);
|
|
65
|
+
await flushPromises();
|
|
66
|
+
|
|
67
|
+
await wrapper.get('[data-testid="login"]').trigger('click');
|
|
68
|
+
await flushPromises();
|
|
69
|
+
|
|
70
|
+
expect(sdkMock.login).not.toHaveBeenCalled();
|
|
71
|
+
expect(sdkMock.networkRequest).not.toHaveBeenCalled();
|
|
72
|
+
expect(wrapper.get('[data-testid="status"]').text()).toBe('auth.login 调用失败');
|
|
73
|
+
expect(wrapper.get('[data-testid="output"]').text()).toContain('VITE_HEYBOX_AUTH_EXCHANGE_URL');
|
|
66
74
|
});
|
|
67
75
|
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface AuthorizationCodeLoginResult {
|
|
2
|
+
code: string;
|
|
3
|
+
expiresIn: number;
|
|
4
|
+
scopes: string[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface LoginAndExchangeAuthorizationCodeOptions {
|
|
8
|
+
exchangeUrl?: string;
|
|
9
|
+
login: () => Promise<AuthorizationCodeLoginResult>;
|
|
10
|
+
request: (config: {
|
|
11
|
+
method: 'POST';
|
|
12
|
+
url: string;
|
|
13
|
+
data: { code: string };
|
|
14
|
+
}) => Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function loginAndExchangeAuthorizationCode(options: LoginAndExchangeAuthorizationCodeOptions) {
|
|
18
|
+
const exchangeUrl = options.exchangeUrl?.trim();
|
|
19
|
+
if (!exchangeUrl) {
|
|
20
|
+
throw new Error('请先配置 VITE_HEYBOX_AUTH_EXCHANGE_URL');
|
|
21
|
+
}
|
|
22
|
+
if (!isAllowedExchangeUrl(exchangeUrl)) {
|
|
23
|
+
throw new Error('VITE_HEYBOX_AUTH_EXCHANGE_URL 必须使用 HTTPS,本地调试仅允许 localhost');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { code, expiresIn, scopes } = await options.login();
|
|
27
|
+
await options.request({
|
|
28
|
+
method: 'POST',
|
|
29
|
+
url: exchangeUrl,
|
|
30
|
+
data: { code },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return { expiresIn, scopes };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isAllowedExchangeUrl(value: string) {
|
|
37
|
+
try {
|
|
38
|
+
const url = new URL(value);
|
|
39
|
+
if (url.protocol === 'https:') {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/vite.cjs.js
CHANGED
|
@@ -8,7 +8,7 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
|
|
|
8
8
|
/** 构建时替换为当前发布包的实际版本。 */
|
|
9
9
|
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
10
10
|
? undefined
|
|
11
|
-
: '0.
|
|
11
|
+
: '0.7.0-alpha.2';
|
|
12
12
|
|
|
13
13
|
var re = {exports: {}};
|
|
14
14
|
|
|
@@ -11304,7 +11304,7 @@ function parse(html, options) {
|
|
|
11304
11304
|
/** 小程序沙盒通信命名空间,父容器与 iframe 内 SDK 必须保持一致。 */
|
|
11305
11305
|
const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
|
|
11306
11306
|
/** 小程序沙盒通信协议版本。 */
|
|
11307
|
-
const MINI_PROGRAM_MESSAGE_VERSION =
|
|
11307
|
+
const MINI_PROGRAM_MESSAGE_VERSION = 2;
|
|
11308
11308
|
/** 父容器注入到小程序 URL 上的通信 nonce 参数名。 */
|
|
11309
11309
|
const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
11310
11310
|
|
package/dist/vite.esm.js
CHANGED
|
@@ -5,7 +5,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
|
|
|
5
5
|
/** 构建时替换为当前发布包的实际版本。 */
|
|
6
6
|
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
7
7
|
? undefined
|
|
8
|
-
: '0.
|
|
8
|
+
: '0.7.0-alpha.2';
|
|
9
9
|
|
|
10
10
|
var re = {exports: {}};
|
|
11
11
|
|
|
@@ -11301,7 +11301,7 @@ function parse(html, options) {
|
|
|
11301
11301
|
/** 小程序沙盒通信命名空间,父容器与 iframe 内 SDK 必须保持一致。 */
|
|
11302
11302
|
const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
|
|
11303
11303
|
/** 小程序沙盒通信协议版本。 */
|
|
11304
|
-
const MINI_PROGRAM_MESSAGE_VERSION =
|
|
11304
|
+
const MINI_PROGRAM_MESSAGE_VERSION = 2;
|
|
11305
11305
|
/** 父容器注入到小程序 URL 上的通信 nonce 参数名。 */
|
|
11306
11306
|
const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
11307
11307
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heybox/hb-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0-alpha.2",
|
|
4
4
|
"sideEffects": [
|
|
5
5
|
"./src/index.ts",
|
|
6
6
|
"./src/core/singleton.ts",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"vue": "^2.7.16",
|
|
96
96
|
"vite": "^8.0.12",
|
|
97
97
|
"vitest": "^3.2.4",
|
|
98
|
-
"@heybox/hb-api": "~1.25.
|
|
98
|
+
"@heybox/hb-api": "~1.25.16"
|
|
99
99
|
},
|
|
100
100
|
"publishConfig": {
|
|
101
101
|
"registry": "https://registry.npmjs.org/",
|