@heybox/hb-sdk 0.8.0-alpha → 0.8.0-alpha.12
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 +106 -395
- package/README.md +107 -24
- package/THIRD_PARTY_NOTICES.md +1755 -0
- package/dist/cli-chunks/{build-DJWFSM1B.cjs → build-DNtjBmyy.cjs} +8 -5
- package/dist/cli-chunks/{context-DV2UK1Nz.cjs → context-CO_3KgM_.cjs} +39 -81
- package/dist/cli-chunks/{create-2HfoB48V.cjs → create-DG8JxrPU.cjs} +2 -2
- package/dist/cli-chunks/{dev-Dgt2zS9k.cjs → dev-C5U1Lo4e.cjs} +404 -577
- package/dist/cli-chunks/doctor-B50OXvrj.cjs +65 -0
- package/dist/cli-chunks/{index-D62ANeBv.cjs → index-B4nFqf-3.cjs} +50 -30
- package/dist/cli-chunks/{index-BjoSXl8C.cjs → index-B8FK1ojC.cjs} +2 -2
- package/dist/cli-chunks/{index.esm-CigcxJ2B.cjs → index.esm-DiX5WKSp.cjs} +8 -7
- package/dist/cli-chunks/{login-Cumknwdx.cjs → login-CBwmxsEK.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-CcE-HMmd.cjs → project-vite-DWpmHOsV.cjs} +1 -1
- package/dist/cli-chunks/{remote-DDdP3xcE.cjs → remote-Dt-U4gDD.cjs} +57 -25
- package/dist/cli-chunks/{runtime-gate-DFjw66kF.cjs → runtime-gate-CMnGCdfq.cjs} +11 -3
- package/dist/cli-chunks/{runtime-permission-env-CjsCe5bp.cjs → runtime-permission-env-BcUsO3nG.cjs} +351 -0
- package/dist/cli-chunks/{session-BDi_AZSv.cjs → session-yy543BIB.cjs} +1 -1
- package/dist/cli-chunks/skill-B4SWt2o6.cjs +83 -0
- package/dist/cli-chunks/version-CmizPu7D.cjs +8 -0
- package/dist/cli.cjs +1 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-TzYf9L6C.js +99 -0
- package/dist/devtools/browser-dev-host/assets/heybox-logo-CogNENsk.svg +6 -0
- package/dist/devtools/browser-dev-host/assets/index-C5MZZDa5.js +567 -0
- package/dist/devtools/browser-dev-host/assets/index-P-ra4m1y.css +1 -0
- package/dist/devtools/browser-dev-host/assets/workbench-state-BwV7bm4n.js +5 -0
- package/dist/devtools/browser-dev-host/index.html +6 -435
- package/dist/index.cjs.js +1023 -29
- package/dist/index.esm.js +1023 -30
- package/dist/protocol.cjs.js +381 -37
- package/dist/protocol.esm.js +355 -38
- package/dist/templates/{vue3-vite-ts → vanilla-vite-js}/.gitignore.ejs +0 -1
- package/dist/templates/vanilla-vite-js/README.md.ejs +14 -0
- package/dist/templates/vanilla-vite-js/index.html.ejs +20 -0
- package/dist/templates/vanilla-vite-js/package.json.ejs +22 -0
- package/dist/templates/vanilla-vite-js/src/assets/heybox-logo.svg +8 -0
- package/dist/templates/vanilla-vite-js/src/main.js +39 -0
- package/dist/templates/vanilla-vite-js/src/styles.css +155 -0
- package/dist/templates/{vue3-vite-ts/vite.config.ts → vanilla-vite-js/vite.config.js} +1 -2
- package/dist/vite.cjs.js +281 -13
- package/dist/vite.esm.js +281 -13
- package/package.json +34 -11
- package/skill/SKILL.md +22 -20
- package/skill/references/api-protocol.md +100 -6
- package/skill/references/api-root.md +199 -28
- package/skill/references/cli.md +41 -24
- package/skill/references/examples.md +30 -1
- package/skill/references/llms-index.md +1 -1
- package/skill/references/recipes.md +139 -36
- package/skill/references/safety-boundaries.md +20 -5
- package/skill/skill.json +10 -5
- package/types/core/client.d.ts +17 -1
- package/types/core/mini-dev-console.d.ts +36 -0
- package/types/core/sdk.d.ts +3 -0
- package/types/core/singleton.d.ts +3 -0
- package/types/index.d.ts +4 -2
- package/types/miniapp-manifest/index.d.ts +1 -0
- package/types/miniapp-manifest/node.d.ts +3 -0
- package/types/miniapp-manifest/permissions.d.ts +37 -0
- package/types/miniapp-manifest/schema.d.ts +5 -0
- package/types/modules/files/index.d.ts +5 -0
- package/types/modules/files/registry.d.ts +35 -0
- package/types/modules/files/types.d.ts +159 -0
- package/types/modules/network/index.d.ts +50 -3
- package/types/modules/share/index.d.ts +1 -1
- package/types/modules/share/show-share-menu.d.ts +1 -1
- package/types/modules/share/types.d.ts +2 -4
- package/types/protocol/capabilities.d.ts +2 -2
- package/types/protocol/constants.d.ts +1 -1
- package/types/protocol/guards.d.ts +1 -1
- package/types/protocol/types.d.ts +1 -1
- package/types/protocol.d.ts +4 -3
- package/types/skill-metadata.d.ts +0 -4
- package/types/vite/index.d.ts +3 -1
- package/dist/cli-chunks/doctor-C95gIao_.cjs +0 -204
- package/dist/devtools/browser-dev-host/main.js +0 -12263
- package/dist/templates/vue3-vite-ts/README.md.ejs +0 -47
- package/dist/templates/vue3-vite-ts/index.html.ejs +0 -12
- package/dist/templates/vue3-vite-ts/package.json.ejs +0 -33
- package/dist/templates/vue3-vite-ts/src/App.vue +0 -78
- package/dist/templates/vue3-vite-ts/src/__tests__/App.spec.ts +0 -148
- package/dist/templates/vue3-vite-ts/src/auth-handoff.ts +0 -46
- package/dist/templates/vue3-vite-ts/src/main.ts +0 -5
- package/dist/templates/vue3-vite-ts/src/styles.css +0 -60
- package/dist/templates/vue3-vite-ts/src/vite-env.d.ts +0 -1
- package/dist/templates/vue3-vite-ts/tsconfig.app.json +0 -17
- package/dist/templates/vue3-vite-ts/tsconfig.json +0 -11
- package/dist/templates/vue3-vite-ts/tsconfig.node.json +0 -11
- package/dist/templates/vue3-vite-ts/vitest.config.ts +0 -10
- package/skill/scripts/check-references.mjs +0 -14
- package/skill/scripts/markdown-sections.mjs +0 -36
- package/skill/scripts/package-skill.mjs +0 -60
- package/skill/scripts/package-skill.sh +0 -6
- package/skill/scripts/skill-metadata.mjs +0 -77
- package/skill/scripts/sync-agent-skills-payload.mjs +0 -359
- package/skill/scripts/sync-references.mjs +0 -794
- package/skill/scripts/validate-skill.mjs +0 -263
|
@@ -8,16 +8,17 @@ var node_crypto = require('node:crypto');
|
|
|
8
8
|
var promises = require('node:dns/promises');
|
|
9
9
|
var node_http = require('node:http');
|
|
10
10
|
var node_module = require('node:module');
|
|
11
|
-
var context = require('./context-
|
|
11
|
+
var context = require('./context-CO_3KgM_.cjs');
|
|
12
|
+
var runtimePermissionEnv = require('./runtime-permission-env-BcUsO3nG.cjs');
|
|
12
13
|
var undici = require('undici');
|
|
13
14
|
var fs$1 = require('node:fs/promises');
|
|
14
|
-
var index = require('./index-
|
|
15
|
+
var index = require('./index-B4nFqf-3.cjs');
|
|
15
16
|
var require$$0$1 = require('crypto');
|
|
16
17
|
var require$$0 = require('buffer');
|
|
17
|
-
var session = require('./session-
|
|
18
|
+
var session = require('./session-yy543BIB.cjs');
|
|
18
19
|
var browser = require('./browser-RAy8e8cV.cjs');
|
|
19
|
-
var projectVite = require('./project-vite-
|
|
20
|
-
var
|
|
20
|
+
var projectVite = require('./project-vite-DWpmHOsV.cjs');
|
|
21
|
+
var version = require('./version-CmizPu7D.cjs');
|
|
21
22
|
|
|
22
23
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
23
24
|
class Locked extends Error {
|
|
@@ -429,6 +430,25 @@ class BrowserDevHostHttpError extends Error {
|
|
|
429
430
|
this.name = 'BrowserDevHostHttpError';
|
|
430
431
|
}
|
|
431
432
|
}
|
|
433
|
+
function createBrowserDevHostRequestAbortScope(request, response) {
|
|
434
|
+
const controller = new AbortController();
|
|
435
|
+
const abort = () => controller.abort();
|
|
436
|
+
const onResponseClose = () => {
|
|
437
|
+
if (!response.writableEnded)
|
|
438
|
+
abort();
|
|
439
|
+
};
|
|
440
|
+
request.once('aborted', abort);
|
|
441
|
+
response.once('close', onResponseClose);
|
|
442
|
+
if (request.aborted || (response.destroyed && !response.writableEnded))
|
|
443
|
+
abort();
|
|
444
|
+
return {
|
|
445
|
+
dispose() {
|
|
446
|
+
request.removeListener('aborted', abort);
|
|
447
|
+
response.removeListener('close', onResponseClose);
|
|
448
|
+
},
|
|
449
|
+
signal: controller.signal,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
432
452
|
function readJsonRequestBody(request, options = {}) {
|
|
433
453
|
return new Promise((resolve, reject) => {
|
|
434
454
|
const chunks = [];
|
|
@@ -498,7 +518,7 @@ function writeSerializedJsonResponse(response, status, body) {
|
|
|
498
518
|
});
|
|
499
519
|
response.end(body);
|
|
500
520
|
}
|
|
501
|
-
function isRecord$
|
|
521
|
+
function isRecord$2(value) {
|
|
502
522
|
return Object.prototype.toString.call(value) === '[object Object]';
|
|
503
523
|
}
|
|
504
524
|
function isObjectLike(value) {
|
|
@@ -594,7 +614,6 @@ function redactSensitivePairsInRawText(text) {
|
|
|
594
614
|
});
|
|
595
615
|
}
|
|
596
616
|
|
|
597
|
-
const MAX_BROWSER_DEV_HOST_NETWORK_REDIRECTS = 5;
|
|
598
617
|
const MAX_BROWSER_DEV_HOST_NETWORK_REQUEST_BODY = 2 * 1024 * 1024;
|
|
599
618
|
const MAX_BROWSER_DEV_HOST_NETWORK_RESPONSE_BODY = 5 * 1024 * 1024;
|
|
600
619
|
const DENIED_NETWORK_TARGETS = createDeniedNetworkTargets();
|
|
@@ -615,16 +634,17 @@ async function handleBrowserDevHostNetworkProxy(request, response, fetchImpl, lo
|
|
|
615
634
|
let payload;
|
|
616
635
|
try {
|
|
617
636
|
payload = await readJsonRequestBody(request);
|
|
618
|
-
const requestAbort =
|
|
619
|
-
|
|
620
|
-
requestAbort.
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
637
|
+
const requestAbort = createBrowserDevHostRequestAbortScope(request, response);
|
|
638
|
+
try {
|
|
639
|
+
const result = await requestBrowserDevHostNetwork(payload, fetchImpl, lookupHostname, options.allowedHttpOrigins, requestAbort.signal);
|
|
640
|
+
if (typeof result.status === 'number' && (result.status < 200 || result.status >= 300)) {
|
|
641
|
+
logBrowserDevHostNetworkProxyDiagnostic('http_status', payload, result);
|
|
642
|
+
}
|
|
643
|
+
writeNetworkJsonResponse(response, 200, result, cors);
|
|
644
|
+
}
|
|
645
|
+
finally {
|
|
646
|
+
requestAbort.dispose();
|
|
626
647
|
}
|
|
627
|
-
writeNetworkJsonResponse(response, 200, result, cors);
|
|
628
648
|
}
|
|
629
649
|
catch (error) {
|
|
630
650
|
logBrowserDevHostNetworkProxyDiagnostic('proxy_error', payload, undefined, error);
|
|
@@ -639,10 +659,10 @@ function logBrowserDevHostNetworkProxyDiagnostic(phase, payload, result, error)
|
|
|
639
659
|
return;
|
|
640
660
|
}
|
|
641
661
|
const detail = { phase };
|
|
642
|
-
if (isRecord$
|
|
662
|
+
if (isRecord$2(payload) && typeof payload.url === 'string') {
|
|
643
663
|
detail.url = sanitizeNetworkRequestUrl(payload.url);
|
|
644
664
|
}
|
|
645
|
-
if (isRecord$
|
|
665
|
+
if (isRecord$2(payload) && typeof payload.method === 'string') {
|
|
646
666
|
detail.method = payload.method;
|
|
647
667
|
}
|
|
648
668
|
if (typeof result?.status === 'number') {
|
|
@@ -673,14 +693,14 @@ function writeNetworkJsonResponse(response, status, body, cors) {
|
|
|
673
693
|
}
|
|
674
694
|
const BROWSER_DEV_HOST_NETWORK_MULTIPART_UNSUPPORTED_MESSAGE = 'network.request 不支持 multipart/form-data。App Host 客户端代发仅支持 application/x-www-form-urlencoded(form)与 application/json;请将 data 编码为 form 字符串(例如 new URLSearchParams({...}).toString())并设置 Content-Type: application/x-www-form-urlencoded。文件上传请使用专用上传能力,勿手写 multipart boundary。';
|
|
675
695
|
async function requestBrowserDevHostNetwork(payload, fetchImpl, lookupHostname, allowedHttpOrigins, requestSignal) {
|
|
676
|
-
if (!isRecord$
|
|
696
|
+
if (!isRecord$2(payload) || typeof payload.url !== 'string' || !payload.url.trim()) {
|
|
677
697
|
throw createBrowserDevHostError(400, 'INVALID_NETWORK_REQUEST', 'network.request url 必须是非空字符串');
|
|
678
698
|
}
|
|
679
699
|
const url = createBrowserDevHostNetworkUrl(payload.url, payload.params);
|
|
680
700
|
const method = typeof payload.method === 'string' && payload.method.trim() ? payload.method.trim().toUpperCase() : 'GET';
|
|
681
701
|
const headers = normalizeBrowserDevHostNetworkHeaders(payload.headers);
|
|
682
702
|
assertSupportedBrowserDevHostNetworkRequestShape(headers, payload.data);
|
|
683
|
-
const body = createBrowserDevHostNetworkBody(method, payload.data, headers);
|
|
703
|
+
const body = createBrowserDevHostNetworkBody(method, payload.data, headers, readBodyEncoding(payload.bodyEncoding, payload.data));
|
|
684
704
|
const controller = new AbortController();
|
|
685
705
|
const onRequestAbort = () => controller.abort(requestSignal.reason);
|
|
686
706
|
requestSignal.addEventListener('abort', onRequestAbort, { once: true });
|
|
@@ -691,7 +711,7 @@ async function requestBrowserDevHostNetwork(payload, fetchImpl, lookupHostname,
|
|
|
691
711
|
const pinnedTargets = new Map();
|
|
692
712
|
const dispatcher = createPinnedNetworkDispatcher(pinnedTargets);
|
|
693
713
|
try {
|
|
694
|
-
const response = await
|
|
714
|
+
const response = await fetchBrowserDevHostNetworkNoRedirect({
|
|
695
715
|
body,
|
|
696
716
|
dispatcher,
|
|
697
717
|
fetchImpl,
|
|
@@ -724,43 +744,17 @@ async function requestBrowserDevHostNetwork(payload, fetchImpl, lookupHostname,
|
|
|
724
744
|
await dispatcher.close();
|
|
725
745
|
}
|
|
726
746
|
}
|
|
727
|
-
async function
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
options.
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
method,
|
|
739
|
-
redirect: 'manual',
|
|
740
|
-
signal: options.signal,
|
|
741
|
-
});
|
|
742
|
-
if (![301, 302, 303, 307, 308].includes(response.status)) {
|
|
743
|
-
return response;
|
|
744
|
-
}
|
|
745
|
-
const location = response.headers.get('location');
|
|
746
|
-
if (!location) {
|
|
747
|
-
return response;
|
|
748
|
-
}
|
|
749
|
-
if (redirectCount === MAX_BROWSER_DEV_HOST_NETWORK_REDIRECTS) {
|
|
750
|
-
throw createBrowserDevHostError(502, 'TOO_MANY_REDIRECTS', 'network.request redirect limit exceeded');
|
|
751
|
-
}
|
|
752
|
-
await response.body?.cancel();
|
|
753
|
-
const redirectedUrl = new URL(location, url);
|
|
754
|
-
if (redirectedUrl.origin !== url.origin) {
|
|
755
|
-
throw createBrowserDevHostError(403, 'NETWORK_REDIRECT_DENIED', 'network.request 不允许跨 origin redirect');
|
|
756
|
-
}
|
|
757
|
-
url = redirectedUrl;
|
|
758
|
-
if (response.status === 303 || ((response.status === 301 || response.status === 302) && method === 'POST')) {
|
|
759
|
-
method = 'GET';
|
|
760
|
-
body = undefined;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
throw createBrowserDevHostError(502, 'TOO_MANY_REDIRECTS', 'network.request redirect limit exceeded');
|
|
747
|
+
async function fetchBrowserDevHostNetworkNoRedirect(options) {
|
|
748
|
+
const pinnedTarget = await resolveBrowserDevHostNetworkTarget(options.url, options.lookupHostname, options.allowedHttpOrigins);
|
|
749
|
+
options.pinnedTargets.set(createPinnedNetworkTargetKey(options.url), pinnedTarget);
|
|
750
|
+
return options.fetchImpl(options.url.toString(), {
|
|
751
|
+
body: options.body,
|
|
752
|
+
dispatcher: options.dispatcher,
|
|
753
|
+
headers: options.headers,
|
|
754
|
+
method: options.method,
|
|
755
|
+
redirect: 'manual',
|
|
756
|
+
signal: options.signal,
|
|
757
|
+
});
|
|
764
758
|
}
|
|
765
759
|
async function resolveBrowserDevHostNetworkTarget(url, lookupHostname, allowedHttpOrigins) {
|
|
766
760
|
if (!['http:', 'https:'].includes(url.protocol)) {
|
|
@@ -890,7 +884,7 @@ function createBrowserDevHostNetworkUrl(input, params) {
|
|
|
890
884
|
if (!['http:', 'https:'].includes(url.protocol)) {
|
|
891
885
|
throw createBrowserDevHostError(400, 'INVALID_NETWORK_REQUEST', 'network.request 仅支持 HTTP(S) URL');
|
|
892
886
|
}
|
|
893
|
-
if (isRecord$
|
|
887
|
+
if (isRecord$2(params)) {
|
|
894
888
|
Object.entries(params).forEach(([key, value]) => {
|
|
895
889
|
if (value !== undefined && value !== null) {
|
|
896
890
|
url.searchParams.set(key, String(value));
|
|
@@ -901,7 +895,7 @@ function createBrowserDevHostNetworkUrl(input, params) {
|
|
|
901
895
|
}
|
|
902
896
|
function normalizeBrowserDevHostNetworkHeaders(headers) {
|
|
903
897
|
const normalized = new Headers();
|
|
904
|
-
if (!isRecord$
|
|
898
|
+
if (!isRecord$2(headers)) {
|
|
905
899
|
return normalized;
|
|
906
900
|
}
|
|
907
901
|
Object.entries(headers).forEach(([key, value]) => {
|
|
@@ -947,22 +941,60 @@ function looksLikeMultipartBody(data) {
|
|
|
947
941
|
const trimmed = data.trimStart();
|
|
948
942
|
return trimmed.startsWith('--') && /content-disposition\s*:\s*form-data/i.test(data);
|
|
949
943
|
}
|
|
950
|
-
function createBrowserDevHostNetworkBody(method, data, headers) {
|
|
944
|
+
function createBrowserDevHostNetworkBody(method, data, headers, bodyEncoding) {
|
|
951
945
|
if (data === undefined || method === 'GET' || method === 'HEAD') {
|
|
952
946
|
return undefined;
|
|
953
947
|
}
|
|
954
|
-
const body = typeof data === 'string'
|
|
948
|
+
const body = typeof data === 'string'
|
|
949
|
+
? data
|
|
950
|
+
: bodyEncoding === 'form'
|
|
951
|
+
? createLegacyFormBody$1(data)
|
|
952
|
+
: JSON.stringify(data);
|
|
955
953
|
if (Buffer.byteLength(body) > MAX_BROWSER_DEV_HOST_NETWORK_REQUEST_BODY) {
|
|
956
954
|
throw createBrowserDevHostError(413, 'REQUEST_ENTITY_TOO_LARGE', 'network.request data exceeds 2097152 bytes');
|
|
957
955
|
}
|
|
958
|
-
if (
|
|
959
|
-
|
|
956
|
+
if (bodyEncoding === 'form' && !headers.has('content-type')) {
|
|
957
|
+
headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
|
|
960
958
|
}
|
|
961
|
-
if (!headers.has('content-type')) {
|
|
959
|
+
else if (bodyEncoding === 'json' && typeof data !== 'string' && !headers.has('content-type')) {
|
|
962
960
|
headers.set('content-type', 'application/json');
|
|
963
961
|
}
|
|
964
962
|
return body;
|
|
965
963
|
}
|
|
964
|
+
function readBodyEncoding(value, data) {
|
|
965
|
+
if (value === 'form' || value === 'json')
|
|
966
|
+
return value;
|
|
967
|
+
if (value === undefined && (data === undefined || typeof data === 'string'))
|
|
968
|
+
return undefined;
|
|
969
|
+
throw createBrowserDevHostError(400, 'INVALID_NETWORK_REQUEST', 'network.request bodyEncoding 必须明确为 form 或 json');
|
|
970
|
+
}
|
|
971
|
+
function encodeLegacyFormComponent$1(value) {
|
|
972
|
+
return encodeURIComponent(value).replace(/[!'()*]/g, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
973
|
+
}
|
|
974
|
+
function appendLegacyFormValue$1(pairs, key, value) {
|
|
975
|
+
if (value === undefined)
|
|
976
|
+
return;
|
|
977
|
+
if (value === null) {
|
|
978
|
+
pairs.push([key, '']);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
if (Array.isArray(value)) {
|
|
982
|
+
value.forEach((item) => appendLegacyFormValue$1(pairs, key, item));
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (typeof value === 'object' && !(value instanceof Date)) {
|
|
986
|
+
Object.entries(value).forEach(([childKey, childValue]) => appendLegacyFormValue$1(pairs, `${key}[${childKey}]`, childValue));
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
pairs.push([key, value instanceof Date ? value.toISOString() : String(value)]);
|
|
990
|
+
}
|
|
991
|
+
function createLegacyFormBody$1(data) {
|
|
992
|
+
const pairs = [];
|
|
993
|
+
if (data && typeof data === 'object') {
|
|
994
|
+
Object.entries(data).forEach(([key, value]) => appendLegacyFormValue$1(pairs, key, value));
|
|
995
|
+
}
|
|
996
|
+
return pairs.map(([key, value]) => `${encodeLegacyFormComponent$1(key)}=${encodeLegacyFormComponent$1(value)}`).join('&');
|
|
997
|
+
}
|
|
966
998
|
function safeJsonParse(text) {
|
|
967
999
|
try {
|
|
968
1000
|
return JSON.parse(text);
|
|
@@ -1098,7 +1130,7 @@ function createDevSessionRoutes(options) {
|
|
|
1098
1130
|
};
|
|
1099
1131
|
}
|
|
1100
1132
|
function validateDevSessionCreatePayload(payload, allowedMiniUrlOrigins) {
|
|
1101
|
-
if (!isRecord$
|
|
1133
|
+
if (!isRecord$2(payload) || Object.keys(payload).some((key) => !['mini_url_origin', 'runtime_permissions'].includes(key))) {
|
|
1102
1134
|
throw new TypeError('Dev Session body is invalid');
|
|
1103
1135
|
}
|
|
1104
1136
|
if (typeof payload.mini_url_origin !== 'string') {
|
|
@@ -1117,11 +1149,11 @@ function validateDevSessionCreatePayload(payload, allowedMiniUrlOrigins) {
|
|
|
1117
1149
|
!allowedMiniUrlOrigins.has(miniUrlOrigin.origin)) {
|
|
1118
1150
|
throw new TypeError('mini_url_origin must be an allowed LAN origin');
|
|
1119
1151
|
}
|
|
1120
|
-
const parsedPermissions =
|
|
1152
|
+
const parsedPermissions = runtimePermissionEnv.parseMiniProgramRuntimePermissions(payload.runtime_permissions);
|
|
1121
1153
|
if (!parsedPermissions.valid) {
|
|
1122
1154
|
throw new TypeError('runtime_permissions is invalid');
|
|
1123
1155
|
}
|
|
1124
|
-
const networkPermission = parsedPermissions.permissions
|
|
1156
|
+
const networkPermission = parsedPermissions.permissions.network;
|
|
1125
1157
|
if (networkPermission?.config.useOfficialDomain !== false) {
|
|
1126
1158
|
throw new TypeError('Dev Session does not allow official domains');
|
|
1127
1159
|
}
|
|
@@ -1573,7 +1605,7 @@ async function selectAvailablePort(options) {
|
|
|
1573
1605
|
port: createPortCandidates(options.startPort, maxAttempts),
|
|
1574
1606
|
});
|
|
1575
1607
|
if (port < options.startPort || port >= options.startPort + maxAttempts) {
|
|
1576
|
-
throw new Error(
|
|
1608
|
+
throw new Error(`无法找到可用端口(${options.label}),起始端口:${options.startPort}`);
|
|
1577
1609
|
}
|
|
1578
1610
|
return port;
|
|
1579
1611
|
}
|
|
@@ -1589,6 +1621,22 @@ const TOKEN_PATTERN = /^[A-Za-z0-9_-]{22}$/;
|
|
|
1589
1621
|
const MAX_TOKEN_GENERATION_ATTEMPTS = 8;
|
|
1590
1622
|
const CLOSE_WINDOW_DELAY_MS = 500;
|
|
1591
1623
|
const CLOSE_WINDOW_PROTOCOL = `heybox://${encodeURIComponent(JSON.stringify({ protocol_type: 'closeWindow' }))}`;
|
|
1624
|
+
const MINI_PROGRAM_URL_QUERY_PARAM$1 = 'mini_url';
|
|
1625
|
+
const MINI_PROGRAM_SDK_VERSION_QUERY_PARAM = 'sdk_version';
|
|
1626
|
+
const MINI_PROGRAM_LOCAL_DEV_LAUNCH_TOKEN_QUERY_PARAM = 'local_dev_launch_token';
|
|
1627
|
+
const MINI_PROGRAM_DEV_SHELL_URL = 'heybox-mini-dev://sandbox';
|
|
1628
|
+
const OPEN_IN_APP_URL = 'https://api.xiaoheihe.cn/open_inapp/';
|
|
1629
|
+
function createMobileAppQrPayload(appUrl, options = {}) {
|
|
1630
|
+
const protocol = createHeyboxProtocol(createMiniProgramDevShellOpenWindowPayload(appUrl, options));
|
|
1631
|
+
return `${OPEN_IN_APP_URL}#${protocol}`;
|
|
1632
|
+
}
|
|
1633
|
+
function projectMiniProgramDevContextUrlToPrivateHost(value, host) {
|
|
1634
|
+
const url = parseDevContextUrl(value, 'loopback', 'devContextUrl must be a canonical loopback Dev Session URL');
|
|
1635
|
+
if (!isCanonicalPrivateLanIpv4(host))
|
|
1636
|
+
throw new TypeError('Mobile Dev Context host must be a canonical private IPv4 address');
|
|
1637
|
+
url.hostname = host;
|
|
1638
|
+
return url.href;
|
|
1639
|
+
}
|
|
1592
1640
|
function createMobileLaunchRedirectRegistry(options = {}) {
|
|
1593
1641
|
const capacity = options.capacity ?? DEFAULT_CAPACITY;
|
|
1594
1642
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
@@ -1673,6 +1721,69 @@ function createMobileLaunchRedirectDocument(payload) {
|
|
|
1673
1721
|
const closeUrl = JSON.stringify(CLOSE_WINDOW_PROTOCOL).replaceAll('<', '\\u003c');
|
|
1674
1722
|
return `<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>打开调试小程序</title></head><body><p>正在打开本地调试小程序…</p><p><a href="${href}" id="launch">如果没有自动跳转,请点这里</a></p><script>(function(){var launchUrl=${launchUrl};var closeUrl=${closeUrl};function go(){location.href=launchUrl;setTimeout(function(){location.href=closeUrl;},${CLOSE_WINDOW_DELAY_MS});}document.getElementById("launch").addEventListener("click",function(event){event.preventDefault();go();});go();})();</script></body></html>`;
|
|
1675
1723
|
}
|
|
1724
|
+
function createMiniProgramDevShellOpenWindowPayload(appUrl, options) {
|
|
1725
|
+
const devShellUrl = `${MINI_PROGRAM_DEV_SHELL_URL}?${readDevShellQueryEntries(appUrl, options)
|
|
1726
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
1727
|
+
.join('&')}`;
|
|
1728
|
+
const miniProgramId = options.miniProgramId?.trim();
|
|
1729
|
+
return {
|
|
1730
|
+
protocol_type: 'openWindow',
|
|
1731
|
+
full_screen: true,
|
|
1732
|
+
mini_program: '1',
|
|
1733
|
+
...(miniProgramId ? { mini_program_id: miniProgramId } : {}),
|
|
1734
|
+
navigation_bar: { title: '' },
|
|
1735
|
+
webview: { url: devShellUrl, pull: false, refresh: false },
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
function readDevShellQueryEntries(appUrl, options) {
|
|
1739
|
+
const entries = [[MINI_PROGRAM_URL_QUERY_PARAM$1, appUrl]];
|
|
1740
|
+
const miniProgramId = options.miniProgramId?.trim();
|
|
1741
|
+
if (miniProgramId)
|
|
1742
|
+
entries.push(['mini_program_id', miniProgramId]);
|
|
1743
|
+
const localDevLaunchToken = options.localDevLaunchToken?.trim();
|
|
1744
|
+
if (localDevLaunchToken)
|
|
1745
|
+
entries.push([MINI_PROGRAM_LOCAL_DEV_LAUNCH_TOKEN_QUERY_PARAM, localDevLaunchToken]);
|
|
1746
|
+
const devContextUrl = readDevContextUrl(options.devContextUrl);
|
|
1747
|
+
if (devContextUrl)
|
|
1748
|
+
entries.push([MINI_PROGRAM_DEV_CONTEXT_QUERY_PARAM, devContextUrl]);
|
|
1749
|
+
const sdkVersion = options.sdkVersion?.trim();
|
|
1750
|
+
if (sdkVersion)
|
|
1751
|
+
entries.push([MINI_PROGRAM_SDK_VERSION_QUERY_PARAM, sdkVersion]);
|
|
1752
|
+
return entries;
|
|
1753
|
+
}
|
|
1754
|
+
function readDevContextUrl(value) {
|
|
1755
|
+
if (value === undefined || value === '')
|
|
1756
|
+
return undefined;
|
|
1757
|
+
return parseDevContextUrl(value, 'loopback-or-private', 'devContextUrl must be a canonical Dev Session URL').href;
|
|
1758
|
+
}
|
|
1759
|
+
function parseDevContextUrl(value, hostPolicy, errorMessage) {
|
|
1760
|
+
if (value !== value.trim())
|
|
1761
|
+
throw new TypeError(errorMessage);
|
|
1762
|
+
let url;
|
|
1763
|
+
try {
|
|
1764
|
+
url = new URL(value);
|
|
1765
|
+
}
|
|
1766
|
+
catch {
|
|
1767
|
+
throw new TypeError(errorMessage);
|
|
1768
|
+
}
|
|
1769
|
+
const tokenPath = `${MINI_PROGRAM_DEV_CONTEXT_PATH}/`;
|
|
1770
|
+
const hostAllowed = isLoopbackHostname(url.hostname) || (hostPolicy === 'loopback-or-private' && isCanonicalPrivateLanIpv4(url.hostname));
|
|
1771
|
+
if (url.protocol !== 'http:' ||
|
|
1772
|
+
url.username !== '' ||
|
|
1773
|
+
url.password !== '' ||
|
|
1774
|
+
url.search !== '' ||
|
|
1775
|
+
url.hash !== '' ||
|
|
1776
|
+
!url.pathname.startsWith(tokenPath) ||
|
|
1777
|
+
!/^[a-f0-9]{64}$/.test(url.pathname.slice(tokenPath.length)) ||
|
|
1778
|
+
!hostAllowed ||
|
|
1779
|
+
url.href !== value) {
|
|
1780
|
+
throw new TypeError(errorMessage);
|
|
1781
|
+
}
|
|
1782
|
+
return url;
|
|
1783
|
+
}
|
|
1784
|
+
function createHeyboxProtocol(payload) {
|
|
1785
|
+
return `heybox://${encodeURIComponent(JSON.stringify(payload))}`;
|
|
1786
|
+
}
|
|
1676
1787
|
function escapeHtml(value) {
|
|
1677
1788
|
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
|
|
1678
1789
|
}
|
|
@@ -1713,13 +1824,12 @@ function assertAddressId(addressId) {
|
|
|
1713
1824
|
}
|
|
1714
1825
|
}
|
|
1715
1826
|
|
|
1716
|
-
const MINI_PROGRAM_URL_QUERY_PARAM
|
|
1827
|
+
const MINI_PROGRAM_URL_QUERY_PARAM = 'mini_url';
|
|
1717
1828
|
const BROWSER_DEV_HOST_BOOTSTRAP_PATH = '/__hb_sdk_bootstrap__';
|
|
1718
1829
|
const BROWSER_DEV_HOST_CHANNEL_PATH = '/__hb_sdk__/channel';
|
|
1719
1830
|
const BROWSER_DEV_HOST_NETWORK_PATH = '/__hb_sdk__/network';
|
|
1720
1831
|
const MOBILE_LAUNCH_REDIRECT_CONTROL_PATH = '/__hb_sdk__/mobile-launch-redirect';
|
|
1721
1832
|
const MOBILE_LAUNCH_REDIRECT_CONTROL_BODY_LIMIT_BYTES = 66 * 1024;
|
|
1722
|
-
const DEV_PERMISSIONS_PATH = '/__hb_sdk__/dev-permissions';
|
|
1723
1833
|
const DEV_LISTEN_HOST$1 = '::';
|
|
1724
1834
|
const DEV_PORT_PROBE_HOST = '0.0.0.0';
|
|
1725
1835
|
const LOCAL_DEV_URL_HOST$1 = '127.0.0.1';
|
|
@@ -1741,7 +1851,8 @@ const DEFAULT_CHANNEL_TRANSPORT = {
|
|
|
1741
1851
|
};
|
|
1742
1852
|
const IPV4_LOOPBACK_ADDRESSES = new net.BlockList();
|
|
1743
1853
|
IPV4_LOOPBACK_ADDRESSES.addSubnet('127.0.0.0', 8, 'ipv4');
|
|
1744
|
-
const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-
|
|
1854
|
+
const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-C5U1Lo4e.cjs', document.baseURI).href)))('ws');
|
|
1855
|
+
const { parse: parseHtml } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-C5U1Lo4e.cjs', document.baseURI).href)))('parse5');
|
|
1745
1856
|
const BROWSER_DEV_HOST_ROOT_CANDIDATES = [
|
|
1746
1857
|
path.resolve(__dirname, 'devtools/browser-dev-host'),
|
|
1747
1858
|
path.resolve(__dirname, '../devtools/browser-dev-host'),
|
|
@@ -1771,7 +1882,6 @@ async function startBrowserDevHostServer(options) {
|
|
|
1771
1882
|
devContext: options.devContext,
|
|
1772
1883
|
lanAddresses: options.lanAddresses ?? [],
|
|
1773
1884
|
loginAvailable: options.loginService !== undefined,
|
|
1774
|
-
macAppProtocol: options.macAppProtocol,
|
|
1775
1885
|
miniProgram: options.miniProgram,
|
|
1776
1886
|
mobileLaunchRedirect: initialMobileLaunchRedirect,
|
|
1777
1887
|
nativeAppLaunchUnavailableReason: options.nativeAppLaunchUnavailableReason,
|
|
@@ -1787,7 +1897,6 @@ async function startBrowserDevHostServer(options) {
|
|
|
1787
1897
|
isMobileLaunchRedirectPeerAllowed: options.isMobileLaunchRedirectPeerAllowed ?? isAllowedMobileLaunchRedirectPeer,
|
|
1788
1898
|
lookupHostname: options.lookupHostname ?? (async (hostname) => promises.lookup(hostname, { all: true, verbatim: true })),
|
|
1789
1899
|
now: options.now ?? Date.now,
|
|
1790
|
-
permissionController: options.permissionController,
|
|
1791
1900
|
channelHeartbeatIntervalMs: options.channelHeartbeatIntervalMs ?? DEFAULT_CHANNEL_HEARTBEAT_INTERVAL_MS,
|
|
1792
1901
|
channelHelloTimeoutMs: options.channelHelloTimeoutMs ?? DEFAULT_CHANNEL_HELLO_TIMEOUT_MS,
|
|
1793
1902
|
channelOutboundBufferLimitBytes: options.channelOutboundBufferLimitBytes ?? DEFAULT_CHANNEL_OUTBOUND_BUFFER_LIMIT_BYTES,
|
|
@@ -1855,10 +1964,6 @@ async function startBrowserDevHostServer(options) {
|
|
|
1855
1964
|
delete bootstrap.mobileLaunchRedirect;
|
|
1856
1965
|
}
|
|
1857
1966
|
}
|
|
1858
|
-
if (next.macAppProtocol === null)
|
|
1859
|
-
delete bootstrap.macAppProtocol;
|
|
1860
|
-
else if (next.macAppProtocol !== undefined)
|
|
1861
|
-
bootstrap.macAppProtocol = next.macAppProtocol;
|
|
1862
1967
|
if (next.miniProgram !== undefined)
|
|
1863
1968
|
bootstrap.miniProgram = next.miniProgram;
|
|
1864
1969
|
bootstrapJson = serializeBrowserDevHostBootstrap(bootstrap);
|
|
@@ -1891,7 +1996,36 @@ function assertCompleteMiniProgramBrowserDevHostRoot(root) {
|
|
|
1891
1996
|
}
|
|
1892
1997
|
}
|
|
1893
1998
|
function isCompleteMiniProgramBrowserDevHostRoot(root) {
|
|
1894
|
-
|
|
1999
|
+
const entryPath = path.join(root, 'index.html');
|
|
2000
|
+
if (!fs.existsSync(entryPath))
|
|
2001
|
+
return false;
|
|
2002
|
+
// 测试夹具与旧发布产物仍使用固定入口;Vue 3 构建使用 HTML 中的哈希资源。
|
|
2003
|
+
if (fs.statSync(path.join(root, 'main.js'), { throwIfNoEntry: false })?.isFile())
|
|
2004
|
+
return true;
|
|
2005
|
+
try {
|
|
2006
|
+
const references = collectBrowserDevHostLocalReferences(parseHtml(fs.readFileSync(entryPath, 'utf8')));
|
|
2007
|
+
return (references.some((reference) => /\.m?js(?:$|\?)/.test(reference)) &&
|
|
2008
|
+
references.every((reference) => {
|
|
2009
|
+
const pathname = decodeURIComponent(reference.split(/[?#]/, 1)[0]);
|
|
2010
|
+
const resolved = path.resolve(root, pathname.replace(/^\/+/, ''));
|
|
2011
|
+
return resolved.startsWith(`${root}${path.sep}`) && fs.statSync(resolved, { throwIfNoEntry: false })?.isFile() === true;
|
|
2012
|
+
}));
|
|
2013
|
+
}
|
|
2014
|
+
catch {
|
|
2015
|
+
return false;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
function collectBrowserDevHostLocalReferences(node, references = []) {
|
|
2019
|
+
for (const attribute of node.attrs ?? []) {
|
|
2020
|
+
if (!['href', 'src'].includes(attribute.name))
|
|
2021
|
+
continue;
|
|
2022
|
+
if (attribute.value && !/^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i.test(attribute.value))
|
|
2023
|
+
references.push(attribute.value);
|
|
2024
|
+
}
|
|
2025
|
+
for (const child of node.childNodes ?? []) {
|
|
2026
|
+
collectBrowserDevHostLocalReferences(child, references);
|
|
2027
|
+
}
|
|
2028
|
+
return [...new Set(references)];
|
|
1895
2029
|
}
|
|
1896
2030
|
function createMissingBrowserDevHostError() {
|
|
1897
2031
|
return new Error('未找到完整的 hb-sdk browser dev host 静态产物。请先执行 @heybox/hb-sdk 的 build:browser-dev-host。');
|
|
@@ -1905,7 +2039,7 @@ function createBrowserDevHostNetworkUrls(options) {
|
|
|
1905
2039
|
}
|
|
1906
2040
|
function createBrowserDevHostUrlWithHost(options) {
|
|
1907
2041
|
const url = new URL(`http://${options.host}:${options.port}/`);
|
|
1908
|
-
url.searchParams.set(MINI_PROGRAM_URL_QUERY_PARAM
|
|
2042
|
+
url.searchParams.set(MINI_PROGRAM_URL_QUERY_PARAM, options.appUrl);
|
|
1909
2043
|
return url.toString();
|
|
1910
2044
|
}
|
|
1911
2045
|
function serializeBrowserDevHostBootstrap(bootstrap) {
|
|
@@ -2015,10 +2149,10 @@ function createBrowserDevHostServer(root, runtime) {
|
|
|
2015
2149
|
writeForbidden(response);
|
|
2016
2150
|
return;
|
|
2017
2151
|
}
|
|
2018
|
-
if (isBrowserDevHostDocumentPath(pathname) && !requestUrl.searchParams.has(MINI_PROGRAM_URL_QUERY_PARAM
|
|
2152
|
+
if (isBrowserDevHostDocumentPath(pathname) && !requestUrl.searchParams.has(MINI_PROGRAM_URL_QUERY_PARAM)) {
|
|
2019
2153
|
const targetUrl = new URL(requestUrl);
|
|
2020
2154
|
targetUrl.pathname = '/';
|
|
2021
|
-
targetUrl.searchParams.set(MINI_PROGRAM_URL_QUERY_PARAM
|
|
2155
|
+
targetUrl.searchParams.set(MINI_PROGRAM_URL_QUERY_PARAM, runtime.appUrl);
|
|
2022
2156
|
response.writeHead(302, {
|
|
2023
2157
|
location: `${targetUrl.pathname}${targetUrl.search}`,
|
|
2024
2158
|
'cache-control': 'no-store',
|
|
@@ -2129,28 +2263,6 @@ function createBrowserDevHostServer(root, runtime) {
|
|
|
2129
2263
|
request.once('close', () => eventStreams.delete(response));
|
|
2130
2264
|
return;
|
|
2131
2265
|
}
|
|
2132
|
-
if (pathname === DEV_PERMISSIONS_PATH && runtime.permissionController) {
|
|
2133
|
-
if (!loopbackPeer || !hasNumericLoopbackHost(request)) {
|
|
2134
|
-
writeForbidden(response);
|
|
2135
|
-
return;
|
|
2136
|
-
}
|
|
2137
|
-
if (request.method === 'GET') {
|
|
2138
|
-
writeJsonResponse(response, 200, await runtime.permissionController.get());
|
|
2139
|
-
return;
|
|
2140
|
-
}
|
|
2141
|
-
if (request.method !== 'PUT' && request.method !== 'DELETE') {
|
|
2142
|
-
writeMethodNotAllowed(response, 'GET, PUT, DELETE');
|
|
2143
|
-
return;
|
|
2144
|
-
}
|
|
2145
|
-
if (!isValidSensitiveRequest(request, loopbackPeer, runtime.sessionController.csrfToken)) {
|
|
2146
|
-
writeForbidden(response);
|
|
2147
|
-
return;
|
|
2148
|
-
}
|
|
2149
|
-
if (await handleDevPermissionMutation(request, response, runtime.permissionController)) {
|
|
2150
|
-
channelServer.publish('status.changed');
|
|
2151
|
-
}
|
|
2152
|
-
return;
|
|
2153
|
-
}
|
|
2154
2266
|
if (pathname === MINI_PROGRAM_DEV_CONTEXT_PATH) {
|
|
2155
2267
|
if (request.method !== 'POST') {
|
|
2156
2268
|
writeMethodNotAllowed(response, 'POST');
|
|
@@ -2531,7 +2643,7 @@ function createBrowserDevHostChannelServer(server, options) {
|
|
|
2531
2643
|
}
|
|
2532
2644
|
async function readBrowserDevHostChannelSnapshot(runtime, cookieHeader) {
|
|
2533
2645
|
const status = await readAccountStatus(runtime, cookieHeader);
|
|
2534
|
-
const permissions =
|
|
2646
|
+
const permissions = context.createBrowserDevPermissionState(runtime.readDevContext());
|
|
2535
2647
|
return {
|
|
2536
2648
|
account: {
|
|
2537
2649
|
revision: status.revision,
|
|
@@ -2549,13 +2661,6 @@ async function readAccountStatus(runtime, cookieHeader) {
|
|
|
2549
2661
|
...(status.authenticated ? { userId: status.userId } : {}),
|
|
2550
2662
|
};
|
|
2551
2663
|
}
|
|
2552
|
-
async function readBrowserDevPermissionState(controller, fallbackContext) {
|
|
2553
|
-
if (!controller)
|
|
2554
|
-
return context.createBrowserDevPermissionState(fallbackContext);
|
|
2555
|
-
const configuration = await controller.get();
|
|
2556
|
-
const runtimePermissions = configuration.override_permissions ?? configuration.remote_permissions;
|
|
2557
|
-
return context.createBrowserDevPermissionState(runtimePermissions === undefined ? { source: configuration.source } : { runtimePermissions });
|
|
2558
|
-
}
|
|
2559
2664
|
function createConnectionEpoch(random) {
|
|
2560
2665
|
const value = random(16).toString('base64url');
|
|
2561
2666
|
if (!/^[A-Za-z0-9_-]{22}$/.test(value))
|
|
@@ -2607,7 +2712,7 @@ function rejectWebSocketUpgrade(socket) {
|
|
|
2607
2712
|
async function handleLoginIntent(request, response, controller) {
|
|
2608
2713
|
try {
|
|
2609
2714
|
const payload = await readJsonRequestBody(request);
|
|
2610
|
-
if (!isRecord$
|
|
2715
|
+
if (!isRecord$2(payload) || Object.keys(payload).some((key) => key !== 'return_path') || typeof payload.return_path !== 'string') {
|
|
2611
2716
|
throw new BrowserDevSessionError(400, 'INVALID_LOGIN_INTENT', 'Browser Dev Host login intent is invalid');
|
|
2612
2717
|
}
|
|
2613
2718
|
const result = await controller.createLoginIntent({
|
|
@@ -2654,8 +2759,7 @@ async function handleLoginCallback(request, response, requestUrl, controller) {
|
|
|
2654
2759
|
}
|
|
2655
2760
|
}
|
|
2656
2761
|
async function handleAuthenticatedRequest(request, response, runtime) {
|
|
2657
|
-
const abort =
|
|
2658
|
-
request.once('aborted', () => abort.abort());
|
|
2762
|
+
const abort = createBrowserDevHostRequestAbortScope(request, response);
|
|
2659
2763
|
try {
|
|
2660
2764
|
const payload = await readJsonRequestBody(request);
|
|
2661
2765
|
const result = runtime.cliAuth
|
|
@@ -2666,6 +2770,9 @@ async function handleAuthenticatedRequest(request, response, runtime) {
|
|
|
2666
2770
|
catch (error) {
|
|
2667
2771
|
writeSessionError(response, error);
|
|
2668
2772
|
}
|
|
2773
|
+
finally {
|
|
2774
|
+
abort.dispose();
|
|
2775
|
+
}
|
|
2669
2776
|
}
|
|
2670
2777
|
async function handleMobileLaunchRedirectRotation(request, response, rotate) {
|
|
2671
2778
|
try {
|
|
@@ -2676,7 +2783,7 @@ async function handleMobileLaunchRedirectRotation(request, response, rotate) {
|
|
|
2676
2783
|
destroyOnLimit: false,
|
|
2677
2784
|
maxBytes: MOBILE_LAUNCH_REDIRECT_CONTROL_BODY_LIMIT_BYTES,
|
|
2678
2785
|
});
|
|
2679
|
-
if (!isRecord$
|
|
2786
|
+
if (!isRecord$2(value) ||
|
|
2680
2787
|
Object.keys(value).length !== 3 ||
|
|
2681
2788
|
typeof value.address_id !== 'string' ||
|
|
2682
2789
|
typeof value.payload !== 'string' ||
|
|
@@ -2730,49 +2837,6 @@ function hasValidCsrf(request, csrfToken) {
|
|
|
2730
2837
|
const value = request.headers['x-hb-sdk-csrf'];
|
|
2731
2838
|
return typeof value === 'string' && value === csrfToken;
|
|
2732
2839
|
}
|
|
2733
|
-
async function handleDevPermissionMutation(request, response, controller) {
|
|
2734
|
-
try {
|
|
2735
|
-
if (request.method === 'DELETE') {
|
|
2736
|
-
if (hasRequestBody(request)) {
|
|
2737
|
-
if (!request.headers['content-type']?.toLowerCase().startsWith('application/json')) {
|
|
2738
|
-
throw createBrowserDevHostError(400, 'INVALID_DEV_PERMISSIONS', 'Dev permissions body must be JSON');
|
|
2739
|
-
}
|
|
2740
|
-
const payload = await readJsonRequestBody(request);
|
|
2741
|
-
if (!isRecord$3(payload) || Object.keys(payload).length > 0) {
|
|
2742
|
-
throw createBrowserDevHostError(400, 'INVALID_DEV_PERMISSIONS', 'Dev permissions reset body is invalid');
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
writeJsonResponse(response, 200, await controller.reset());
|
|
2746
|
-
return true;
|
|
2747
|
-
}
|
|
2748
|
-
if (!request.headers['content-type']?.toLowerCase().startsWith('application/json')) {
|
|
2749
|
-
throw createBrowserDevHostError(400, 'INVALID_DEV_PERMISSIONS', 'Dev permissions body must be JSON');
|
|
2750
|
-
}
|
|
2751
|
-
const payload = await readJsonRequestBody(request);
|
|
2752
|
-
if (!isRecord$3(payload) || Object.keys(payload).length !== 1 || !Object.hasOwn(payload, 'runtime_permissions')) {
|
|
2753
|
-
throw createBrowserDevHostError(400, 'INVALID_DEV_PERMISSIONS', 'Dev permissions update body is invalid');
|
|
2754
|
-
}
|
|
2755
|
-
const parsed = context.parseMiniProgramRuntimePermissions(payload.runtime_permissions);
|
|
2756
|
-
if (!parsed.valid) {
|
|
2757
|
-
throw createBrowserDevHostError(400, 'INVALID_DEV_PERMISSIONS', 'runtime_permissions is invalid');
|
|
2758
|
-
}
|
|
2759
|
-
const runtimePermissions = JSON.parse(JSON.stringify(payload.runtime_permissions));
|
|
2760
|
-
writeJsonResponse(response, 200, await controller.save(runtimePermissions));
|
|
2761
|
-
return true;
|
|
2762
|
-
}
|
|
2763
|
-
catch (error) {
|
|
2764
|
-
const errorStatus = readBrowserDevHostErrorStatus(error);
|
|
2765
|
-
const status = request.method === 'DELETE' && errorStatus === 500 ? 502 : errorStatus;
|
|
2766
|
-
writeJsonResponse(response, status, {
|
|
2767
|
-
code: request.method === 'DELETE' ? 'DEV_PERMISSIONS_RESET_FAILED' : 'DEV_PERMISSIONS_UPDATE_FAILED',
|
|
2768
|
-
message: readErrorMessage(error),
|
|
2769
|
-
});
|
|
2770
|
-
return false;
|
|
2771
|
-
}
|
|
2772
|
-
}
|
|
2773
|
-
function hasRequestBody(request) {
|
|
2774
|
-
return request.headers['transfer-encoding'] !== undefined || Number(request.headers['content-length'] ?? 0) > 0;
|
|
2775
|
-
}
|
|
2776
2840
|
function writeMethodNotAllowed(response, allowedMethod) {
|
|
2777
2841
|
response.writeHead(405, {
|
|
2778
2842
|
allow: allowedMethod,
|
|
@@ -2904,7 +2968,7 @@ async function listenHttpServer(server, options, getPortImpl, listen) {
|
|
|
2904
2968
|
const port = await selectAvailablePort({
|
|
2905
2969
|
getPort: getPortImpl,
|
|
2906
2970
|
host: DEV_PORT_PROBE_HOST,
|
|
2907
|
-
label: '
|
|
2971
|
+
label: '浏览器调试页服务',
|
|
2908
2972
|
maxAttempts: lastCandidate - nextCandidate + 1,
|
|
2909
2973
|
startPort: nextCandidate,
|
|
2910
2974
|
});
|
|
@@ -2923,7 +2987,7 @@ async function listenHttpServer(server, options, getPortImpl, listen) {
|
|
|
2923
2987
|
nextCandidate = port + 1;
|
|
2924
2988
|
}
|
|
2925
2989
|
}
|
|
2926
|
-
throw lastAddressInUseError ?? new Error(
|
|
2990
|
+
throw lastAddressInUseError ?? new Error(`无法找到可用浏览器调试页服务端口,起始端口:${options.port}`);
|
|
2927
2991
|
}
|
|
2928
2992
|
const listenBrowserDevHostServer = (server, options) => new Promise((resolve, reject) => {
|
|
2929
2993
|
const onError = (error) => {
|
|
@@ -4819,7 +4883,7 @@ const DEFAULT_RUNTIME_SANITIZE_MAX_STRING_LENGTH = 2000;
|
|
|
4819
4883
|
*
|
|
4820
4884
|
* @param value - 待判断的值。
|
|
4821
4885
|
*/
|
|
4822
|
-
function isRecord$
|
|
4886
|
+
function isRecord$1(value) {
|
|
4823
4887
|
return Boolean(value && typeof value === 'object');
|
|
4824
4888
|
}
|
|
4825
4889
|
/**
|
|
@@ -4939,14 +5003,14 @@ function normalizeRuntimeError(error, options = {}) {
|
|
|
4939
5003
|
if (error instanceof RuntimeError) {
|
|
4940
5004
|
return error;
|
|
4941
5005
|
}
|
|
4942
|
-
if (isRecord$
|
|
5006
|
+
if (isRecord$1(error)) {
|
|
4943
5007
|
const code = readString(error, 'code') ?? options.code ?? 'runtime.error.unknown';
|
|
4944
5008
|
const message = readString(error, 'message') ?? options.message ?? 'Unknown runtime error';
|
|
4945
5009
|
const domain = readString(error, 'domain') ?? options.domain ?? inferRuntimeErrorDomain(code);
|
|
4946
5010
|
const severity = readString(error, 'severity') ?? options.severity;
|
|
4947
5011
|
const details = sanitizeRuntimeRecord({
|
|
4948
5012
|
...options.details,
|
|
4949
|
-
...((isRecord$
|
|
5013
|
+
...((isRecord$1(error.details) ? error.details : undefined) ?? {}),
|
|
4950
5014
|
});
|
|
4951
5015
|
return createRuntimeError({
|
|
4952
5016
|
cause: error instanceof Error ? error.cause : error.cause,
|
|
@@ -5016,7 +5080,7 @@ function serializeRuntimeError(error, options = {}, depth = 0) {
|
|
|
5016
5080
|
const normalized = normalizeRuntimeError(error);
|
|
5017
5081
|
const maxCauseDepth = options.maxCauseDepth ?? 4;
|
|
5018
5082
|
const cause = depth < maxCauseDepth
|
|
5019
|
-
? (normalized.cause ?? (isRecord$
|
|
5083
|
+
? (normalized.cause ?? (isRecord$1(error) && 'cause' in error ? error.cause : undefined))
|
|
5020
5084
|
: undefined;
|
|
5021
5085
|
const details = sanitizeRuntimeRecord(normalized.details, options);
|
|
5022
5086
|
const serialized = {
|
|
@@ -5153,6 +5217,9 @@ function resolveRuntimeLogTelemetryReportId(entry) {
|
|
|
5153
5217
|
function shouldReportRuntimeLogToTelemetry(entry, options = {}) {
|
|
5154
5218
|
const minLevel = options.minLevel ?? DEFAULT_RUNTIME_LOG_TELEMETRY_MIN_LEVEL;
|
|
5155
5219
|
const ignoreMessages = options.ignoreMessages ?? [...DEFAULT_RUNTIME_LOG_TELEMETRY_IGNORE_MESSAGES];
|
|
5220
|
+
if (options.shouldReport && !options.shouldReport(entry)) {
|
|
5221
|
+
return false;
|
|
5222
|
+
}
|
|
5156
5223
|
return isRuntimeLogLevelEnabled(entry.level, minLevel) && !ignoreMessages.some((pattern) => pattern.test(entry.message));
|
|
5157
5224
|
}
|
|
5158
5225
|
/**
|
|
@@ -5454,10 +5521,14 @@ function createTelemetryRuntimeLogSink(telemetry, options = {}) {
|
|
|
5454
5521
|
severity: entry.level,
|
|
5455
5522
|
});
|
|
5456
5523
|
const { requestId, ...reportContext } = entry.context ?? {};
|
|
5524
|
+
// 调用方可在上下文里显式给出聚合指纹(例如全局错误捕获已按统一规则算好);
|
|
5525
|
+
// 显式值优先于按本条日志错误推导的指纹,避免同一错误在直接上报与 sink 转发
|
|
5526
|
+
// 两条出口下落到不同 issue。
|
|
5527
|
+
const contextFingerprint = reportContext.runtime_error_fingerprint;
|
|
5457
5528
|
currentTelemetry.reportError({
|
|
5458
5529
|
addition: {
|
|
5459
5530
|
...reportContext,
|
|
5460
|
-
runtime_error_fingerprint: entry.errorInfo?.fingerprint,
|
|
5531
|
+
runtime_error_fingerprint: typeof contextFingerprint === 'string' && contextFingerprint ? contextFingerprint : entry.errorInfo?.fingerprint,
|
|
5461
5532
|
runtime_log_domain: entry.domain,
|
|
5462
5533
|
runtime_log_id: entry.id,
|
|
5463
5534
|
runtime_log_level: entry.level,
|
|
@@ -5686,6 +5757,7 @@ function createEndpointObservationConfig(endpoint, options = {}) {
|
|
|
5686
5757
|
errorMode: options.errorMode ?? endpoint.errorMode,
|
|
5687
5758
|
failurePresentation: options.failurePresentation ?? endpoint.failurePresentation,
|
|
5688
5759
|
jsonParseMode: options.jsonParseMode ?? endpoint.jsonParseMode,
|
|
5760
|
+
maxResponseBytes: options.maxResponseBytes ?? endpoint.maxResponseBytes,
|
|
5689
5761
|
metadata: {
|
|
5690
5762
|
...options.metadata,
|
|
5691
5763
|
endpointId: endpoint.id,
|
|
@@ -5693,10 +5765,12 @@ function createEndpointObservationConfig(endpoint, options = {}) {
|
|
|
5693
5765
|
method: endpoint.method,
|
|
5694
5766
|
observability: options.observability ?? endpoint.observability,
|
|
5695
5767
|
queryEncoding: options.queryEncoding ?? endpoint.queryEncoding,
|
|
5768
|
+
redirectPolicy: options.redirectPolicy ?? endpoint.redirectPolicy,
|
|
5696
5769
|
responseType: options.responseType ?? endpoint.responseType,
|
|
5697
5770
|
security: options.security ?? endpoint.security,
|
|
5698
5771
|
timeout: options.timeout ?? endpoint.timeout,
|
|
5699
5772
|
url: typeof endpoint.path === 'string' ? endpoint.path : `endpoint:${endpoint.id}`,
|
|
5773
|
+
withCredentials: options.withCredentials ?? endpoint.withCredentials,
|
|
5700
5774
|
};
|
|
5701
5775
|
}
|
|
5702
5776
|
/**
|
|
@@ -5783,13 +5857,16 @@ async function createEndpointConfig(endpoint, input, options = {}) {
|
|
|
5783
5857
|
},
|
|
5784
5858
|
method: endpoint.method,
|
|
5785
5859
|
jsonParseMode: options.jsonParseMode ?? endpoint.jsonParseMode,
|
|
5860
|
+
maxResponseBytes: options.maxResponseBytes ?? endpoint.maxResponseBytes,
|
|
5786
5861
|
observability: options.observability ?? endpoint.observability,
|
|
5787
5862
|
query: mergeRecord(await endpoint.query?.(input), options.params, options.query),
|
|
5788
5863
|
queryEncoding: options.queryEncoding ?? endpoint.queryEncoding,
|
|
5864
|
+
redirectPolicy: options.redirectPolicy ?? endpoint.redirectPolicy,
|
|
5789
5865
|
responseType: options.responseType ?? endpoint.responseType,
|
|
5790
5866
|
security: options.security ?? endpoint.security,
|
|
5791
5867
|
timeout: options.timeout ?? endpoint.timeout,
|
|
5792
5868
|
url: getEndpointPath(endpoint, input),
|
|
5869
|
+
withCredentials: options.withCredentials ?? endpoint.withCredentials,
|
|
5793
5870
|
};
|
|
5794
5871
|
}
|
|
5795
5872
|
/**
|
|
@@ -5993,15 +6070,18 @@ function createRuntimeRequest(options) {
|
|
|
5993
6070
|
channel: transportConfig.channel,
|
|
5994
6071
|
headers: transportConfig.headers,
|
|
5995
6072
|
jsonParseMode: transportConfig.jsonParseMode,
|
|
6073
|
+
maxResponseBytes: transportConfig.maxResponseBytes,
|
|
5996
6074
|
metadata: transportConfig.metadata,
|
|
5997
6075
|
method: transportConfig.method,
|
|
5998
6076
|
query: transportConfig.query,
|
|
5999
6077
|
queryEncoding: transportConfig.queryEncoding,
|
|
6078
|
+
redirectPolicy: transportConfig.redirectPolicy,
|
|
6000
6079
|
responseType: transportConfig.responseType,
|
|
6001
6080
|
signal: transportConfig.signal,
|
|
6002
6081
|
timeout: transportConfig.timeout,
|
|
6003
6082
|
traceId: transportConfig.traceId,
|
|
6004
6083
|
url: transportConfig.url,
|
|
6084
|
+
withCredentials: transportConfig.withCredentials,
|
|
6005
6085
|
});
|
|
6006
6086
|
return {
|
|
6007
6087
|
fallbackUsed: transportIndex > 0,
|
|
@@ -7867,7 +7947,7 @@ const DEFAULT_NODE_RUNTIME_CONFIG_VALUE_PREFIX = 'RUNTIME_VALUE_';
|
|
|
7867
7947
|
*
|
|
7868
7948
|
* @param value - 待判断的值。
|
|
7869
7949
|
*/
|
|
7870
|
-
function isRecord
|
|
7950
|
+
function isRecord(value) {
|
|
7871
7951
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
7872
7952
|
}
|
|
7873
7953
|
/**
|
|
@@ -7987,7 +8067,7 @@ function parseEnvValue(value) {
|
|
|
7987
8067
|
* @param value - 原始值。
|
|
7988
8068
|
*/
|
|
7989
8069
|
function normalizeFlags(value) {
|
|
7990
|
-
if (!isRecord
|
|
8070
|
+
if (!isRecord(value)) {
|
|
7991
8071
|
return undefined;
|
|
7992
8072
|
}
|
|
7993
8073
|
const flags = {};
|
|
@@ -8004,11 +8084,11 @@ function normalizeFlags(value) {
|
|
|
8004
8084
|
* @param value - 原始值。
|
|
8005
8085
|
*/
|
|
8006
8086
|
function normalizeConfigInput(value) {
|
|
8007
|
-
if (!isRecord
|
|
8087
|
+
if (!isRecord(value)) {
|
|
8008
8088
|
return undefined;
|
|
8009
8089
|
}
|
|
8010
8090
|
const input = {};
|
|
8011
|
-
if (isRecord
|
|
8091
|
+
if (isRecord(value.build)) {
|
|
8012
8092
|
input.build = value.build;
|
|
8013
8093
|
}
|
|
8014
8094
|
if (typeof value.channel === 'string') {
|
|
@@ -8017,16 +8097,16 @@ function normalizeConfigInput(value) {
|
|
|
8017
8097
|
if (typeof value.env === 'string') {
|
|
8018
8098
|
input.env = value.env;
|
|
8019
8099
|
}
|
|
8020
|
-
if (isRecord
|
|
8100
|
+
if (isRecord(value.flags)) {
|
|
8021
8101
|
input.flags = normalizeFlags(value.flags);
|
|
8022
8102
|
}
|
|
8023
8103
|
if (typeof value.isProd === 'boolean') {
|
|
8024
8104
|
input.isProd = value.isProd;
|
|
8025
8105
|
}
|
|
8026
|
-
if (isRecord
|
|
8106
|
+
if (isRecord(value.tenant)) {
|
|
8027
8107
|
input.tenant = value.tenant;
|
|
8028
8108
|
}
|
|
8029
|
-
if (isRecord
|
|
8109
|
+
if (isRecord(value.values)) {
|
|
8030
8110
|
input.values = value.values;
|
|
8031
8111
|
}
|
|
8032
8112
|
return hasConfigInput(input) ? input : undefined;
|
|
@@ -8570,23 +8650,47 @@ function createFetchRequestControl(signal, timeout) {
|
|
|
8570
8650
|
* @param response - transport 响应对象。
|
|
8571
8651
|
* @param responseType - responseType 参数。
|
|
8572
8652
|
*/
|
|
8573
|
-
async function parseFetchResponse(response, responseType, jsonParseMode) {
|
|
8653
|
+
async function parseFetchResponse(response, responseType, jsonParseMode, maxResponseBytes) {
|
|
8654
|
+
const limit = normalizeMaxResponseBytes(maxResponseBytes);
|
|
8574
8655
|
if (responseType === 'raw') {
|
|
8656
|
+
if (limit !== undefined) {
|
|
8657
|
+
throw createRuntimeError({
|
|
8658
|
+
code: 'runtime.transport.raw_response_limit_unsupported',
|
|
8659
|
+
domain: 'transport',
|
|
8660
|
+
message: 'Raw fetch response cannot enforce maxResponseBytes',
|
|
8661
|
+
recoverable: false,
|
|
8662
|
+
severity: 'error',
|
|
8663
|
+
});
|
|
8664
|
+
}
|
|
8575
8665
|
return response;
|
|
8576
8666
|
}
|
|
8577
8667
|
if (response.status === 204) {
|
|
8578
8668
|
return undefined;
|
|
8579
8669
|
}
|
|
8580
|
-
if (responseType === 'text') {
|
|
8670
|
+
if (limit === undefined && responseType === 'text') {
|
|
8581
8671
|
return response.text();
|
|
8582
8672
|
}
|
|
8583
|
-
if (responseType === 'blob') {
|
|
8673
|
+
if (limit === undefined && responseType === 'blob') {
|
|
8584
8674
|
return response.blob();
|
|
8585
8675
|
}
|
|
8586
|
-
if (responseType === 'arrayBuffer') {
|
|
8676
|
+
if (limit === undefined && responseType === 'arrayBuffer') {
|
|
8587
8677
|
return response.arrayBuffer();
|
|
8588
8678
|
}
|
|
8589
|
-
|
|
8679
|
+
if (limit === undefined) {
|
|
8680
|
+
return parseFetchText(await response.text(), responseType, jsonParseMode);
|
|
8681
|
+
}
|
|
8682
|
+
const bytes = await readBoundedResponse(response, limit);
|
|
8683
|
+
if (responseType === 'arrayBuffer')
|
|
8684
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
8685
|
+
if (responseType === 'blob') {
|
|
8686
|
+
const blobBytes = bytes.slice().buffer;
|
|
8687
|
+
return new Blob([blobBytes], { type: response.headers.get('content-type') ?? '' });
|
|
8688
|
+
}
|
|
8689
|
+
return parseFetchText(new TextDecoder().decode(bytes), responseType, jsonParseMode);
|
|
8690
|
+
}
|
|
8691
|
+
function parseFetchText(text, responseType, jsonParseMode) {
|
|
8692
|
+
if (responseType === 'text')
|
|
8693
|
+
return text;
|
|
8590
8694
|
if (!text) {
|
|
8591
8695
|
return undefined;
|
|
8592
8696
|
}
|
|
@@ -8600,6 +8704,62 @@ async function parseFetchResponse(response, responseType, jsonParseMode) {
|
|
|
8600
8704
|
throw error;
|
|
8601
8705
|
}
|
|
8602
8706
|
}
|
|
8707
|
+
function normalizeMaxResponseBytes(value) {
|
|
8708
|
+
if (value === undefined)
|
|
8709
|
+
return undefined;
|
|
8710
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
8711
|
+
throw new TypeError('maxResponseBytes must be a positive safe integer');
|
|
8712
|
+
return value;
|
|
8713
|
+
}
|
|
8714
|
+
function responseTooLarge(maxResponseBytes, receivedBytes) {
|
|
8715
|
+
return createRuntimeError({
|
|
8716
|
+
code: 'runtime.transport.response_too_large',
|
|
8717
|
+
details: {
|
|
8718
|
+
maxResponseBytes,
|
|
8719
|
+
...(receivedBytes === undefined ? {} : { receivedBytes }),
|
|
8720
|
+
},
|
|
8721
|
+
domain: 'transport',
|
|
8722
|
+
message: `Fetch response exceeds ${maxResponseBytes} bytes`,
|
|
8723
|
+
recoverable: false,
|
|
8724
|
+
severity: 'error',
|
|
8725
|
+
});
|
|
8726
|
+
}
|
|
8727
|
+
async function readBoundedResponse(response, maxResponseBytes) {
|
|
8728
|
+
const declared = response.headers.get('content-length');
|
|
8729
|
+
if (declared && /^(?:0|[1-9]\d*)$/.test(declared)) {
|
|
8730
|
+
const declaredBytes = Number(declared);
|
|
8731
|
+
if (Number.isSafeInteger(declaredBytes) && declaredBytes > maxResponseBytes)
|
|
8732
|
+
throw responseTooLarge(maxResponseBytes, declaredBytes);
|
|
8733
|
+
}
|
|
8734
|
+
const reader = response.body?.getReader();
|
|
8735
|
+
if (!reader)
|
|
8736
|
+
return new Uint8Array();
|
|
8737
|
+
const chunks = [];
|
|
8738
|
+
let receivedBytes = 0;
|
|
8739
|
+
try {
|
|
8740
|
+
while (true) {
|
|
8741
|
+
const next = await reader.read();
|
|
8742
|
+
if (next.done)
|
|
8743
|
+
break;
|
|
8744
|
+
receivedBytes += next.value.byteLength;
|
|
8745
|
+
if (receivedBytes > maxResponseBytes) {
|
|
8746
|
+
await reader.cancel().catch(() => undefined);
|
|
8747
|
+
throw responseTooLarge(maxResponseBytes, receivedBytes);
|
|
8748
|
+
}
|
|
8749
|
+
chunks.push(next.value);
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8752
|
+
finally {
|
|
8753
|
+
reader.releaseLock();
|
|
8754
|
+
}
|
|
8755
|
+
const result = new Uint8Array(receivedBytes);
|
|
8756
|
+
let offset = 0;
|
|
8757
|
+
for (const chunk of chunks) {
|
|
8758
|
+
result.set(chunk, offset);
|
|
8759
|
+
offset += chunk.byteLength;
|
|
8760
|
+
}
|
|
8761
|
+
return result;
|
|
8762
|
+
}
|
|
8603
8763
|
/**
|
|
8604
8764
|
* 创建基于 fetch 的 runtime transport。
|
|
8605
8765
|
*
|
|
@@ -8638,13 +8798,14 @@ function createFetchRuntimeTransport(options = {}) {
|
|
|
8638
8798
|
try {
|
|
8639
8799
|
const response = await requestControl.wait(fetcher(url, {
|
|
8640
8800
|
body: createRequestBody(request.body, headers, request.bodyEncoding),
|
|
8641
|
-
credentials: options.credentials,
|
|
8801
|
+
credentials: request.withCredentials === undefined ? options.credentials : request.withCredentials ? 'include' : 'omit',
|
|
8642
8802
|
headers,
|
|
8643
8803
|
method: request.method ?? 'GET',
|
|
8804
|
+
...(request.redirectPolicy === undefined ? {} : { redirect: request.redirectPolicy }),
|
|
8644
8805
|
...(requestControl.signal ? { signal: requestControl.signal } : undefined),
|
|
8645
8806
|
}));
|
|
8646
8807
|
return {
|
|
8647
|
-
data: await parseFetchResponse(response, request.responseType ?? 'json', request.jsonParseMode ?? 'strict'),
|
|
8808
|
+
data: await parseFetchResponse(response, request.responseType ?? 'json', request.jsonParseMode ?? 'strict', request.maxResponseBytes),
|
|
8648
8809
|
headers: Object.fromEntries(response.headers.entries()),
|
|
8649
8810
|
raw: response,
|
|
8650
8811
|
request: {
|
|
@@ -10741,7 +10902,7 @@ function assertNodeRuntimeCapabilities(ctx, settings) {
|
|
|
10741
10902
|
function createNodeRealtimeDriverRegistry() {
|
|
10742
10903
|
const registry = createRuntimeRealtimeDriverRegistry();
|
|
10743
10904
|
registry.registerLazy('node-websocket', async () => {
|
|
10744
|
-
const mod = await Promise.resolve().then(function () { return require('./index.esm-
|
|
10905
|
+
const mod = await Promise.resolve().then(function () { return require('./index.esm-DiX5WKSp.cjs'); });
|
|
10745
10906
|
return mod.createNodeWebSocketRuntimeDriver();
|
|
10746
10907
|
});
|
|
10747
10908
|
return registry;
|
|
@@ -14527,6 +14688,7 @@ const websitePolicy = {
|
|
|
14527
14688
|
const CREDENTIAL_HEADER_RE = /^(?:authentication-info|authorization|cookie|cookie2|proxy-authenticate|proxy-authentication-info|proxy-authorization|set-cookie|set-cookie2|www-authenticate|pkey|x-pkey|x-access-token|x-auth-token|x-api-key)$/i;
|
|
14528
14689
|
function createBrowserDevHostCliAuth(options = {}) {
|
|
14529
14690
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
14691
|
+
const noRedirectFetch = (input, init) => fetchImpl(input, { ...init, redirect: 'manual' });
|
|
14530
14692
|
const store = options.store ?? { env: options.env };
|
|
14531
14693
|
const readSession = options.readSession ?? (() => session.readHeyboxAuthSession(store));
|
|
14532
14694
|
let lastKey = '';
|
|
@@ -14538,7 +14700,7 @@ function createBrowserDevHostCliAuth(options = {}) {
|
|
|
14538
14700
|
runtimePromise ??= createNodeProcessRuntime({
|
|
14539
14701
|
appId: 'hb-sdk-browser-dev-host',
|
|
14540
14702
|
environment: createNodeProcessRuntimeEnvironment({
|
|
14541
|
-
transport: createFetchRuntimeTransport({ fetch:
|
|
14703
|
+
transport: createFetchRuntimeTransport({ fetch: noRedirectFetch }),
|
|
14542
14704
|
}),
|
|
14543
14705
|
identity: {
|
|
14544
14706
|
container: 'browser',
|
|
@@ -14609,10 +14771,10 @@ async function readCliUserProfile(runtime, session) {
|
|
|
14609
14771
|
}
|
|
14610
14772
|
}
|
|
14611
14773
|
function readCliUserProfileFields(value) {
|
|
14612
|
-
if (!isRecord$
|
|
14774
|
+
if (!isRecord$2(value))
|
|
14613
14775
|
return undefined;
|
|
14614
|
-
const data = isRecord$
|
|
14615
|
-
const result = isRecord$
|
|
14776
|
+
const data = isRecord$2(value.data) ? value.data : value;
|
|
14777
|
+
const result = isRecord$2(data.result) ? data.result : data;
|
|
14616
14778
|
const nickname = typeof result.display_username === 'string' ? result.display_username.trim() : '';
|
|
14617
14779
|
const avatar = typeof result.display_avatar === 'string' ? result.display_avatar.trim() : '';
|
|
14618
14780
|
if (!nickname && !avatar)
|
|
@@ -14673,7 +14835,7 @@ async function executeCliHeyboxAuthenticatedRequest(runtime, session, request, s
|
|
|
14673
14835
|
}
|
|
14674
14836
|
function readQueryParams(params) {
|
|
14675
14837
|
const query = {};
|
|
14676
|
-
if (!isRecord$
|
|
14838
|
+
if (!isRecord$2(params))
|
|
14677
14839
|
return query;
|
|
14678
14840
|
for (const [key, value] of Object.entries(params)) {
|
|
14679
14841
|
if (value === undefined || value === null || key === 'heybox_id')
|
|
@@ -14698,111 +14860,6 @@ function containsSecret(value, secret) {
|
|
|
14698
14860
|
}
|
|
14699
14861
|
}
|
|
14700
14862
|
|
|
14701
|
-
const MINI_PROGRAM_URL_QUERY_PARAM = 'mini_url';
|
|
14702
|
-
const MINI_PROGRAM_SDK_VERSION_QUERY_PARAM = 'sdk_version';
|
|
14703
|
-
const MINI_PROGRAM_LOCAL_DEV_LAUNCH_TOKEN_QUERY_PARAM = 'local_dev_launch_token';
|
|
14704
|
-
const MINI_PROGRAM_DEV_SHELL_URL = 'heybox-mini-dev://sandbox';
|
|
14705
|
-
const OPEN_IN_APP_URL = 'https://api.xiaoheihe.cn/open_inapp/';
|
|
14706
|
-
function createMacAppProtocol(appUrl, options = {}) {
|
|
14707
|
-
return createHeyboxProtocol(createMiniProgramDevShellOpenWindowPayload(appUrl, options));
|
|
14708
|
-
}
|
|
14709
|
-
function createMobileAppQrPayload(appUrl, options = {}) {
|
|
14710
|
-
const protocol = createHeyboxProtocol(createMiniProgramDevShellOpenWindowPayload(appUrl, { ...options, encodeMiniUrl: false }));
|
|
14711
|
-
return `${OPEN_IN_APP_URL}#${protocol}`;
|
|
14712
|
-
}
|
|
14713
|
-
function createMiniProgramDevShellOpenWindowPayload(appUrl, options = {}) {
|
|
14714
|
-
const devShellUrl = options.encodeMiniUrl === false
|
|
14715
|
-
? createPartiallyEncodedMiniProgramDevShellUrl(appUrl, options)
|
|
14716
|
-
: createEncodedMiniProgramDevShellUrl(appUrl, options);
|
|
14717
|
-
return createOpenWindowPayload(devShellUrl, options.miniProgramId);
|
|
14718
|
-
}
|
|
14719
|
-
function createOpenWindowPayload(webviewUrl, miniProgramIdValue) {
|
|
14720
|
-
const miniProgramId = miniProgramIdValue?.trim();
|
|
14721
|
-
return {
|
|
14722
|
-
protocol_type: 'openWindow',
|
|
14723
|
-
full_screen: true,
|
|
14724
|
-
mini_program: '1',
|
|
14725
|
-
...(miniProgramId ? { mini_program_id: miniProgramId } : {}),
|
|
14726
|
-
navigation_bar: {
|
|
14727
|
-
title: '',
|
|
14728
|
-
},
|
|
14729
|
-
webview: {
|
|
14730
|
-
url: webviewUrl,
|
|
14731
|
-
pull: false,
|
|
14732
|
-
refresh: false,
|
|
14733
|
-
},
|
|
14734
|
-
};
|
|
14735
|
-
}
|
|
14736
|
-
function readDevShellQueryEntries(appUrl, options) {
|
|
14737
|
-
const entries = [[MINI_PROGRAM_URL_QUERY_PARAM, appUrl]];
|
|
14738
|
-
const miniProgramId = options.miniProgramId?.trim();
|
|
14739
|
-
if (miniProgramId)
|
|
14740
|
-
entries.push(['mini_program_id', miniProgramId]);
|
|
14741
|
-
const localDevLaunchToken = options.localDevLaunchToken?.trim();
|
|
14742
|
-
if (localDevLaunchToken)
|
|
14743
|
-
entries.push([MINI_PROGRAM_LOCAL_DEV_LAUNCH_TOKEN_QUERY_PARAM, localDevLaunchToken]);
|
|
14744
|
-
const devContextUrl = readDevContextUrl(options.devContextUrl);
|
|
14745
|
-
if (devContextUrl)
|
|
14746
|
-
entries.push([MINI_PROGRAM_DEV_CONTEXT_QUERY_PARAM, devContextUrl]);
|
|
14747
|
-
const sdkVersion = options.sdkVersion?.trim();
|
|
14748
|
-
if (sdkVersion)
|
|
14749
|
-
entries.push([MINI_PROGRAM_SDK_VERSION_QUERY_PARAM, sdkVersion]);
|
|
14750
|
-
return entries;
|
|
14751
|
-
}
|
|
14752
|
-
function createPartiallyEncodedMiniProgramDevShellUrl(appUrl, options) {
|
|
14753
|
-
return `${MINI_PROGRAM_DEV_SHELL_URL}?${readDevShellQueryEntries(appUrl, options)
|
|
14754
|
-
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
14755
|
-
.join('&')}`;
|
|
14756
|
-
}
|
|
14757
|
-
function createEncodedMiniProgramDevShellUrl(appUrl, options) {
|
|
14758
|
-
const devShellUrl = new URL(MINI_PROGRAM_DEV_SHELL_URL);
|
|
14759
|
-
for (const [key, value] of readDevShellQueryEntries(appUrl, options)) {
|
|
14760
|
-
devShellUrl.searchParams.set(key, value);
|
|
14761
|
-
}
|
|
14762
|
-
return devShellUrl.toString();
|
|
14763
|
-
}
|
|
14764
|
-
function readDevContextUrl(value) {
|
|
14765
|
-
if (value === undefined || value === '')
|
|
14766
|
-
return undefined;
|
|
14767
|
-
return parseDevContextUrl(value, 'loopback-or-private', 'devContextUrl must be a canonical Dev Session URL').href;
|
|
14768
|
-
}
|
|
14769
|
-
function projectMiniProgramDevContextUrlToPrivateHost(value, host) {
|
|
14770
|
-
const url = parseDevContextUrl(value, 'loopback', 'devContextUrl must be a canonical loopback Dev Session URL');
|
|
14771
|
-
if (!isCanonicalPrivateLanIpv4(host)) {
|
|
14772
|
-
throw new TypeError('Mobile Dev Context host must be a canonical private IPv4 address');
|
|
14773
|
-
}
|
|
14774
|
-
url.hostname = host;
|
|
14775
|
-
return url.href;
|
|
14776
|
-
}
|
|
14777
|
-
function parseDevContextUrl(value, hostPolicy, errorMessage) {
|
|
14778
|
-
if (value !== value.trim())
|
|
14779
|
-
throw new TypeError(errorMessage);
|
|
14780
|
-
let url;
|
|
14781
|
-
try {
|
|
14782
|
-
url = new URL(value);
|
|
14783
|
-
}
|
|
14784
|
-
catch {
|
|
14785
|
-
throw new TypeError(errorMessage);
|
|
14786
|
-
}
|
|
14787
|
-
const tokenPath = `${MINI_PROGRAM_DEV_CONTEXT_PATH}/`;
|
|
14788
|
-
const hostAllowed = isLoopbackHostname(url.hostname) || (hostPolicy === 'loopback-or-private' && isCanonicalPrivateLanIpv4(url.hostname));
|
|
14789
|
-
if (url.protocol !== 'http:' ||
|
|
14790
|
-
url.username !== '' ||
|
|
14791
|
-
url.password !== '' ||
|
|
14792
|
-
url.search !== '' ||
|
|
14793
|
-
url.hash !== '' ||
|
|
14794
|
-
!url.pathname.startsWith(tokenPath) ||
|
|
14795
|
-
!/^[a-f0-9]{64}$/.test(url.pathname.slice(tokenPath.length)) ||
|
|
14796
|
-
!hostAllowed ||
|
|
14797
|
-
url.href !== value) {
|
|
14798
|
-
throw new TypeError(errorMessage);
|
|
14799
|
-
}
|
|
14800
|
-
return url;
|
|
14801
|
-
}
|
|
14802
|
-
function createHeyboxProtocol(payload) {
|
|
14803
|
-
return `heybox://${encodeURIComponent(JSON.stringify(payload))}`;
|
|
14804
|
-
}
|
|
14805
|
-
|
|
14806
14863
|
const LAUNCH_CORS_HEADERS = {
|
|
14807
14864
|
'access-control-allow-methods': 'GET',
|
|
14808
14865
|
'access-control-allow-origin': '*',
|
|
@@ -14858,7 +14915,7 @@ function createLaunchSnapshot(source, requestOrigin) {
|
|
|
14858
14915
|
if (storedContextUrl) {
|
|
14859
14916
|
snapshot.dev_context_url = projectStoredDevContextUrl(storedContextUrl, requestOrigin.hostname);
|
|
14860
14917
|
}
|
|
14861
|
-
if (
|
|
14918
|
+
if (runtimePermissionEnv.parseMiniProgramRuntimePermissions(source.runtimePermissions).valid && source.runtimePermissions) {
|
|
14862
14919
|
snapshot.runtime_permissions = JSON.parse(JSON.stringify(source.runtimePermissions));
|
|
14863
14920
|
}
|
|
14864
14921
|
return snapshot;
|
|
@@ -14905,133 +14962,12 @@ function writeLaunchResponse(response, status, body) {
|
|
|
14905
14962
|
response.end(body ?? '');
|
|
14906
14963
|
}
|
|
14907
14964
|
|
|
14908
|
-
/** 构建时替换为当前发布包的实际版本。 */
|
|
14909
|
-
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
14910
|
-
? undefined
|
|
14911
|
-
: '0.8.0-alpha';
|
|
14912
|
-
|
|
14913
|
-
class DevPermissionOverrideStoreError extends Error {
|
|
14914
|
-
code;
|
|
14915
|
-
constructor(message, code, options = {}) {
|
|
14916
|
-
super(message, { cause: options.cause });
|
|
14917
|
-
this.code = code;
|
|
14918
|
-
this.name = 'DevPermissionOverrideStoreError';
|
|
14919
|
-
}
|
|
14920
|
-
}
|
|
14921
|
-
const CACHE_VERSION = 1;
|
|
14922
|
-
const CACHE_DIRECTORY_NAME = 'dev-permission-overrides';
|
|
14923
|
-
const defaultPaths = index.envPaths('hb-sdk', { suffix: '' });
|
|
14924
|
-
function createDevPermissionOverrideStore(options = {}) {
|
|
14925
|
-
const cacheDirectory = path.join(options.cacheRoot ?? defaultPaths.cache, CACHE_DIRECTORY_NAME);
|
|
14926
|
-
return {
|
|
14927
|
-
async read(scope) {
|
|
14928
|
-
return readScopeFile(await getScopeFile(cacheDirectory, scope));
|
|
14929
|
-
},
|
|
14930
|
-
async remove(scope) {
|
|
14931
|
-
const scopeFile = await getScopeFile(cacheDirectory, scope);
|
|
14932
|
-
try {
|
|
14933
|
-
await fs$1.unlink(scopeFile);
|
|
14934
|
-
}
|
|
14935
|
-
catch (error) {
|
|
14936
|
-
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
14937
|
-
return;
|
|
14938
|
-
}
|
|
14939
|
-
throw createWriteError(error);
|
|
14940
|
-
}
|
|
14941
|
-
},
|
|
14942
|
-
async set(scope, overrides) {
|
|
14943
|
-
const scopeFile = await getScopeFile(cacheDirectory, scope);
|
|
14944
|
-
try {
|
|
14945
|
-
await fs$1.mkdir(cacheDirectory, { recursive: true, mode: 0o700 });
|
|
14946
|
-
await fs$1.chmod(cacheDirectory, 0o700);
|
|
14947
|
-
await readScopeFile(scopeFile);
|
|
14948
|
-
await writeScopeFileAtomically(scopeFile, {
|
|
14949
|
-
version: CACHE_VERSION,
|
|
14950
|
-
overrides: cloneOverrides(overrides),
|
|
14951
|
-
});
|
|
14952
|
-
}
|
|
14953
|
-
catch (error) {
|
|
14954
|
-
if (error instanceof DevPermissionOverrideStoreError) {
|
|
14955
|
-
throw error;
|
|
14956
|
-
}
|
|
14957
|
-
throw createWriteError(error);
|
|
14958
|
-
}
|
|
14959
|
-
},
|
|
14960
|
-
};
|
|
14961
|
-
}
|
|
14962
|
-
async function getScopeFile(cacheDirectory, scope) {
|
|
14963
|
-
const projectRoot = await fs$1.realpath(scope.projectRoot);
|
|
14964
|
-
const miniProgramId = scope.miniProgramId ?? 'anonymous';
|
|
14965
|
-
const scopeHash = node_crypto.createHash('sha256').update(`${projectRoot}\0${miniProgramId}`).digest('hex');
|
|
14966
|
-
return path.join(cacheDirectory, `${scopeHash}.json`);
|
|
14967
|
-
}
|
|
14968
|
-
async function readScopeFile(scopeFile) {
|
|
14969
|
-
let contents;
|
|
14970
|
-
try {
|
|
14971
|
-
contents = await fs$1.readFile(scopeFile, 'utf8');
|
|
14972
|
-
}
|
|
14973
|
-
catch (error) {
|
|
14974
|
-
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
14975
|
-
return undefined;
|
|
14976
|
-
}
|
|
14977
|
-
throw error;
|
|
14978
|
-
}
|
|
14979
|
-
try {
|
|
14980
|
-
const parsed = JSON.parse(contents);
|
|
14981
|
-
const overrides = parsePersistedOverride(parsed);
|
|
14982
|
-
if (!overrides) {
|
|
14983
|
-
throw new TypeError('invalid cache schema');
|
|
14984
|
-
}
|
|
14985
|
-
return overrides;
|
|
14986
|
-
}
|
|
14987
|
-
catch (error) {
|
|
14988
|
-
throw new DevPermissionOverrideStoreError('hb-sdk 本地权限覆盖缓存已损坏', 'DEV_PERMISSION_OVERRIDE_CACHE_CORRUPT', {
|
|
14989
|
-
cause: error,
|
|
14990
|
-
});
|
|
14991
|
-
}
|
|
14992
|
-
}
|
|
14993
|
-
async function writeScopeFileAtomically(scopeFile, value) {
|
|
14994
|
-
const tempFile = `${scopeFile}.${process.pid}.${node_crypto.randomBytes(8).toString('hex')}.tmp`;
|
|
14995
|
-
let handle;
|
|
14996
|
-
try {
|
|
14997
|
-
handle = await fs$1.open(tempFile, 'wx', 0o600);
|
|
14998
|
-
await handle.writeFile(`${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
14999
|
-
await handle.sync();
|
|
15000
|
-
await handle.close();
|
|
15001
|
-
handle = undefined;
|
|
15002
|
-
await fs$1.rename(tempFile, scopeFile);
|
|
15003
|
-
}
|
|
15004
|
-
finally {
|
|
15005
|
-
await handle?.close().catch(() => undefined);
|
|
15006
|
-
await fs$1.unlink(tempFile).catch(() => undefined);
|
|
15007
|
-
}
|
|
15008
|
-
}
|
|
15009
|
-
function createWriteError(cause) {
|
|
15010
|
-
return new DevPermissionOverrideStoreError('无法写入 hb-sdk 本地权限覆盖缓存', 'DEV_PERMISSION_OVERRIDE_WRITE_FAILED', { cause });
|
|
15011
|
-
}
|
|
15012
|
-
function cloneOverrides(overrides) {
|
|
15013
|
-
return {
|
|
15014
|
-
networkRequest: { ...overrides.networkRequest },
|
|
15015
|
-
};
|
|
15016
|
-
}
|
|
15017
|
-
function parsePersistedOverride(value) {
|
|
15018
|
-
if (!isRecord(value) || value.version !== CACHE_VERSION || Object.keys(value).length !== 2)
|
|
15019
|
-
return undefined;
|
|
15020
|
-
return context.parseBrowserDevPermissionState(value.overrides);
|
|
15021
|
-
}
|
|
15022
|
-
function isRecord(value) {
|
|
15023
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
15024
|
-
}
|
|
15025
|
-
function isNodeError(error) {
|
|
15026
|
-
return error instanceof Error && 'code' in error;
|
|
15027
|
-
}
|
|
15028
|
-
|
|
15029
14965
|
const DEFAULT_APP_PORT = 5173;
|
|
15030
14966
|
const DEFAULT_BROWSER_DEV_HOST_PORT = 5174;
|
|
15031
14967
|
const DEFAULT_DEV_CONTEXT_TIMEOUT_MS = 3000;
|
|
15032
14968
|
const DEV_LISTEN_HOST = '0.0.0.0';
|
|
15033
14969
|
const LOCAL_DEV_URL_HOST = '127.0.0.1';
|
|
15034
|
-
//
|
|
14970
|
+
// 与 Heybox H5 Vite 插件共享,让 hb-sdk 统一管理启动输出。
|
|
15035
14971
|
const MANAGED_DEV_OUTPUT_ENV = 'HEYBOX_DEV_SERVER_MANAGED_OUTPUT';
|
|
15036
14972
|
const VITE_LOG_LEVEL = 'warn';
|
|
15037
14973
|
async function runDevCommand(options, runtime = {}) {
|
|
@@ -15046,7 +14982,7 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15046
14982
|
const appPort = await selectAvailablePort({
|
|
15047
14983
|
getPort: getPortImpl,
|
|
15048
14984
|
host: DEV_LISTEN_HOST,
|
|
15049
|
-
label: 'Vite
|
|
14985
|
+
label: 'Vite 开发服务器',
|
|
15050
14986
|
startPort: options.port ?? DEFAULT_APP_PORT,
|
|
15051
14987
|
});
|
|
15052
14988
|
const resolvedOptions = {
|
|
@@ -15062,46 +14998,9 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15062
14998
|
projectRoot,
|
|
15063
14999
|
timeoutMs: runtime.devContextTimeoutMs ?? DEFAULT_DEV_CONTEXT_TIMEOUT_MS,
|
|
15064
15000
|
});
|
|
15065
|
-
const
|
|
15066
|
-
|
|
15067
|
-
|
|
15068
|
-
devContext: devLaunchContext,
|
|
15069
|
-
loadRemoteContext: async () => (await loadDevLaunchContext({
|
|
15070
|
-
env: runtime.env,
|
|
15071
|
-
fetchImpl,
|
|
15072
|
-
logger,
|
|
15073
|
-
projectRoot,
|
|
15074
|
-
timeoutMs: runtime.devContextTimeoutMs ?? DEFAULT_DEV_CONTEXT_TIMEOUT_MS,
|
|
15075
|
-
})).context,
|
|
15076
|
-
projectRoot,
|
|
15077
|
-
});
|
|
15078
|
-
let launchRuntimePermissions = context.createBrowserDevRuntimePermissions(context.createBrowserDevPermissionState(devLaunchContext));
|
|
15079
|
-
const rememberLaunchRuntimePermissions = (configuration) => {
|
|
15080
|
-
const snapshot = configuration.override_permissions ?? configuration.remote_permissions;
|
|
15081
|
-
launchRuntimePermissions = context.parseMiniProgramRuntimePermissions(snapshot).valid
|
|
15082
|
-
? JSON.parse(JSON.stringify(snapshot))
|
|
15083
|
-
: context.createBrowserDevRuntimePermissions({ networkRequest: { status: 'disabled', useOfficialDomain: false } });
|
|
15084
|
-
};
|
|
15085
|
-
rememberLaunchRuntimePermissions(await innerPermissionController.get());
|
|
15086
|
-
const permissionController = {
|
|
15087
|
-
get: async () => {
|
|
15088
|
-
const configuration = await innerPermissionController.get();
|
|
15089
|
-
rememberLaunchRuntimePermissions(configuration);
|
|
15090
|
-
return configuration;
|
|
15091
|
-
},
|
|
15092
|
-
reset: async () => {
|
|
15093
|
-
const configuration = await innerPermissionController.reset();
|
|
15094
|
-
rememberLaunchRuntimePermissions(configuration);
|
|
15095
|
-
return configuration;
|
|
15096
|
-
},
|
|
15097
|
-
save: async (runtimePermissions) => {
|
|
15098
|
-
const configuration = await innerPermissionController.save(runtimePermissions);
|
|
15099
|
-
rememberLaunchRuntimePermissions(configuration);
|
|
15100
|
-
return configuration;
|
|
15101
|
-
},
|
|
15102
|
-
};
|
|
15103
|
-
const vite = await logger.task('正在加载项目 Vite', () => projectVite.loadProjectVite(projectRoot), { successText: '已加载项目 Vite' });
|
|
15104
|
-
const appServer = await logger.task('正在创建 Vite dev server', () => runtimePermissionEnv.withMiniappPlatformCspEnv(context.shouldSkipMiniappPlatformCsp(devLaunchContext.runtimePermissions), () => withManagedDevOutputEnv(() => vite.createServer(createViteServerOptions(projectRoot, resolvedOptions)), runtime.env), runtime.env), { successText: '已创建 Vite dev server' });
|
|
15001
|
+
const launchRuntimePermissions = context.createBrowserDevRuntimePermissions(devLaunchContext.runtimePermissions, context.createBrowserDevPermissionState(devLaunchContext));
|
|
15002
|
+
const vite = await runDevStartupTask(logger, '正在加载项目 Vite', () => projectVite.loadProjectVite(projectRoot), '已加载项目 Vite');
|
|
15003
|
+
const appServer = await runDevStartupTask(logger, '正在创建 Vite 开发服务器', () => runtimePermissionEnv.withMiniappPlatformCspEnv(context.shouldSkipMiniappPlatformCsp(devLaunchContext.runtimePermissions), () => withManagedDevOutputEnv(() => vite.createServer(createViteServerOptions(projectRoot, resolvedOptions)), runtime.env), runtime.env), '已创建 Vite 开发服务器');
|
|
15105
15004
|
const mobileDevContextUrlsByOrigin = new Map();
|
|
15106
15005
|
let currentLocalDevLaunchToken = devLaunchContext.localDevLaunchToken;
|
|
15107
15006
|
appServer.middlewares.use((request, response, next) => {
|
|
@@ -15110,17 +15009,13 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15110
15009
|
localDevLaunchToken: currentLocalDevLaunchToken,
|
|
15111
15010
|
miniProgramId: devLaunchContext.miniProgramId,
|
|
15112
15011
|
runtimePermissions: launchRuntimePermissions,
|
|
15113
|
-
sdkVersion: HB_SDK_VERSION,
|
|
15012
|
+
sdkVersion: version.HB_SDK_VERSION,
|
|
15114
15013
|
}));
|
|
15115
15014
|
if (!handled)
|
|
15116
15015
|
next();
|
|
15117
15016
|
});
|
|
15118
|
-
await logger
|
|
15119
|
-
|
|
15120
|
-
});
|
|
15121
|
-
const appUrl = await logger.task('正在探测小程序入口', () => resolveViteAppUrl(appServer, resolvedOptions, fetchImpl), {
|
|
15122
|
-
successText: '已确定小程序入口',
|
|
15123
|
-
});
|
|
15017
|
+
await runDevStartupTask(logger, '正在启动 Vite 开发服务器', () => appServer.listen(appPort), 'Vite 开发服务器已启动');
|
|
15018
|
+
const appUrl = await runDevStartupTask(logger, '正在探测小程序入口', () => resolveViteAppUrl(appServer, resolvedOptions, fetchImpl), '已确定小程序入口');
|
|
15124
15019
|
const buildLanAddresses = () => createLanAddressCandidates({
|
|
15125
15020
|
appUrl,
|
|
15126
15021
|
devContextUrlsByMiniUrlOrigin: mobileDevContextUrlsByOrigin,
|
|
@@ -15128,14 +15023,14 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15128
15023
|
interfaces: networkInterfaceSnapshot,
|
|
15129
15024
|
localDevLaunchToken: currentLocalDevLaunchToken,
|
|
15130
15025
|
miniProgramId: devLaunchContext.miniProgramId,
|
|
15131
|
-
sdkVersion: HB_SDK_VERSION,
|
|
15026
|
+
sdkVersion: version.HB_SDK_VERSION,
|
|
15132
15027
|
viteNetworkUrls: appServer.resolvedUrls?.network ?? [],
|
|
15133
15028
|
});
|
|
15134
15029
|
const lanAddresses = buildLanAddresses();
|
|
15135
15030
|
const closers = [() => appServer.close()];
|
|
15136
15031
|
let browserDevHost;
|
|
15137
15032
|
try {
|
|
15138
|
-
browserDevHost = await logger
|
|
15033
|
+
browserDevHost = await runDevStartupTask(logger, '正在启动浏览器调试页服务', () => startBrowserDevHostServer({
|
|
15139
15034
|
appUrl,
|
|
15140
15035
|
devContext: devLaunchContext,
|
|
15141
15036
|
fetchImpl,
|
|
@@ -15146,23 +15041,17 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15146
15041
|
loginService: runtime.loginService,
|
|
15147
15042
|
miniProgram,
|
|
15148
15043
|
cliAuth: runtime.cliAuth ?? createBrowserDevHostCliAuth({ env: runtime.env, fetchImpl }),
|
|
15149
|
-
macAppProtocol: createMacAppProtocol(appUrl, {
|
|
15150
|
-
miniProgramId: devLaunchContext.miniProgramId,
|
|
15151
|
-
localDevLaunchToken: devLaunchContext.localDevLaunchToken,
|
|
15152
|
-
sdkVersion: HB_SDK_VERSION,
|
|
15153
|
-
}),
|
|
15154
15044
|
onMobileDevSessionCreated: ({ devContextUrl, miniUrlOrigin }) => {
|
|
15155
15045
|
mobileDevContextUrlsByOrigin.set(miniUrlOrigin, devContextUrl);
|
|
15156
15046
|
browserDevHost.updateBootstrap({
|
|
15157
15047
|
lanAddresses: buildLanAddresses(),
|
|
15158
15048
|
});
|
|
15159
15049
|
},
|
|
15160
|
-
permissionController,
|
|
15161
15050
|
port: options.browserDevHostPort ?? DEFAULT_BROWSER_DEV_HOST_PORT,
|
|
15162
15051
|
root: runtime.browserDevHostRoot,
|
|
15163
15052
|
rootCandidates: runtime.browserDevHostRootCandidates,
|
|
15164
15053
|
runtimePermissions: devLaunchContext.runtimePermissions,
|
|
15165
|
-
}),
|
|
15054
|
+
}), '浏览器调试页服务已启动');
|
|
15166
15055
|
}
|
|
15167
15056
|
catch (error) {
|
|
15168
15057
|
await closeAll(closers);
|
|
@@ -15186,11 +15075,6 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15186
15075
|
browserDevHost.updateBootstrap({
|
|
15187
15076
|
devContext: tokenContext,
|
|
15188
15077
|
lanAddresses: buildLanAddresses(),
|
|
15189
|
-
macAppProtocol: createMacAppProtocol(appUrl, {
|
|
15190
|
-
miniProgramId: devLaunchContext.miniProgramId,
|
|
15191
|
-
localDevLaunchToken,
|
|
15192
|
-
sdkVersion: HB_SDK_VERSION,
|
|
15193
|
-
}),
|
|
15194
15078
|
});
|
|
15195
15079
|
},
|
|
15196
15080
|
projectRoot,
|
|
@@ -15198,23 +15082,29 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15198
15082
|
});
|
|
15199
15083
|
closers.push(tokenSession.close);
|
|
15200
15084
|
}
|
|
15201
|
-
logger.success('
|
|
15202
|
-
logger.info(
|
|
15203
|
-
logger.info(
|
|
15204
|
-
logger.info('
|
|
15085
|
+
logger.success('本地调试已启动');
|
|
15086
|
+
logger.info(`调试页:${browserDevHost.url}`);
|
|
15087
|
+
logger.info(`小程序地址:${appUrl}`);
|
|
15088
|
+
logger.info('手机调试:请在调试页扫码');
|
|
15205
15089
|
if (options.open !== false) {
|
|
15206
15090
|
logger.debug('正在打开浏览器调试页');
|
|
15207
15091
|
void openUrl(browserDevHost.url);
|
|
15208
15092
|
}
|
|
15209
15093
|
installShutdownHandlers(closers, runtime.process ?? process);
|
|
15210
15094
|
}
|
|
15095
|
+
function runDevStartupTask(logger, text, action, successText) {
|
|
15096
|
+
if (!logger.verbose) {
|
|
15097
|
+
return action();
|
|
15098
|
+
}
|
|
15099
|
+
return logger.task(text, () => action(), { successText });
|
|
15100
|
+
}
|
|
15211
15101
|
async function loadDevLaunchContext(options) {
|
|
15212
15102
|
const miniProgramId = await context.readBoundMiniProgramId(options.projectRoot);
|
|
15213
15103
|
if (!miniProgramId) {
|
|
15214
15104
|
throw new Error('hb-sdk dev 需要绑定小程序。请先运行 hb-sdk remote create 或 hb-sdk remote bind <mini-program-id>。');
|
|
15215
15105
|
}
|
|
15216
15106
|
try {
|
|
15217
|
-
const { detail, miniProgramId: verifiedMiniProgramId } = await options.logger
|
|
15107
|
+
const { detail, miniProgramId: verifiedMiniProgramId } = await runDevStartupTask(options.logger, '正在读取远端小程序权限', () => loadRemoteDevContextWithTimeout({
|
|
15218
15108
|
fetchImpl: options.fetchImpl,
|
|
15219
15109
|
load: (fetchImpl) => context.getBoundMiniProgram({
|
|
15220
15110
|
cwd: options.projectRoot,
|
|
@@ -15222,12 +15112,16 @@ async function loadDevLaunchContext(options) {
|
|
|
15222
15112
|
fetchImpl,
|
|
15223
15113
|
}),
|
|
15224
15114
|
timeoutMs: options.timeoutMs,
|
|
15225
|
-
}),
|
|
15115
|
+
}), '已读取远端小程序权限');
|
|
15116
|
+
const localPermissions = runtimePermissionEnv.readMiniappPermissionsFromPackageJson(options.projectRoot, runtimePermissionEnv.resolveMiniappSdkVersion());
|
|
15117
|
+
if (!localPermissions.declared)
|
|
15118
|
+
options.logger.warn(runtimePermissionEnv.getMissingPermissionsWarning());
|
|
15119
|
+
const effectivePermissions = runtimePermissionEnv.composeMiniappRuntimePermissions(localPermissions, detail.runtime_permissions).runtimePermissions;
|
|
15226
15120
|
return {
|
|
15227
15121
|
context: {
|
|
15228
15122
|
source: 'remote',
|
|
15229
15123
|
miniProgramId: verifiedMiniProgramId,
|
|
15230
|
-
runtimePermissions:
|
|
15124
|
+
runtimePermissions: effectivePermissions,
|
|
15231
15125
|
},
|
|
15232
15126
|
miniProgram: createMiniProgramSummary(verifiedMiniProgramId, detail),
|
|
15233
15127
|
};
|
|
@@ -15250,72 +15144,6 @@ function createMiniProgramSummary(miniProgramId, detail) {
|
|
|
15250
15144
|
...(typeof detail.developer_name === 'string' && detail.developer_name.trim() ? { developerName: detail.developer_name.trim() } : {}),
|
|
15251
15145
|
};
|
|
15252
15146
|
}
|
|
15253
|
-
function createDevPermissionController(options) {
|
|
15254
|
-
const store = createDevPermissionOverrideStore({ cacheRoot: options.cacheRoot });
|
|
15255
|
-
let devContext = options.devContext;
|
|
15256
|
-
let ephemeralOverride;
|
|
15257
|
-
let persistenceError;
|
|
15258
|
-
const readScope = () => ({
|
|
15259
|
-
projectRoot: options.projectRoot,
|
|
15260
|
-
...(options.boundMiniProgramId ? { miniProgramId: options.boundMiniProgramId } : {}),
|
|
15261
|
-
});
|
|
15262
|
-
const createConfiguration = (overrides, error = persistenceError) => {
|
|
15263
|
-
const miniProgramId = options.boundMiniProgramId ?? devContext.miniProgramId;
|
|
15264
|
-
return {
|
|
15265
|
-
source: devContext.source,
|
|
15266
|
-
...(miniProgramId ? { mini_program_id: miniProgramId } : {}),
|
|
15267
|
-
...(isRuntimePermissionsSnapshot(devContext.runtimePermissions) ? { remote_permissions: devContext.runtimePermissions } : {}),
|
|
15268
|
-
...(overrides ? { override_permissions: context.createBrowserDevRuntimePermissions(overrides) } : {}),
|
|
15269
|
-
...(error ? { persistence_error: error } : {}),
|
|
15270
|
-
};
|
|
15271
|
-
};
|
|
15272
|
-
const get = async () => {
|
|
15273
|
-
try {
|
|
15274
|
-
const persistedOverride = await store.read(readScope());
|
|
15275
|
-
return createConfiguration(ephemeralOverride ?? persistedOverride);
|
|
15276
|
-
}
|
|
15277
|
-
catch (error) {
|
|
15278
|
-
persistenceError = index.readErrorMessage(error);
|
|
15279
|
-
return createConfiguration(ephemeralOverride, persistenceError);
|
|
15280
|
-
}
|
|
15281
|
-
};
|
|
15282
|
-
return {
|
|
15283
|
-
get,
|
|
15284
|
-
async reset() {
|
|
15285
|
-
let refreshedContext;
|
|
15286
|
-
try {
|
|
15287
|
-
refreshedContext = await options.loadRemoteContext();
|
|
15288
|
-
}
|
|
15289
|
-
catch (error) {
|
|
15290
|
-
throw new Error('远端权限读取失败,请检查 CLI 登录状态和网络后重试。', { cause: error });
|
|
15291
|
-
}
|
|
15292
|
-
await store.remove(readScope());
|
|
15293
|
-
devContext = refreshedContext;
|
|
15294
|
-
ephemeralOverride = undefined;
|
|
15295
|
-
persistenceError = undefined;
|
|
15296
|
-
return createConfiguration(undefined);
|
|
15297
|
-
},
|
|
15298
|
-
async save(runtimePermissions) {
|
|
15299
|
-
const overrides = context.createBrowserDevPermissionState({
|
|
15300
|
-
runtimePermissions,
|
|
15301
|
-
});
|
|
15302
|
-
try {
|
|
15303
|
-
await store.set(readScope(), overrides);
|
|
15304
|
-
ephemeralOverride = undefined;
|
|
15305
|
-
persistenceError = undefined;
|
|
15306
|
-
return createConfiguration(overrides);
|
|
15307
|
-
}
|
|
15308
|
-
catch {
|
|
15309
|
-
ephemeralOverride = overrides;
|
|
15310
|
-
persistenceError = '保存失败,当前页面仍会应用该权限,但刷新或重启后会丢失。';
|
|
15311
|
-
return createConfiguration(overrides, persistenceError);
|
|
15312
|
-
}
|
|
15313
|
-
},
|
|
15314
|
-
};
|
|
15315
|
-
}
|
|
15316
|
-
function isRuntimePermissionsSnapshot(value) {
|
|
15317
|
-
return context.parseMiniProgramRuntimePermissions(value).valid;
|
|
15318
|
-
}
|
|
15319
15147
|
async function loadRemoteDevContextWithTimeout(options) {
|
|
15320
15148
|
const controller = new AbortController();
|
|
15321
15149
|
const fetchImpl = ((input, init) => options.fetchImpl(input, { ...init, signal: controller.signal }));
|
|
@@ -15488,25 +15316,30 @@ async function closeAll(closers) {
|
|
|
15488
15316
|
function startLocalDevLaunchTokenSession(options) {
|
|
15489
15317
|
let closed = false;
|
|
15490
15318
|
let expirationTimer;
|
|
15319
|
+
let hasReportedTokenFailure = false;
|
|
15491
15320
|
let refreshTimer;
|
|
15492
15321
|
const refresh = async () => {
|
|
15493
15322
|
try {
|
|
15494
15323
|
const issued = await issueLocalDevLaunchToken(options);
|
|
15495
15324
|
if (closed)
|
|
15496
15325
|
return;
|
|
15497
|
-
if (!issued) {
|
|
15498
|
-
schedule(options.timeoutMs);
|
|
15499
|
-
return;
|
|
15500
|
-
}
|
|
15501
15326
|
options.onToken(issued.token);
|
|
15502
15327
|
const ttl = Math.max(0, issued.expiresAt - Date.now());
|
|
15503
15328
|
scheduleExpiration(ttl);
|
|
15504
15329
|
schedule(Math.max(1_000, Math.floor(ttl * 0.8)));
|
|
15330
|
+
if (hasReportedTokenFailure) {
|
|
15331
|
+
hasReportedTokenFailure = false;
|
|
15332
|
+
options.logger.success('手机扫码调试已恢复');
|
|
15333
|
+
}
|
|
15505
15334
|
}
|
|
15506
15335
|
catch (error) {
|
|
15507
15336
|
if (closed)
|
|
15508
15337
|
return;
|
|
15509
|
-
|
|
15338
|
+
if (!hasReportedTokenFailure) {
|
|
15339
|
+
options.logger.warn('手机扫码调试凭证获取失败,将自动重试');
|
|
15340
|
+
}
|
|
15341
|
+
hasReportedTokenFailure = true;
|
|
15342
|
+
options.logger.debug(`手机扫码调试凭证获取失败,详细原因:${index.readRedactedErrorMessage(error, { verbose: true })}`);
|
|
15510
15343
|
schedule(options.timeoutMs);
|
|
15511
15344
|
}
|
|
15512
15345
|
};
|
|
@@ -15537,31 +15370,25 @@ function startLocalDevLaunchTokenSession(options) {
|
|
|
15537
15370
|
};
|
|
15538
15371
|
}
|
|
15539
15372
|
async function issueLocalDevLaunchToken(options) {
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
|
|
15552
|
-
|
|
15553
|
-
throw new Error('响应缺少 token');
|
|
15554
|
-
}
|
|
15555
|
-
const expiresAt = normalizeExpiresAt(issued.expires_at);
|
|
15556
|
-
if (!expiresAt || expiresAt <= Date.now()) {
|
|
15557
|
-
throw new Error('响应缺少有效 expires_at');
|
|
15558
|
-
}
|
|
15559
|
-
return { expiresAt, token };
|
|
15373
|
+
const issued = await loadRemoteDevContextWithTimeout({
|
|
15374
|
+
fetchImpl: options.fetchImpl,
|
|
15375
|
+
timeoutMs: options.timeoutMs,
|
|
15376
|
+
load: (fetchImpl) => context.createLocalDevLaunchToken({
|
|
15377
|
+
cwd: options.projectRoot,
|
|
15378
|
+
env: options.env,
|
|
15379
|
+
fetchImpl,
|
|
15380
|
+
miniProgramId: options.miniProgramId,
|
|
15381
|
+
}),
|
|
15382
|
+
});
|
|
15383
|
+
const token = typeof issued.token === 'string' ? issued.token.trim() : '';
|
|
15384
|
+
if (!token) {
|
|
15385
|
+
throw new Error('响应缺少 token');
|
|
15560
15386
|
}
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15387
|
+
const expiresAt = normalizeExpiresAt(issued.expires_at);
|
|
15388
|
+
if (!expiresAt || expiresAt <= Date.now()) {
|
|
15389
|
+
throw new Error('响应缺少有效 expires_at');
|
|
15564
15390
|
}
|
|
15391
|
+
return { expiresAt, token };
|
|
15565
15392
|
}
|
|
15566
15393
|
function normalizeExpiresAt(value) {
|
|
15567
15394
|
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|