@dimina-kit/devtools 0.4.0-dev.20260702182435 → 0.4.0-dev.20260703101348
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/dist/main/app/app.js +5 -25
- package/dist/main/app/native-overview.d.ts +27 -0
- package/dist/main/app/native-overview.js +44 -0
- package/dist/main/index.bundle.js +1217 -735
- package/dist/main/ipc/bridge-router.d.ts +8 -0
- package/dist/main/ipc/bridge-router.js +526 -173
- package/dist/main/ipc/project-fs.js +39 -21
- package/dist/main/services/automation/handlers/app.js +143 -109
- package/dist/main/services/console-forward/index.d.ts +12 -1
- package/dist/main/services/console-forward/index.js +99 -1
- package/dist/main/services/diagnostics/index.d.ts +49 -0
- package/dist/main/services/diagnostics/index.js +61 -0
- package/dist/main/services/mcp/tools/simulator-tools.js +118 -97
- package/dist/main/services/network-forward/dispatch-batch.d.ts +22 -0
- package/dist/main/services/network-forward/dispatch-batch.js +21 -0
- package/dist/main/services/network-forward/index.js +37 -20
- package/dist/main/services/notifications/renderer-notifier.d.ts +39 -0
- package/dist/main/services/notifications/renderer-notifier.js +4 -1
- package/dist/main/services/projects/create-project-service.js +54 -34
- package/dist/main/services/projects/project-repository.js +2 -1
- package/dist/main/services/projects/types.d.ts +1 -32
- package/dist/main/services/service-console/console-api.js +36 -27
- package/dist/main/services/simulator-temp-files/disk.js +109 -86
- package/dist/main/services/views/simulator-session-policy.js +17 -0
- package/dist/main/services/workbench-coi-server.js +58 -45
- package/dist/main/services/workbench-context.d.ts +10 -0
- package/dist/main/services/workspace/workspace-service.js +12 -3
- package/dist/main/utils/logger.d.ts +5 -12
- package/dist/main/utils/logger.js +5 -45
- package/dist/main/windows/service-host-window/create.d.ts +15 -1
- package/dist/main/windows/service-host-window/create.js +11 -3
- package/dist/preload/instrumentation/wxml-extract.js +94 -62
- package/dist/preload/runtime/main-api-runner.d.ts +1 -1
- package/dist/preload/shared/api-compat.js +18 -55
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +284 -565
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs +154 -83
- package/dist/preload/windows/simulator.cjs.map +3 -3
- package/dist/preload/windows/simulator.js +154 -83
- package/dist/render-host/render-inspect.js +63 -52
- package/dist/renderer/assets/index-BwzPwCmM.js +49 -0
- package/dist/renderer/assets/{input-Cjk0wSEh.js → input-CTb_le7F.js} +2 -2
- package/dist/renderer/assets/ipc-transport-BPWIV5hA.css +1 -0
- package/dist/renderer/assets/{ipc-transport-OZS-KmOW.js → ipc-transport-Ck5Xa8Tu.js} +2 -2
- package/dist/renderer/assets/popover-B1Zu2UQu.js +2 -0
- package/dist/renderer/assets/{select-BQDXi5ih.js → select-ozSk5Pt6.js} +2 -2
- package/dist/renderer/assets/{settings-tP3Px2KR.js → settings-CNwGOI22.js} +2 -2
- package/dist/renderer/assets/settings-api-DMbMp1Eq.js +2 -0
- package/dist/renderer/assets/workbenchSettings-Cr4EHo5w.js +8 -0
- package/dist/renderer/entries/main/index.html +6 -6
- package/dist/renderer/entries/popover/index.html +5 -5
- package/dist/renderer/entries/settings/index.html +5 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/shared/appdata-accumulator.js +53 -48
- package/dist/shared/bridge-channels.d.ts +22 -0
- package/dist/shared/ipc-channels.d.ts +3 -0
- package/dist/shared/ipc-channels.js +9 -0
- package/dist/shared/open-in-editor-resource-path.d.ts +56 -0
- package/dist/shared/open-in-editor-resource-path.js +133 -0
- package/dist/shared/open-in-editor.d.ts +2 -9
- package/dist/shared/open-in-editor.js +8 -93
- package/dist/shared/request-core.d.ts +84 -0
- package/dist/shared/request-core.js +168 -0
- package/dist/shared/simulator-api-metadata.d.ts +22 -17
- package/dist/shared/simulator-api-metadata.js +36 -0
- package/dist/shared/types.d.ts +20 -4
- package/dist/simulator/assets/{device-shell-CUl0ILfn.js → device-shell-C-wcFq3Z.js} +2 -2
- package/dist/simulator/assets/{jsx-runtime-CDK-o-S0.js → jsx-runtime-BDTY6fEq.js} +2 -2
- package/dist/simulator/assets/simulator-DU3-fS3v.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-DzfMfnVM.js +2 -0
- package/dist/simulator/simulator.html +2 -2
- package/dist/vscode-workbench/assets/__vite-browser-external-CXi6Kz9W.js +1 -0
- package/dist/vscode-workbench/assets/{dist-CS7SQP3K.js → dist-TpGpmMGi.js} +3 -3
- package/dist/vscode-workbench/assets/{iconv-lite-umd-D3q-1-o6.js → iconv-lite-umd-C9tiCAJa.js} +1 -1
- package/dist/vscode-workbench/assets/{index-zjigpZ25.js → index-DJ1HyMZ7.js} +20 -22
- package/dist/vscode-workbench/assets/{jschardet-Cu4ufeHq.js → jschardet-DE1jV513.js} +1 -1
- package/dist/vscode-workbench/index.html +1 -1
- package/package.json +6 -6
- package/dist/renderer/assets/index-B-Dqb7G0.js +0 -49
- package/dist/renderer/assets/ipc-transport-D5dKIti1.css +0 -1
- package/dist/renderer/assets/popover-CQVvOe90.js +0 -2
- package/dist/renderer/assets/settings-api-B6x2mhaD.js +0 -2
- package/dist/renderer/assets/workbenchSettings-Bim9ol9R.js +0 -8
- package/dist/simulator/assets/simulator-Dz8iGcgy.js +0 -10
- package/dist/simulator/assets/simulator-mini-app-Bt639XL_.js +0 -2
- package/dist/vscode-workbench/assets/__vite-browser-external-B0DTNerG.js +0 -1
|
@@ -173,55 +173,20 @@ function validate(channel, schema, args) {
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// src/main/utils/logger.ts
|
|
176
|
-
|
|
177
|
-
debug: 0,
|
|
178
|
-
info: 1,
|
|
179
|
-
warn: 2,
|
|
180
|
-
error: 3
|
|
181
|
-
};
|
|
182
|
-
var LEVEL_PREFIX = {
|
|
183
|
-
debug: "[DEBUG]",
|
|
184
|
-
info: "[INFO]",
|
|
185
|
-
warn: "[WARN]",
|
|
186
|
-
error: "[ERROR]"
|
|
187
|
-
};
|
|
188
|
-
var currentLevel = "debug";
|
|
189
|
-
function shouldLog(level) {
|
|
190
|
-
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[currentLevel];
|
|
191
|
-
}
|
|
192
|
-
function formatMessage(level, tag, message) {
|
|
193
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
194
|
-
return `${ts} ${LEVEL_PREFIX[level]} [${tag}] ${message}`;
|
|
195
|
-
}
|
|
196
|
-
function createLogger(tag) {
|
|
197
|
-
return {
|
|
198
|
-
debug(message, ...args) {
|
|
199
|
-
if (shouldLog("debug")) console.debug(formatMessage("debug", tag, message), ...args);
|
|
200
|
-
},
|
|
201
|
-
info(message, ...args) {
|
|
202
|
-
if (shouldLog("info")) console.info(formatMessage("info", tag, message), ...args);
|
|
203
|
-
},
|
|
204
|
-
warn(message, ...args) {
|
|
205
|
-
if (shouldLog("warn")) console.warn(formatMessage("warn", tag, message), ...args);
|
|
206
|
-
},
|
|
207
|
-
error(message, ...args) {
|
|
208
|
-
if (shouldLog("error")) console.error(formatMessage("error", tag, message), ...args);
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
}
|
|
176
|
+
import { createLogger, setLogLevel } from "@dimina-kit/electron-deck/main";
|
|
212
177
|
|
|
213
178
|
// src/main/utils/ipc-registry.ts
|
|
214
179
|
var log = createLogger("ipc");
|
|
215
|
-
function reportListenerError(channel,
|
|
216
|
-
if (
|
|
217
|
-
log.warn(`schema reject on '${channel}' at [${
|
|
180
|
+
function reportListenerError(channel, err2) {
|
|
181
|
+
if (err2 instanceof IpcValidationError) {
|
|
182
|
+
log.warn(`schema reject on '${channel}' at [${err2.paths.join(", ")}]`);
|
|
218
183
|
return;
|
|
219
184
|
}
|
|
220
|
-
if (
|
|
221
|
-
log.error(`listener threw on '${channel}': ${
|
|
185
|
+
if (err2 instanceof Error) {
|
|
186
|
+
log.error(`listener threw on '${channel}': ${err2.message}`, err2.stack);
|
|
222
187
|
return;
|
|
223
188
|
}
|
|
224
|
-
log.error(`listener threw on '${channel}'`,
|
|
189
|
+
log.error(`listener threw on '${channel}'`, err2);
|
|
225
190
|
}
|
|
226
191
|
function isThenable(v) {
|
|
227
192
|
return v != null && (typeof v === "object" || typeof v === "function") && typeof v.then === "function";
|
|
@@ -284,13 +249,13 @@ var IpcRegistry = class {
|
|
|
284
249
|
return;
|
|
285
250
|
}
|
|
286
251
|
event.returnValue = fn(event, ...args);
|
|
287
|
-
} catch (
|
|
288
|
-
reportListenerError(channel,
|
|
289
|
-
const code =
|
|
252
|
+
} catch (err2) {
|
|
253
|
+
reportListenerError(channel, err2);
|
|
254
|
+
const code = err2?.code;
|
|
290
255
|
event.returnValue = {
|
|
291
256
|
ok: false,
|
|
292
257
|
code: typeof code === "string" ? code : "EUNKNOWN",
|
|
293
|
-
message:
|
|
258
|
+
message: err2 instanceof Error ? err2.message : String(err2)
|
|
294
259
|
};
|
|
295
260
|
}
|
|
296
261
|
};
|
|
@@ -307,10 +272,10 @@ var IpcRegistry = class {
|
|
|
307
272
|
try {
|
|
308
273
|
const ret = raw(event, ...args);
|
|
309
274
|
if (isThenable(ret)) {
|
|
310
|
-
Promise.resolve(ret).catch((
|
|
275
|
+
Promise.resolve(ret).catch((err2) => reportListenerError(channel, err2));
|
|
311
276
|
}
|
|
312
|
-
} catch (
|
|
313
|
-
reportListenerError(channel,
|
|
277
|
+
} catch (err2) {
|
|
278
|
+
reportListenerError(channel, err2);
|
|
314
279
|
}
|
|
315
280
|
};
|
|
316
281
|
const guarded = policy ? (event, ...args) => {
|
|
@@ -428,6 +393,9 @@ var ProjectChannel = {
|
|
|
428
393
|
CaptureThumbnail: "project:captureThumbnail",
|
|
429
394
|
GetThumbnail: "project:getThumbnail"
|
|
430
395
|
};
|
|
396
|
+
var SessionChannel = {
|
|
397
|
+
RuntimeStatus: "session:runtimeStatus"
|
|
398
|
+
};
|
|
431
399
|
var ProjectFsChannel = {
|
|
432
400
|
GetRoot: "project:fs:getRoot",
|
|
433
401
|
ReadFile: "project:fs:readFile",
|
|
@@ -555,9 +523,9 @@ var ReadFileSchema = z.tuple([PathArg]);
|
|
|
555
523
|
var WriteFileSchema = z.tuple([PathArg, z.string()]);
|
|
556
524
|
var ListFilesSchema = z.tuple([PathArg]);
|
|
557
525
|
function makeError(code, message) {
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
return
|
|
526
|
+
const err2 = new Error(message);
|
|
527
|
+
err2.code = code;
|
|
528
|
+
return err2;
|
|
561
529
|
}
|
|
562
530
|
function isWithin(parent, child) {
|
|
563
531
|
if (child === parent) return true;
|
|
@@ -600,8 +568,8 @@ async function resolveWithinProjectRoot(abs, root) {
|
|
|
600
568
|
let realTarget;
|
|
601
569
|
try {
|
|
602
570
|
realTarget = await fs2.realpath(lexical);
|
|
603
|
-
} catch (
|
|
604
|
-
if (
|
|
571
|
+
} catch (err2) {
|
|
572
|
+
if (err2.code !== "ENOENT") throw err2;
|
|
605
573
|
const parent = path2.dirname(lexical);
|
|
606
574
|
const realParent = await fs2.realpath(parent);
|
|
607
575
|
if (!isWithin(realRoot, realParent)) {
|
|
@@ -648,8 +616,8 @@ async function assertWritableAncestor(lexical, root) {
|
|
|
648
616
|
let realAncestor;
|
|
649
617
|
try {
|
|
650
618
|
realAncestor = await fs2.realpath(ancestor);
|
|
651
|
-
} catch (
|
|
652
|
-
if (
|
|
619
|
+
} catch (err2) {
|
|
620
|
+
if (err2.code !== "ENOENT") throw err2;
|
|
653
621
|
const next = path2.dirname(ancestor);
|
|
654
622
|
if (next === ancestor) {
|
|
655
623
|
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
@@ -714,9 +682,9 @@ async function deleteWithin(root, rel) {
|
|
|
714
682
|
let safe;
|
|
715
683
|
try {
|
|
716
684
|
safe = await resolveWithinProjectRoot(joinRel(root, rel), root);
|
|
717
|
-
} catch (
|
|
718
|
-
if (
|
|
719
|
-
throw
|
|
685
|
+
} catch (err2) {
|
|
686
|
+
if (err2.code === "ENOENT") return;
|
|
687
|
+
throw err2;
|
|
720
688
|
}
|
|
721
689
|
await fs2.rm(safe, { recursive: true, force: true });
|
|
722
690
|
}
|
|
@@ -733,8 +701,8 @@ function resolveWithinProjectRootSync(abs, root) {
|
|
|
733
701
|
let realTarget;
|
|
734
702
|
try {
|
|
735
703
|
realTarget = nodeFs.realpathSync(lexical);
|
|
736
|
-
} catch (
|
|
737
|
-
if (
|
|
704
|
+
} catch (err2) {
|
|
705
|
+
if (err2.code !== "ENOENT") throw err2;
|
|
738
706
|
const parent = path2.dirname(lexical);
|
|
739
707
|
const realParent = nodeFs.realpathSync(parent);
|
|
740
708
|
if (!isWithin(realRoot, realParent)) {
|
|
@@ -754,8 +722,8 @@ function assertWritableAncestorSync(lexical, root) {
|
|
|
754
722
|
let realAncestor;
|
|
755
723
|
try {
|
|
756
724
|
realAncestor = nodeFs.realpathSync(ancestor);
|
|
757
|
-
} catch (
|
|
758
|
-
if (
|
|
725
|
+
} catch (err2) {
|
|
726
|
+
if (err2.code !== "ENOENT") throw err2;
|
|
759
727
|
const next = path2.dirname(ancestor);
|
|
760
728
|
if (next === ancestor) {
|
|
761
729
|
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
@@ -800,6 +768,25 @@ function writeFileSync(root, p, content) {
|
|
|
800
768
|
nodeFs.closeSync(fd);
|
|
801
769
|
}
|
|
802
770
|
}
|
|
771
|
+
async function readDirEntriesSafe(dir) {
|
|
772
|
+
try {
|
|
773
|
+
return await fs2.readdir(dir, { withFileTypes: true });
|
|
774
|
+
} catch (err2) {
|
|
775
|
+
const code = err2.code;
|
|
776
|
+
if (code === "EACCES" || code === "ENOENT" || code === "ENOTDIR") return null;
|
|
777
|
+
throw err2;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
function collectListEntry(entry, dir, safeRoot, out, queue) {
|
|
781
|
+
const full = path2.join(dir, entry.name);
|
|
782
|
+
if (entry.isDirectory()) {
|
|
783
|
+
if (!SKIP_DIRS.has(entry.name)) queue.push(full);
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (entry.isFile()) {
|
|
787
|
+
out.push(path2.relative(safeRoot, full).split(path2.sep).join("/"));
|
|
788
|
+
}
|
|
789
|
+
}
|
|
803
790
|
async function listFiles(root, rootArg) {
|
|
804
791
|
const safeRoot = await resolveWithinProjectRoot(rootArg, root);
|
|
805
792
|
const out = [];
|
|
@@ -807,24 +794,11 @@ async function listFiles(root, rootArg) {
|
|
|
807
794
|
while (queue.length > 0) {
|
|
808
795
|
if (out.length >= LIST_FILE_LIMIT) break;
|
|
809
796
|
const dir = queue.shift();
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
} catch (err) {
|
|
814
|
-
const code = err.code;
|
|
815
|
-
if (code === "EACCES" || code === "ENOENT" || code === "ENOTDIR") continue;
|
|
816
|
-
throw err;
|
|
817
|
-
}
|
|
818
|
-
for (const e of entries) {
|
|
797
|
+
const entries = await readDirEntriesSafe(dir);
|
|
798
|
+
if (entries === null) continue;
|
|
799
|
+
for (const entry of entries) {
|
|
819
800
|
if (out.length >= LIST_FILE_LIMIT) break;
|
|
820
|
-
|
|
821
|
-
if (e.isDirectory()) {
|
|
822
|
-
if (SKIP_DIRS.has(e.name)) continue;
|
|
823
|
-
queue.push(full);
|
|
824
|
-
continue;
|
|
825
|
-
}
|
|
826
|
-
if (!e.isFile()) continue;
|
|
827
|
-
out.push(path2.relative(safeRoot, full).split(path2.sep).join("/"));
|
|
801
|
+
collectListEntry(entry, dir, safeRoot, out, queue);
|
|
828
802
|
}
|
|
829
803
|
}
|
|
830
804
|
return out;
|
|
@@ -1050,6 +1024,37 @@ function registerAppLifecycle() {
|
|
|
1050
1024
|
});
|
|
1051
1025
|
}
|
|
1052
1026
|
|
|
1027
|
+
// src/main/app/native-overview.ts
|
|
1028
|
+
async function resolveNativeStorageOverview(context, appId) {
|
|
1029
|
+
try {
|
|
1030
|
+
const info = await context.storageApi?.invoke(appId, "getStorageInfo", {});
|
|
1031
|
+
if (info && typeof info === "object") {
|
|
1032
|
+
const keys = info.keys;
|
|
1033
|
+
if (Array.isArray(keys)) {
|
|
1034
|
+
return {
|
|
1035
|
+
storageKeys: keys.filter((key) => typeof key === "string"),
|
|
1036
|
+
storageCount: keys.length
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
} catch {
|
|
1041
|
+
}
|
|
1042
|
+
return { storageKeys: [], storageCount: 0 };
|
|
1043
|
+
}
|
|
1044
|
+
function resolveNativeAppDataKeys(context, appId) {
|
|
1045
|
+
try {
|
|
1046
|
+
const snapshot = context.appData?.snapshot?.(appId);
|
|
1047
|
+
if (snapshot && typeof snapshot === "object") {
|
|
1048
|
+
const entries = snapshot.entries;
|
|
1049
|
+
if (entries && typeof entries === "object") {
|
|
1050
|
+
return Object.keys(entries);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
return [];
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1053
1058
|
// src/main/services/workbench-context.ts
|
|
1054
1059
|
import {
|
|
1055
1060
|
createConnectionRegistry,
|
|
@@ -1103,6 +1108,9 @@ function createRendererNotifier(ctx) {
|
|
|
1103
1108
|
projectStatus(payload) {
|
|
1104
1109
|
sendToMain(ProjectChannel.Status, payload);
|
|
1105
1110
|
},
|
|
1111
|
+
sessionRuntimeStatus(payload) {
|
|
1112
|
+
sendToMain(SessionChannel.RuntimeStatus, payload);
|
|
1113
|
+
},
|
|
1106
1114
|
compileLog(payload) {
|
|
1107
1115
|
sendToMain(ProjectChannel.CompileLog, payload);
|
|
1108
1116
|
},
|
|
@@ -1150,8 +1158,8 @@ function createSafeAreaController(options = {}) {
|
|
|
1150
1158
|
const attached = /* @__PURE__ */ new Map();
|
|
1151
1159
|
function override(wc, device, isTabPage2) {
|
|
1152
1160
|
if (wc.isDestroyed()) return;
|
|
1153
|
-
void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device, isTabPage2) }).catch((
|
|
1154
|
-
console.warn("[safe-area] setSafeAreaInsetsOverride failed:",
|
|
1161
|
+
void wc.debugger.sendCommand("Emulation.setSafeAreaInsetsOverride", { insets: guestInsets(device, isTabPage2) }).catch((err2) => {
|
|
1162
|
+
console.warn("[safe-area] setSafeAreaInsetsOverride failed:", err2 instanceof Error ? err2.message : err2);
|
|
1155
1163
|
});
|
|
1156
1164
|
}
|
|
1157
1165
|
return {
|
|
@@ -1162,8 +1170,8 @@ function createSafeAreaController(options = {}) {
|
|
|
1162
1170
|
}
|
|
1163
1171
|
try {
|
|
1164
1172
|
wc.debugger.attach("1.3");
|
|
1165
|
-
} catch (
|
|
1166
|
-
console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:",
|
|
1173
|
+
} catch (err2) {
|
|
1174
|
+
console.warn("[safe-area] debugger.attach failed; env(safe-area-inset-*) stays 0:", err2 instanceof Error ? err2.message : err2);
|
|
1167
1175
|
return;
|
|
1168
1176
|
}
|
|
1169
1177
|
attached.set(wc, isTabPage2);
|
|
@@ -1403,8 +1411,8 @@ function createWorkbenchView(ctx, reconciler) {
|
|
|
1403
1411
|
}
|
|
1404
1412
|
ctx.windows.mainWindow.contentView.addChildView(view);
|
|
1405
1413
|
const loadUrl = `${url}index.html?theme=${workbenchThemeScheme()}`;
|
|
1406
|
-
await view.webContents.loadURL(loadUrl).catch((
|
|
1407
|
-
console.error("[workbench] attachWorkbench \u2014 loadURL failed",
|
|
1414
|
+
await view.webContents.loadURL(loadUrl).catch((err2) => {
|
|
1415
|
+
console.error("[workbench] attachWorkbench \u2014 loadURL failed", err2);
|
|
1408
1416
|
});
|
|
1409
1417
|
}
|
|
1410
1418
|
function setWorkbenchSource(url) {
|
|
@@ -1439,7 +1447,7 @@ function createWorkbenchView(ctx, reconciler) {
|
|
|
1439
1447
|
return true
|
|
1440
1448
|
} catch (e) { return false }
|
|
1441
1449
|
})()`;
|
|
1442
|
-
return workbenchView.webContents.executeJavaScript(script, true).then((
|
|
1450
|
+
return workbenchView.webContents.executeJavaScript(script, true).then((ok2) => ok2 === true).catch(() => false);
|
|
1443
1451
|
}
|
|
1444
1452
|
function openFileInWorkbench(relPath, line, column) {
|
|
1445
1453
|
if (!workbenchView || workbenchView.webContents.isDestroyed()) return false;
|
|
@@ -1476,6 +1484,84 @@ function createWorkbenchView(ctx, reconciler) {
|
|
|
1476
1484
|
// src/main/services/views/native-simulator-devtools-host.ts
|
|
1477
1485
|
import { WebContentsView as WebContentsView3 } from "electron";
|
|
1478
1486
|
|
|
1487
|
+
// src/shared/open-in-editor-resource-path.ts
|
|
1488
|
+
function decodePathname(pathname) {
|
|
1489
|
+
return pathname.split("/").map((segment) => {
|
|
1490
|
+
try {
|
|
1491
|
+
return decodeURIComponent(segment);
|
|
1492
|
+
} catch {
|
|
1493
|
+
return segment;
|
|
1494
|
+
}
|
|
1495
|
+
}).join("/");
|
|
1496
|
+
}
|
|
1497
|
+
function normalizeSlashPath(value) {
|
|
1498
|
+
return decodePathname(value.replace(/\\/g, "/")).replace(/\/+/g, "/");
|
|
1499
|
+
}
|
|
1500
|
+
function safeRelativePath(segments) {
|
|
1501
|
+
if (segments.length === 0 || segments.some((segment) => !segment || segment === "." || segment === "..")) {
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
return segments.join("/");
|
|
1505
|
+
}
|
|
1506
|
+
function excludeBuildChunk(rel) {
|
|
1507
|
+
if (!rel) return rel;
|
|
1508
|
+
const baseName = rel.split("/").pop();
|
|
1509
|
+
return baseName === "common.js" || baseName === "vendors.js" || baseName === "runtime.js" || baseName === "taro.js" || baseName === "babelHelpers.js" ? null : rel;
|
|
1510
|
+
}
|
|
1511
|
+
function isOutsideKnownRoot(normalizedRaw) {
|
|
1512
|
+
return /^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw);
|
|
1513
|
+
}
|
|
1514
|
+
function resolveFilesystemRawPath(normalizedRaw, projectRoot) {
|
|
1515
|
+
if (!projectRoot) return void 0;
|
|
1516
|
+
if (normalizedRaw !== projectRoot && !normalizedRaw.startsWith(`${projectRoot}/`)) return void 0;
|
|
1517
|
+
const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, "");
|
|
1518
|
+
return excludeBuildChunk(safeRelativePath(relative.split("/").filter(Boolean)));
|
|
1519
|
+
}
|
|
1520
|
+
function parseResourceUrl(raw, base) {
|
|
1521
|
+
try {
|
|
1522
|
+
return raw.startsWith("/") && !raw.startsWith("//") ? new URL(raw, base.origin) : new URL(raw);
|
|
1523
|
+
} catch {
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
function resolveFileProtocolPath(resource, projectRoot) {
|
|
1528
|
+
const filePath = normalizeSlashPath(resource.pathname);
|
|
1529
|
+
if (!projectRoot || filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)) return null;
|
|
1530
|
+
return excludeBuildChunk(safeRelativePath(
|
|
1531
|
+
filePath.slice(projectRoot.length).replace(/^\/+/, "").split("/").filter(Boolean)
|
|
1532
|
+
));
|
|
1533
|
+
}
|
|
1534
|
+
function resolveHttpProtocolPath(resource, base, context) {
|
|
1535
|
+
if (resource.protocol !== "http:" && resource.protocol !== "https:") return null;
|
|
1536
|
+
if (resource.origin !== base.origin) return null;
|
|
1537
|
+
let segments = decodePathname(resource.pathname).split("/").filter(Boolean);
|
|
1538
|
+
if (context.appId && segments[0] === context.appId) {
|
|
1539
|
+
segments = segments.slice(1);
|
|
1540
|
+
if (context.outputRoot && segments[0] === context.outputRoot) segments = segments.slice(1);
|
|
1541
|
+
}
|
|
1542
|
+
return excludeBuildChunk(safeRelativePath(segments));
|
|
1543
|
+
}
|
|
1544
|
+
function projectAwareResourcePath(resourceUrl, context) {
|
|
1545
|
+
let base;
|
|
1546
|
+
try {
|
|
1547
|
+
base = new URL(context.resourceBaseUrl);
|
|
1548
|
+
} catch {
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
const projectRoot = normalizeSlashPath(context.projectRoot).replace(/\/$/, "");
|
|
1552
|
+
const raw = resourceUrl.trim();
|
|
1553
|
+
if (!raw) return null;
|
|
1554
|
+
const normalizedRaw = normalizeSlashPath(raw);
|
|
1555
|
+
const filesystemRelative = resolveFilesystemRawPath(normalizedRaw, projectRoot);
|
|
1556
|
+
if (filesystemRelative !== void 0) return filesystemRelative;
|
|
1557
|
+
if (isOutsideKnownRoot(normalizedRaw)) return null;
|
|
1558
|
+
if (!raw.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null;
|
|
1559
|
+
const resource = parseResourceUrl(raw, base);
|
|
1560
|
+
if (!resource) return null;
|
|
1561
|
+
if (resource.protocol === "file:") return resolveFileProtocolPath(resource, projectRoot);
|
|
1562
|
+
return resolveHttpProtocolPath(resource, base, context);
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1479
1565
|
// src/shared/open-in-editor.ts
|
|
1480
1566
|
var OPEN_IN_EDITOR_SCHEME = "dimina-open-in-editor";
|
|
1481
1567
|
function decodeOpenInEditorUrl(raw) {
|
|
@@ -1531,70 +1617,6 @@ function resourceUrlToProjectRelativePath(resourceUrl, expectedOriginOrContext)
|
|
|
1531
1617
|
if (segments.length < 2) return null;
|
|
1532
1618
|
return segments.slice(1).join("/");
|
|
1533
1619
|
}
|
|
1534
|
-
function decodePathname(pathname) {
|
|
1535
|
-
return pathname.split("/").map((segment) => {
|
|
1536
|
-
try {
|
|
1537
|
-
return decodeURIComponent(segment);
|
|
1538
|
-
} catch {
|
|
1539
|
-
return segment;
|
|
1540
|
-
}
|
|
1541
|
-
}).join("/");
|
|
1542
|
-
}
|
|
1543
|
-
function normalizeSlashPath(value) {
|
|
1544
|
-
return decodePathname(value.replace(/\\/g, "/")).replace(/\/+/g, "/");
|
|
1545
|
-
}
|
|
1546
|
-
function safeRelativePath(segments) {
|
|
1547
|
-
if (segments.length === 0 || segments.some((segment) => !segment || segment === "." || segment === "..")) {
|
|
1548
|
-
return null;
|
|
1549
|
-
}
|
|
1550
|
-
return segments.join("/");
|
|
1551
|
-
}
|
|
1552
|
-
function projectAwareResourcePath(resourceUrl, context) {
|
|
1553
|
-
let base;
|
|
1554
|
-
try {
|
|
1555
|
-
base = new URL(context.resourceBaseUrl);
|
|
1556
|
-
} catch {
|
|
1557
|
-
return null;
|
|
1558
|
-
}
|
|
1559
|
-
const projectRoot = normalizeSlashPath(context.projectRoot).replace(/\/$/, "");
|
|
1560
|
-
const raw = resourceUrl.trim();
|
|
1561
|
-
if (!raw) return null;
|
|
1562
|
-
const excludeBuildChunk = (rel) => {
|
|
1563
|
-
if (!rel) return rel;
|
|
1564
|
-
const base2 = rel.split("/").pop();
|
|
1565
|
-
return base2 === "common.js" || base2 === "vendors.js" || base2 === "runtime.js" || base2 === "taro.js" || base2 === "babelHelpers.js" ? null : rel;
|
|
1566
|
-
};
|
|
1567
|
-
const normalizedRaw = normalizeSlashPath(raw);
|
|
1568
|
-
if (projectRoot && (normalizedRaw === projectRoot || normalizedRaw.startsWith(`${projectRoot}/`))) {
|
|
1569
|
-
const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, "");
|
|
1570
|
-
return excludeBuildChunk(safeRelativePath(relative.split("/").filter(Boolean)));
|
|
1571
|
-
}
|
|
1572
|
-
if (/^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw)) {
|
|
1573
|
-
return null;
|
|
1574
|
-
}
|
|
1575
|
-
if (!raw.startsWith("/") && !/^[a-z][a-z0-9+.-]*:/i.test(raw)) return null;
|
|
1576
|
-
let resource;
|
|
1577
|
-
try {
|
|
1578
|
-
resource = raw.startsWith("/") && !raw.startsWith("//") ? new URL(raw, base.origin) : new URL(raw);
|
|
1579
|
-
} catch {
|
|
1580
|
-
return null;
|
|
1581
|
-
}
|
|
1582
|
-
if (resource.protocol === "file:") {
|
|
1583
|
-
const filePath = normalizeSlashPath(resource.pathname);
|
|
1584
|
-
if (!projectRoot || filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)) return null;
|
|
1585
|
-
return excludeBuildChunk(safeRelativePath(
|
|
1586
|
-
filePath.slice(projectRoot.length).replace(/^\/+/, "").split("/").filter(Boolean)
|
|
1587
|
-
));
|
|
1588
|
-
}
|
|
1589
|
-
if (resource.protocol !== "http:" && resource.protocol !== "https:") return null;
|
|
1590
|
-
if (resource.origin !== base.origin) return null;
|
|
1591
|
-
let segments = decodePathname(resource.pathname).split("/").filter(Boolean);
|
|
1592
|
-
if (context.appId && segments[0] === context.appId) {
|
|
1593
|
-
segments = segments.slice(1);
|
|
1594
|
-
if (context.outputRoot && segments[0] === context.outputRoot) segments = segments.slice(1);
|
|
1595
|
-
}
|
|
1596
|
-
return excludeBuildChunk(safeRelativePath(segments));
|
|
1597
|
-
}
|
|
1598
1620
|
function projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot) {
|
|
1599
1621
|
let url;
|
|
1600
1622
|
try {
|
|
@@ -1632,10 +1654,16 @@ function buildDevtoolsProjectSourceLinksScript(context) {
|
|
|
1632
1654
|
}
|
|
1633
1655
|
|
|
1634
1656
|
const cfg = ${config}
|
|
1635
|
-
const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
|
|
1636
1657
|
const decodePathname = ${decodePathname.toString()}
|
|
1637
1658
|
const normalizeSlashPath = ${normalizeSlashPath.toString()}
|
|
1638
1659
|
const safeRelativePath = ${safeRelativePath.toString()}
|
|
1660
|
+
const excludeBuildChunk = ${excludeBuildChunk.toString()}
|
|
1661
|
+
const isOutsideKnownRoot = ${isOutsideKnownRoot.toString()}
|
|
1662
|
+
const resolveFilesystemRawPath = ${resolveFilesystemRawPath.toString()}
|
|
1663
|
+
const parseResourceUrl = ${parseResourceUrl.toString()}
|
|
1664
|
+
const resolveFileProtocolPath = ${resolveFileProtocolPath.toString()}
|
|
1665
|
+
const resolveHttpProtocolPath = ${resolveHttpProtocolPath.toString()}
|
|
1666
|
+
const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
|
|
1639
1667
|
const observedRoots = new Set()
|
|
1640
1668
|
const observers = []
|
|
1641
1669
|
const clickBindings = []
|
|
@@ -2243,9 +2271,9 @@ function installElementsForward(deps) {
|
|
|
2243
2271
|
});
|
|
2244
2272
|
}
|
|
2245
2273
|
replyResult(cmd, result);
|
|
2246
|
-
}).catch((
|
|
2274
|
+
}).catch((err2) => {
|
|
2247
2275
|
if (disposed) return;
|
|
2248
|
-
replyError(cmd,
|
|
2276
|
+
replyError(cmd, err2 instanceof Error ? err2.message : String(err2));
|
|
2249
2277
|
});
|
|
2250
2278
|
}
|
|
2251
2279
|
function handleOutbound(batch) {
|
|
@@ -2325,18 +2353,15 @@ function mapConsoleApiType(type) {
|
|
|
2325
2353
|
return "log";
|
|
2326
2354
|
}
|
|
2327
2355
|
var BIGINT_LITERAL = /^-?\d+n$/;
|
|
2328
|
-
function
|
|
2329
|
-
if (
|
|
2330
|
-
if ("
|
|
2331
|
-
if (
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
if (BIGINT_LITERAL.test(u)) return u;
|
|
2338
|
-
return u;
|
|
2339
|
-
}
|
|
2356
|
+
function unserializableToValue(u) {
|
|
2357
|
+
if (u === "Infinity") return Infinity;
|
|
2358
|
+
if (u === "-Infinity") return -Infinity;
|
|
2359
|
+
if (u === "NaN") return NaN;
|
|
2360
|
+
if (u === "-0") return -0;
|
|
2361
|
+
if (BIGINT_LITERAL.test(u)) return u;
|
|
2362
|
+
return u;
|
|
2363
|
+
}
|
|
2364
|
+
function typeFallbackToValue(ro) {
|
|
2340
2365
|
switch (ro.type) {
|
|
2341
2366
|
case "undefined":
|
|
2342
2367
|
return void 0;
|
|
@@ -2351,6 +2376,14 @@ function remoteObjectToValue(ro) {
|
|
|
2351
2376
|
return ro.description ?? "[Unknown]";
|
|
2352
2377
|
}
|
|
2353
2378
|
}
|
|
2379
|
+
function remoteObjectToValue(ro) {
|
|
2380
|
+
if (!ro) return ro;
|
|
2381
|
+
if ("value" in ro) return ro.value;
|
|
2382
|
+
if (typeof ro.unserializableValue === "string") {
|
|
2383
|
+
return unserializableToValue(ro.unserializableValue);
|
|
2384
|
+
}
|
|
2385
|
+
return typeFallbackToValue(ro);
|
|
2386
|
+
}
|
|
2354
2387
|
function needsDeepFetch(ro) {
|
|
2355
2388
|
if (!ro) return false;
|
|
2356
2389
|
if ("value" in ro) return false;
|
|
@@ -2627,8 +2660,8 @@ function createDevtoolsHost(ctx, reconciler, deps) {
|
|
|
2627
2660
|
emit: (entry) => ctx.consoleForwarder?.emit(entry)
|
|
2628
2661
|
}).stop;
|
|
2629
2662
|
return true;
|
|
2630
|
-
} catch (
|
|
2631
|
-
console.warn("[devtools-host] point-at-service-wc failed; retry will rebuild:",
|
|
2663
|
+
} catch (err2) {
|
|
2664
|
+
console.warn("[devtools-host] point-at-service-wc failed; retry will rebuild:", err2);
|
|
2632
2665
|
if (nativeDevtoolsSourceWc?.id === next.id) {
|
|
2633
2666
|
nativeDevtoolsSourceWc = null;
|
|
2634
2667
|
}
|
|
@@ -2822,8 +2855,8 @@ function createHostToolbarPortChannel(opts) {
|
|
|
2822
2855
|
function invokeReadyHandler(handler) {
|
|
2823
2856
|
try {
|
|
2824
2857
|
handler();
|
|
2825
|
-
} catch (
|
|
2826
|
-
console.error("[host-toolbar] onReady handler threw:",
|
|
2858
|
+
} catch (err2) {
|
|
2859
|
+
console.error("[host-toolbar] onReady handler threw:", err2);
|
|
2827
2860
|
}
|
|
2828
2861
|
}
|
|
2829
2862
|
function fireReadyHandlers() {
|
|
@@ -3228,8 +3261,8 @@ async function handleCustomApiBridgeRequest(apis, req) {
|
|
|
3228
3261
|
try {
|
|
3229
3262
|
const result = req.op === "list" ? apis.list() : await apis.invoke(req.name, req.params);
|
|
3230
3263
|
return { id: req.id, result };
|
|
3231
|
-
} catch (
|
|
3232
|
-
return { id: req.id, error:
|
|
3264
|
+
} catch (err2) {
|
|
3265
|
+
return { id: req.id, error: err2 instanceof Error ? err2.message : String(err2) };
|
|
3233
3266
|
}
|
|
3234
3267
|
}
|
|
3235
3268
|
function createSimulatorApiRegistry() {
|
|
@@ -3286,8 +3319,8 @@ function registerMiniappSessionConfigurator(fn) {
|
|
|
3286
3319
|
for (const partition of configuredPartitions) {
|
|
3287
3320
|
try {
|
|
3288
3321
|
fn(electron.session.fromPartition(partition), partition);
|
|
3289
|
-
} catch (
|
|
3290
|
-
console.warn("[miniapp-partition] configurator failed for", partition,
|
|
3322
|
+
} catch (err2) {
|
|
3323
|
+
console.warn("[miniapp-partition] configurator failed for", partition, err2);
|
|
3291
3324
|
}
|
|
3292
3325
|
}
|
|
3293
3326
|
return () => {
|
|
@@ -3303,8 +3336,8 @@ function configureMiniappSession(partition) {
|
|
|
3303
3336
|
for (const fn of configurators) {
|
|
3304
3337
|
try {
|
|
3305
3338
|
fn(sess, partition);
|
|
3306
|
-
} catch (
|
|
3307
|
-
console.warn("[miniapp-partition] configurator failed for", partition,
|
|
3339
|
+
} catch (err2) {
|
|
3340
|
+
console.warn("[miniapp-partition] configurator failed for", partition, err2);
|
|
3308
3341
|
}
|
|
3309
3342
|
}
|
|
3310
3343
|
return sess;
|
|
@@ -3457,7 +3490,7 @@ function createNativeSimulatorView(ctx, reconciler, deps) {
|
|
|
3457
3490
|
}
|
|
3458
3491
|
try {
|
|
3459
3492
|
if (!nativeSimulatorView.webContents.isDestroyed()) {
|
|
3460
|
-
ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)?.catch((
|
|
3493
|
+
ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)?.catch((err2) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err2));
|
|
3461
3494
|
nativeSimulatorView.webContents.close();
|
|
3462
3495
|
}
|
|
3463
3496
|
} catch {
|
|
@@ -3597,8 +3630,8 @@ function createNativeSimulatorView(ctx, reconciler, deps) {
|
|
|
3597
3630
|
e.preventDefault();
|
|
3598
3631
|
}
|
|
3599
3632
|
});
|
|
3600
|
-
void simWc.loadURL(simulatorUrl).catch((
|
|
3601
|
-
console.error("[workbench] attachNativeSimulator \u2014 loadURL failed",
|
|
3633
|
+
void simWc.loadURL(simulatorUrl).catch((err2) => {
|
|
3634
|
+
console.error("[workbench] attachNativeSimulator \u2014 loadURL failed", err2);
|
|
3602
3635
|
});
|
|
3603
3636
|
simulatorWebContentsId = simWc.id;
|
|
3604
3637
|
ctx.networkForward?.attachSimulator(simWc);
|
|
@@ -3840,8 +3873,8 @@ function validateProjectDir(dirPath) {
|
|
|
3840
3873
|
const resolvedRoot = path10.resolve(dirPath, cfg.miniprogramRoot);
|
|
3841
3874
|
return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
|
|
3842
3875
|
}
|
|
3843
|
-
} catch (
|
|
3844
|
-
log2.warn("Failed to parse project.config.json",
|
|
3876
|
+
} catch (err2) {
|
|
3877
|
+
log2.warn("Failed to parse project.config.json", err2);
|
|
3845
3878
|
}
|
|
3846
3879
|
}
|
|
3847
3880
|
return "\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u7A0B\u5E8F\u6E90\u7801\u7684\u76EE\u5F55";
|
|
@@ -3857,8 +3890,8 @@ function addProject(dirPath) {
|
|
|
3857
3890
|
const cfg = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
|
|
3858
3891
|
if (cfg.projectname) name = cfg.projectname;
|
|
3859
3892
|
}
|
|
3860
|
-
} catch (
|
|
3861
|
-
log2.warn("Failed to read project name from config",
|
|
3893
|
+
} catch (err2) {
|
|
3894
|
+
log2.warn("Failed to read project name from config", err2);
|
|
3862
3895
|
}
|
|
3863
3896
|
const project = { name, path: dirPath, lastOpened: null };
|
|
3864
3897
|
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
@@ -3900,7 +3933,8 @@ function getProjectPages(dirPath) {
|
|
|
3900
3933
|
pages: appJson.pages || [],
|
|
3901
3934
|
entryPagePath: appJson.entryPagePath || appJson.pages?.[0] || ""
|
|
3902
3935
|
};
|
|
3903
|
-
} catch {
|
|
3936
|
+
} catch (err2) {
|
|
3937
|
+
log2.warn(`Failed to read project pages from ${appJsonPath}`, err2);
|
|
3904
3938
|
return { pages: [], entryPagePath: "" };
|
|
3905
3939
|
}
|
|
3906
3940
|
}
|
|
@@ -4002,14 +4036,14 @@ function createWorkspaceService(ctx) {
|
|
|
4002
4036
|
let lastClosedProjectPath = "";
|
|
4003
4037
|
let logGeneration = 0;
|
|
4004
4038
|
const opLock = createOpLock();
|
|
4005
|
-
function sendStatus(status2, message, hotReload) {
|
|
4006
|
-
ctx.notify.projectStatus(
|
|
4039
|
+
function sendStatus(status2, message, hotReload, pages, watcherDead) {
|
|
4040
|
+
ctx.notify.projectStatus({ status: status2, message, ...hotReload ? { hotReload: true } : {}, ...pages ? { pages } : {}, ...watcherDead ? { watcher: "dead" } : {} });
|
|
4007
4041
|
}
|
|
4008
4042
|
function bestEffort(label, fn) {
|
|
4009
4043
|
try {
|
|
4010
4044
|
fn();
|
|
4011
|
-
} catch (
|
|
4012
|
-
console.warn(`[workspace] ${label} failed (non-fatal):`,
|
|
4045
|
+
} catch (err2) {
|
|
4046
|
+
console.warn(`[workspace] ${label} failed (non-fatal):`, err2);
|
|
4013
4047
|
}
|
|
4014
4048
|
}
|
|
4015
4049
|
async function disposeSession() {
|
|
@@ -4023,8 +4057,8 @@ function createWorkspaceService(ctx) {
|
|
|
4023
4057
|
(_, reject) => setTimeout(() => reject(new Error("session close timed out")), 5e3)
|
|
4024
4058
|
)
|
|
4025
4059
|
]);
|
|
4026
|
-
} catch (
|
|
4027
|
-
console.error("[workspace] session close failed:",
|
|
4060
|
+
} catch (err2) {
|
|
4061
|
+
console.error("[workspace] session close failed:", err2);
|
|
4028
4062
|
}
|
|
4029
4063
|
}
|
|
4030
4064
|
async function runOpenTeardown(mySeq) {
|
|
@@ -4064,17 +4098,22 @@ function createWorkspaceService(ctx) {
|
|
|
4064
4098
|
sourcemap: true,
|
|
4065
4099
|
fileTypes: ctx.fileTypes,
|
|
4066
4100
|
watch: compile.watch,
|
|
4067
|
-
onRebuild: () =>
|
|
4068
|
-
|
|
4101
|
+
onRebuild: () => {
|
|
4102
|
+
const { pages } = getProjectPages(projectPath);
|
|
4103
|
+
sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u91CD\u542F", true, pages.length ? pages : void 0);
|
|
4104
|
+
},
|
|
4105
|
+
onBuildError: (err2) => sendStatus("error", String(err2)),
|
|
4106
|
+
// Watcher died mid-session (EMFILE, permission loss, …): non-fatal, so 'ready' stays but `watcher: 'dead'` flags that saves no longer auto-rebuild.
|
|
4107
|
+
onWatcherError: () => sendStatus("ready", "\u6587\u4EF6\u76D1\u542C\u5DF2\u505C\u6B62\uFF0C\u4FDD\u5B58\u4E0D\u518D\u89E6\u53D1\u81EA\u52A8\u7F16\u8BD1", false, void 0, true),
|
|
4069
4108
|
onLog: (entry) => {
|
|
4070
4109
|
if (sessionGeneration !== logGeneration) return;
|
|
4071
4110
|
ctx.notify.compileLog({ stream: entry.stream, text: entry.text, at: Date.now() });
|
|
4072
4111
|
}
|
|
4073
4112
|
});
|
|
4074
4113
|
return { session: session5 };
|
|
4075
|
-
} catch (
|
|
4114
|
+
} catch (err2) {
|
|
4076
4115
|
clearSimulatorServicewechatReferer();
|
|
4077
|
-
return { error: String(
|
|
4116
|
+
return { error: String(err2) };
|
|
4078
4117
|
}
|
|
4079
4118
|
}
|
|
4080
4119
|
async function rejectInvalidAppId(session5) {
|
|
@@ -4112,8 +4151,8 @@ function createWorkspaceService(ctx) {
|
|
|
4112
4151
|
if (ctx.onBeforeOpenProject) {
|
|
4113
4152
|
try {
|
|
4114
4153
|
await ctx.onBeforeOpenProject(projectPath);
|
|
4115
|
-
} catch (
|
|
4116
|
-
const error =
|
|
4154
|
+
} catch (err2) {
|
|
4155
|
+
const error = err2 instanceof Error ? err2.message : String(err2);
|
|
4117
4156
|
sendStatus("error", error);
|
|
4118
4157
|
return { success: false, error };
|
|
4119
4158
|
}
|
|
@@ -4607,51 +4646,60 @@ import path14 from "path";
|
|
|
4607
4646
|
import fs7 from "node:fs";
|
|
4608
4647
|
import path13 from "node:path";
|
|
4609
4648
|
var DEFAULT_TEMPLATE_ID = "blank";
|
|
4610
|
-
|
|
4611
|
-
const name = (
|
|
4649
|
+
function resolveName(rawName) {
|
|
4650
|
+
const name = (rawName ?? "").trim();
|
|
4612
4651
|
if (name.length === 0) {
|
|
4613
4652
|
throw new Error("Project name cannot be empty");
|
|
4614
4653
|
}
|
|
4615
|
-
|
|
4654
|
+
return name;
|
|
4655
|
+
}
|
|
4656
|
+
function validateTargetPath(target) {
|
|
4616
4657
|
if (!target || typeof target !== "string") {
|
|
4617
4658
|
throw new Error("Project path is required");
|
|
4618
4659
|
}
|
|
4619
|
-
if (fs7.existsSync(target))
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
}
|
|
4624
|
-
const entries = fs7.readdirSync(target);
|
|
4625
|
-
if (entries.length > 0) {
|
|
4626
|
-
throw new Error(
|
|
4627
|
-
`Project path is not empty (refusing to overwrite): ${target}`
|
|
4628
|
-
);
|
|
4629
|
-
}
|
|
4660
|
+
if (!fs7.existsSync(target)) return;
|
|
4661
|
+
const stat = fs7.statSync(target);
|
|
4662
|
+
if (!stat.isDirectory()) {
|
|
4663
|
+
throw new Error(`Project path exists and is not a directory: ${target}`);
|
|
4630
4664
|
}
|
|
4631
|
-
const
|
|
4632
|
-
|
|
4665
|
+
const entries = fs7.readdirSync(target);
|
|
4666
|
+
if (entries.length > 0) {
|
|
4667
|
+
throw new Error(
|
|
4668
|
+
`Project path is not empty (refusing to overwrite): ${target}`
|
|
4669
|
+
);
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
function resolveTemplate(templates, templateId) {
|
|
4673
|
+
const id = templateId ?? DEFAULT_TEMPLATE_ID;
|
|
4674
|
+
const template = templates.find((t) => t.id === id);
|
|
4633
4675
|
if (!template) {
|
|
4634
|
-
throw new Error(`Template not found: ${
|
|
4676
|
+
throw new Error(`Template not found: ${id}`);
|
|
4635
4677
|
}
|
|
4636
4678
|
if (!template.source && !template.generate) {
|
|
4637
4679
|
throw new Error(
|
|
4638
|
-
`Template '${
|
|
4680
|
+
`Template '${id}' has neither a source directory nor a generate function`
|
|
4639
4681
|
);
|
|
4640
4682
|
}
|
|
4683
|
+
return template;
|
|
4684
|
+
}
|
|
4685
|
+
async function materializeTemplate(target, template, name) {
|
|
4641
4686
|
fs7.mkdirSync(target, { recursive: true });
|
|
4642
4687
|
if (template.generate) {
|
|
4643
4688
|
await template.generate(target, { name });
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
recursive: true,
|
|
4652
|
-
force: true
|
|
4653
|
-
});
|
|
4689
|
+
return;
|
|
4690
|
+
}
|
|
4691
|
+
if (!template.source) return;
|
|
4692
|
+
if (!fs7.existsSync(template.source.path)) {
|
|
4693
|
+
throw new Error(
|
|
4694
|
+
`Template source missing on disk: ${template.source.path}`
|
|
4695
|
+
);
|
|
4654
4696
|
}
|
|
4697
|
+
fs7.cpSync(template.source.path, target, {
|
|
4698
|
+
recursive: true,
|
|
4699
|
+
force: true
|
|
4700
|
+
});
|
|
4701
|
+
}
|
|
4702
|
+
function writeProjectConfig(target, name) {
|
|
4655
4703
|
const cfgPath = path13.join(target, "project.config.json");
|
|
4656
4704
|
let cfg = {};
|
|
4657
4705
|
if (fs7.existsSync(cfgPath)) {
|
|
@@ -4663,6 +4711,14 @@ async function createProject(input, ctx) {
|
|
|
4663
4711
|
}
|
|
4664
4712
|
cfg.projectname = name;
|
|
4665
4713
|
fs7.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
4714
|
+
}
|
|
4715
|
+
async function createProject(input, ctx) {
|
|
4716
|
+
const name = resolveName(input.name);
|
|
4717
|
+
const target = input.path;
|
|
4718
|
+
validateTargetPath(target);
|
|
4719
|
+
const template = resolveTemplate(ctx.templates, input.templateId);
|
|
4720
|
+
await materializeTemplate(target, template, name);
|
|
4721
|
+
writeProjectConfig(target, name);
|
|
4666
4722
|
const created = await ctx.projectsProvider.addProject(target);
|
|
4667
4723
|
return created;
|
|
4668
4724
|
}
|
|
@@ -4726,15 +4782,15 @@ function registerProjectsIpc(ctx) {
|
|
|
4726
4782
|
templates: ctx.projectTemplates ?? [],
|
|
4727
4783
|
projectsProvider: ctx.projectsProvider
|
|
4728
4784
|
});
|
|
4729
|
-
} catch (
|
|
4785
|
+
} catch (err2) {
|
|
4730
4786
|
await dialog.showMessageBox(ctx.windows.mainWindow, {
|
|
4731
4787
|
type: "error",
|
|
4732
4788
|
title: "\u521B\u5EFA\u9879\u76EE\u5931\u8D25",
|
|
4733
4789
|
message: "\u65E0\u6CD5\u521B\u5EFA\u9879\u76EE",
|
|
4734
|
-
detail:
|
|
4790
|
+
detail: err2 instanceof Error ? err2.message : String(err2),
|
|
4735
4791
|
buttons: ["\u786E\u5B9A"]
|
|
4736
4792
|
});
|
|
4737
|
-
throw
|
|
4793
|
+
throw err2;
|
|
4738
4794
|
}
|
|
4739
4795
|
try {
|
|
4740
4796
|
const settings = loadWorkbenchSettings();
|
|
@@ -4742,8 +4798,8 @@ function registerProjectsIpc(ctx) {
|
|
|
4742
4798
|
if (newBase && settings.lastCreateBaseDir !== newBase) {
|
|
4743
4799
|
saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
|
|
4744
4800
|
}
|
|
4745
|
-
} catch (
|
|
4746
|
-
console.warn("[projects:create] failed to persist lastCreateBaseDir",
|
|
4801
|
+
} catch (err2) {
|
|
4802
|
+
console.warn("[projects:create] failed to persist lastCreateBaseDir", err2);
|
|
4747
4803
|
}
|
|
4748
4804
|
return project;
|
|
4749
4805
|
}).handle(ProjectsChannel.GetCreateDefaults, () => {
|
|
@@ -4913,11 +4969,30 @@ import { app as app12, ipcMain as ipcMain3, protocol as protocol2, session as el
|
|
|
4913
4969
|
import path17 from "node:path";
|
|
4914
4970
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
4915
4971
|
|
|
4972
|
+
// src/shared/request-core.ts
|
|
4973
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
4974
|
+
var MAX_TIMEOUT_MS = 2147483647;
|
|
4975
|
+
function resolveTimeoutBudgetMs(timeout) {
|
|
4976
|
+
const t = Number(timeout);
|
|
4977
|
+
return Number.isFinite(t) && t > 0 && t <= MAX_TIMEOUT_MS ? t : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
4978
|
+
}
|
|
4979
|
+
|
|
4916
4980
|
// src/shared/simulator-api-metadata.ts
|
|
4917
4981
|
var PERSISTENT_SIMULATOR_APIS = /* @__PURE__ */ new Set(["audioListen"]);
|
|
4918
4982
|
function isPersistentSimulatorApi(name) {
|
|
4919
4983
|
return PERSISTENT_SIMULATOR_APIS.has(name);
|
|
4920
4984
|
}
|
|
4985
|
+
var NETWORK_BUDGET_SIMULATOR_APIS = /* @__PURE__ */ new Set([
|
|
4986
|
+
"request",
|
|
4987
|
+
"downloadFile",
|
|
4988
|
+
"uploadFile"
|
|
4989
|
+
]);
|
|
4990
|
+
var API_CALL_WATCHDOG_MS = 5e3;
|
|
4991
|
+
function apiCallWatchdogMs(name, params) {
|
|
4992
|
+
if (!NETWORK_BUDGET_SIMULATOR_APIS.has(name)) return API_CALL_WATCHDOG_MS;
|
|
4993
|
+
const budget = resolveTimeoutBudgetMs(params?.timeout);
|
|
4994
|
+
return Math.min(budget + API_CALL_WATCHDOG_MS, MAX_TIMEOUT_MS);
|
|
4995
|
+
}
|
|
4921
4996
|
|
|
4922
4997
|
// src/main/ipc/session-listener-bag.ts
|
|
4923
4998
|
function createSessionListenerBag() {
|
|
@@ -4984,7 +5059,7 @@ async function startDiminaResourceServer(rootDir) {
|
|
|
4984
5059
|
baseUrl: `http://127.0.0.1:${address.port}/`,
|
|
4985
5060
|
close() {
|
|
4986
5061
|
return new Promise((resolve, reject) => {
|
|
4987
|
-
server.close((
|
|
5062
|
+
server.close((err2) => err2 ? reject(err2) : resolve());
|
|
4988
5063
|
});
|
|
4989
5064
|
}
|
|
4990
5065
|
};
|
|
@@ -5084,10 +5159,13 @@ function buildServiceHostSpawnUrl(opts) {
|
|
|
5084
5159
|
}
|
|
5085
5160
|
return url.toString();
|
|
5086
5161
|
}
|
|
5087
|
-
function navigateServiceHost(win, url) {
|
|
5162
|
+
function navigateServiceHost(win, url, opts) {
|
|
5088
5163
|
const loaded = Promise.resolve(win.loadURL(url)).then(
|
|
5089
5164
|
() => void 0,
|
|
5090
|
-
() =>
|
|
5165
|
+
(err2) => {
|
|
5166
|
+
opts?.onLoadFailed?.(err2);
|
|
5167
|
+
return void 0;
|
|
5168
|
+
}
|
|
5091
5169
|
);
|
|
5092
5170
|
if (!app10.isPackaged && process.env.NODE_ENV !== "test") {
|
|
5093
5171
|
const w = win;
|
|
@@ -5126,7 +5204,7 @@ function serviceHostSpec(appId, projectPath) {
|
|
|
5126
5204
|
}
|
|
5127
5205
|
function createServiceHostWindow(opts) {
|
|
5128
5206
|
const win = constructServiceHostWindow({ appId: opts.appId, partition: miniappPartition(opts.appId, opts.projectPath) });
|
|
5129
|
-
void navigateServiceHost(win, buildServiceHostSpawnUrl(opts));
|
|
5207
|
+
void navigateServiceHost(win, buildServiceHostSpawnUrl(opts), { onLoadFailed: opts.onLoadFailed });
|
|
5130
5208
|
return win;
|
|
5131
5209
|
}
|
|
5132
5210
|
|
|
@@ -5443,9 +5521,49 @@ function buildForwardScript(level, args) {
|
|
|
5443
5521
|
return `(()=>{try{const a=JSON.parse(${JSON.stringify(argsJson)});console[${JSON.stringify(method)}]('[\u89C6\u56FE]',...a)}catch(_){}})()
|
|
5444
5522
|
//# sourceURL=${RENDER_FORWARD_SOURCE_URL}`;
|
|
5445
5523
|
}
|
|
5446
|
-
|
|
5524
|
+
var DIAGNOSTIC_CONSOLE_CALL = {
|
|
5525
|
+
error: "console.error",
|
|
5526
|
+
warn: "console.warn",
|
|
5527
|
+
info: "console.info"
|
|
5528
|
+
};
|
|
5529
|
+
function buildDiagnosticScript(severity, message) {
|
|
5530
|
+
const call = DIAGNOSTIC_CONSOLE_CALL[severity];
|
|
5531
|
+
const argsJson = JSON.stringify([`[dimina-kit] ${message}`]);
|
|
5532
|
+
return `(()=>{try{const a=JSON.parse(${JSON.stringify(argsJson)});${call}(...a)}catch(_){}})()
|
|
5533
|
+
//# sourceURL=${RENDER_FORWARD_SOURCE_URL}`;
|
|
5534
|
+
}
|
|
5535
|
+
function createConsoleForwarder(bridge, diagnostics) {
|
|
5447
5536
|
const sinks = /* @__PURE__ */ new Set();
|
|
5448
5537
|
const registry = new DisposableRegistry4();
|
|
5538
|
+
const pendingBySession = /* @__PURE__ */ new Map();
|
|
5539
|
+
const pendingGlobal = [];
|
|
5540
|
+
const readySessions = /* @__PURE__ */ new Set();
|
|
5541
|
+
function injectDiagnostic(wc, d) {
|
|
5542
|
+
wc.executeJavaScript(buildDiagnosticScript(d.severity, d.message), true).catch(() => {
|
|
5543
|
+
});
|
|
5544
|
+
}
|
|
5545
|
+
function handleDiagnostic(d) {
|
|
5546
|
+
if (d.appSessionId) {
|
|
5547
|
+
const wc2 = readySessions.has(d.appSessionId) && bridge.getServiceWcForBridge ? bridge.getServiceWcForBridge(d.appSessionId) : null;
|
|
5548
|
+
if (wc2 && !wc2.isDestroyed()) {
|
|
5549
|
+
injectDiagnostic(wc2, d);
|
|
5550
|
+
return;
|
|
5551
|
+
}
|
|
5552
|
+
const bucket = pendingBySession.get(d.appSessionId);
|
|
5553
|
+
if (bucket) bucket.push(d);
|
|
5554
|
+
else pendingBySession.set(d.appSessionId, [d]);
|
|
5555
|
+
return;
|
|
5556
|
+
}
|
|
5557
|
+
const wc = bridge.getServiceWc();
|
|
5558
|
+
if (wc && !wc.isDestroyed()) {
|
|
5559
|
+
injectDiagnostic(wc, d);
|
|
5560
|
+
return;
|
|
5561
|
+
}
|
|
5562
|
+
pendingGlobal.push(d);
|
|
5563
|
+
}
|
|
5564
|
+
if (diagnostics) {
|
|
5565
|
+
registry.add(diagnostics.subscribe(handleDiagnostic, { replay: true }));
|
|
5566
|
+
}
|
|
5449
5567
|
function forwardRenderToServiceHost(entry) {
|
|
5450
5568
|
let wc = null;
|
|
5451
5569
|
if (entry.bridgeId && bridge.getServiceWcForBridge) {
|
|
@@ -5482,13 +5600,84 @@ function createConsoleForwarder(bridge) {
|
|
|
5482
5600
|
sinks.delete(sink);
|
|
5483
5601
|
}));
|
|
5484
5602
|
},
|
|
5603
|
+
notifyServiceHostReady(appSessionId) {
|
|
5604
|
+
readySessions.add(appSessionId);
|
|
5605
|
+
const wc = bridge.getServiceWcForBridge ? bridge.getServiceWcForBridge(appSessionId) : null;
|
|
5606
|
+
if (!wc) return;
|
|
5607
|
+
const sessionEntries = pendingBySession.get(appSessionId);
|
|
5608
|
+
if (sessionEntries) {
|
|
5609
|
+
for (const d of sessionEntries) injectDiagnostic(wc, d);
|
|
5610
|
+
pendingBySession.delete(appSessionId);
|
|
5611
|
+
}
|
|
5612
|
+
if (pendingGlobal.length) {
|
|
5613
|
+
for (const d of pendingGlobal) injectDiagnostic(wc, d);
|
|
5614
|
+
pendingGlobal.length = 0;
|
|
5615
|
+
}
|
|
5616
|
+
},
|
|
5485
5617
|
dispose() {
|
|
5486
5618
|
sinks.clear();
|
|
5619
|
+
pendingBySession.clear();
|
|
5620
|
+
pendingGlobal.length = 0;
|
|
5621
|
+
readySessions.clear();
|
|
5487
5622
|
return registry.disposeAll();
|
|
5488
5623
|
}
|
|
5489
5624
|
};
|
|
5490
5625
|
}
|
|
5491
5626
|
|
|
5627
|
+
// src/main/services/diagnostics/index.ts
|
|
5628
|
+
var DEFAULT_BUFFER_CAP = 200;
|
|
5629
|
+
var CONSOLE_METHOD = {
|
|
5630
|
+
error: "error",
|
|
5631
|
+
warn: "warn",
|
|
5632
|
+
info: "info"
|
|
5633
|
+
};
|
|
5634
|
+
function createDiagnosticsBus(opts) {
|
|
5635
|
+
const bufferCap = opts?.bufferCap && opts.bufferCap > 0 ? opts.bufferCap : DEFAULT_BUFFER_CAP;
|
|
5636
|
+
const buffer = [];
|
|
5637
|
+
const sinks = /* @__PURE__ */ new Set();
|
|
5638
|
+
let disposed = false;
|
|
5639
|
+
return {
|
|
5640
|
+
report(d) {
|
|
5641
|
+
if (disposed) return;
|
|
5642
|
+
const entry = { ...d, ts: Date.now() };
|
|
5643
|
+
buffer.push(entry);
|
|
5644
|
+
if (buffer.length > bufferCap) buffer.shift();
|
|
5645
|
+
console[CONSOLE_METHOD[entry.severity]](`[dimina-kit:${entry.code}] ${entry.message}`);
|
|
5646
|
+
for (const sink of sinks) {
|
|
5647
|
+
try {
|
|
5648
|
+
sink(entry);
|
|
5649
|
+
} catch {
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
},
|
|
5653
|
+
subscribe(sink, subOpts) {
|
|
5654
|
+
const replay = subOpts?.replay ?? true;
|
|
5655
|
+
if (replay) {
|
|
5656
|
+
for (const entry of buffer) {
|
|
5657
|
+
try {
|
|
5658
|
+
sink(entry);
|
|
5659
|
+
} catch {
|
|
5660
|
+
}
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
sinks.add(sink);
|
|
5664
|
+
let released = false;
|
|
5665
|
+
return {
|
|
5666
|
+
dispose() {
|
|
5667
|
+
if (released) return;
|
|
5668
|
+
released = true;
|
|
5669
|
+
sinks.delete(sink);
|
|
5670
|
+
}
|
|
5671
|
+
};
|
|
5672
|
+
},
|
|
5673
|
+
dispose() {
|
|
5674
|
+
disposed = true;
|
|
5675
|
+
sinks.clear();
|
|
5676
|
+
buffer.length = 0;
|
|
5677
|
+
}
|
|
5678
|
+
};
|
|
5679
|
+
}
|
|
5680
|
+
|
|
5492
5681
|
// src/main/services/simulator-storage/index.ts
|
|
5493
5682
|
import { app as app11, webContents as wcStatic } from "electron";
|
|
5494
5683
|
import { DisposableRegistry as DisposableRegistry5 } from "@dimina-kit/electron-deck/main";
|
|
@@ -6059,6 +6248,7 @@ function rewriteSourceMappingUrl(source, scriptUrl) {
|
|
|
6059
6248
|
//# sourceMappingURL=${absolute}`;
|
|
6060
6249
|
}
|
|
6061
6250
|
var STACK_ID = "stack_0";
|
|
6251
|
+
var LAUNCH_TIMEOUT_MS = 2e4;
|
|
6062
6252
|
var PREWARM_MAX_POOL_SIZE = 4;
|
|
6063
6253
|
function resolvePrewarmPoolSize() {
|
|
6064
6254
|
if (process.env.DIMINA_PREWARM_DISABLE === "1") return 0;
|
|
@@ -6080,28 +6270,33 @@ function summarizeBridgeMsg(payload) {
|
|
|
6080
6270
|
const parts = [type, bridgeId ? `bridge=${bridgeId}` : void 0].filter(Boolean);
|
|
6081
6271
|
return parts.length > 0 ? parts.join(" ") : void 0;
|
|
6082
6272
|
}
|
|
6083
|
-
|
|
6273
|
+
function findAppSessionByAppId(state, appId) {
|
|
6274
|
+
let match;
|
|
6275
|
+
for (const ap of state.appSessions.values()) if (ap.appId === appId) match = ap;
|
|
6276
|
+
return match;
|
|
6277
|
+
}
|
|
6278
|
+
function resolveFallbackAppSession(state) {
|
|
6279
|
+
let last;
|
|
6280
|
+
const distinctAppIds = /* @__PURE__ */ new Set();
|
|
6281
|
+
for (const ap of state.appSessions.values()) {
|
|
6282
|
+
last = ap;
|
|
6283
|
+
distinctAppIds.add(ap.appId);
|
|
6284
|
+
}
|
|
6285
|
+
return distinctAppIds.size <= 1 ? last : void 0;
|
|
6286
|
+
}
|
|
6084
6287
|
function resolveCurrentApp(state, ctx, appId) {
|
|
6085
6288
|
if (appId) {
|
|
6086
|
-
|
|
6087
|
-
for (const ap of state.appSessions.values()) if (ap.appId === appId) match = ap;
|
|
6289
|
+
const match = findAppSessionByAppId(state, appId);
|
|
6088
6290
|
if (match) return match;
|
|
6089
6291
|
}
|
|
6090
6292
|
const appInfo = ctx.workspace?.getSession?.()?.appInfo;
|
|
6091
6293
|
const activeAppId = appInfo?.appId;
|
|
6092
6294
|
if (activeAppId) {
|
|
6093
|
-
|
|
6094
|
-
for (const ap of state.appSessions.values()) if (ap.appId === activeAppId) match = ap;
|
|
6295
|
+
const match = findAppSessionByAppId(state, activeAppId);
|
|
6095
6296
|
if (match) return match;
|
|
6096
6297
|
}
|
|
6097
6298
|
if (ctx.workspace?.isClosing?.()) return void 0;
|
|
6098
|
-
|
|
6099
|
-
const distinctAppIds = /* @__PURE__ */ new Set();
|
|
6100
|
-
for (const ap of state.appSessions.values()) {
|
|
6101
|
-
last = ap;
|
|
6102
|
-
distinctAppIds.add(ap.appId);
|
|
6103
|
-
}
|
|
6104
|
-
return distinctAppIds.size <= 1 ? last : void 0;
|
|
6299
|
+
return resolveFallbackAppSession(state);
|
|
6105
6300
|
}
|
|
6106
6301
|
function installBridgeRouter(ctx) {
|
|
6107
6302
|
const state = {
|
|
@@ -6244,13 +6439,20 @@ function installBridgeRouter(ctx) {
|
|
|
6244
6439
|
;
|
|
6245
6440
|
globalThis.__diminaResourceCensus = () => bridgeHandle.census?.();
|
|
6246
6441
|
}
|
|
6247
|
-
const
|
|
6442
|
+
const ownsDiagnosticsBus = ctx.diagnostics === void 0;
|
|
6443
|
+
const diagnosticsBus = ctx.diagnostics ?? createDiagnosticsBus();
|
|
6444
|
+
ctx.diagnostics = diagnosticsBus;
|
|
6445
|
+
const consoleForwarder = createConsoleForwarder(bridgeHandle, diagnosticsBus);
|
|
6248
6446
|
ctx.consoleForwarder = consoleForwarder;
|
|
6249
6447
|
ctx.guestConsole = consoleForwarder;
|
|
6250
6448
|
ctx.registry.add(() => {
|
|
6251
6449
|
void consoleForwarder.dispose();
|
|
6252
6450
|
ctx.consoleForwarder = void 0;
|
|
6253
6451
|
ctx.guestConsole = void 0;
|
|
6452
|
+
if (ownsDiagnosticsBus) {
|
|
6453
|
+
diagnosticsBus.dispose();
|
|
6454
|
+
ctx.diagnostics = void 0;
|
|
6455
|
+
}
|
|
6254
6456
|
});
|
|
6255
6457
|
const onActivePage = (event, payload) => {
|
|
6256
6458
|
const ap = state.appSessions.get(payload.appSessionId);
|
|
@@ -6410,6 +6612,39 @@ function installBridgeRouter(ctx) {
|
|
|
6410
6612
|
);
|
|
6411
6613
|
});
|
|
6412
6614
|
}
|
|
6615
|
+
function clearLaunchTimer(ap) {
|
|
6616
|
+
if (ap.launchTimer === null) return;
|
|
6617
|
+
clearTimeout(ap.launchTimer);
|
|
6618
|
+
ap.launchTimer = null;
|
|
6619
|
+
}
|
|
6620
|
+
function startLaunchTimer(state, ctx, ap) {
|
|
6621
|
+
ap.launchTimer = setTimeout(() => {
|
|
6622
|
+
ap.launchTimer = null;
|
|
6623
|
+
if (ap.running) return;
|
|
6624
|
+
if (state.appSessions.get(ap.appSessionId) !== ap) return;
|
|
6625
|
+
const reason = `Service host did not report readiness within ${LAUNCH_TIMEOUT_MS}ms`;
|
|
6626
|
+
ctx.diagnostics?.report({
|
|
6627
|
+
severity: "error",
|
|
6628
|
+
code: "launch-timeout",
|
|
6629
|
+
message: reason,
|
|
6630
|
+
appSessionId: ap.appSessionId
|
|
6631
|
+
});
|
|
6632
|
+
pushRuntimeStatus(ctx, ap, { phase: "launch-failed", code: "timeout", reason });
|
|
6633
|
+
}, LAUNCH_TIMEOUT_MS);
|
|
6634
|
+
}
|
|
6635
|
+
function markSessionRunning(ctx, ap, page) {
|
|
6636
|
+
if (!page.isRoot || ap.running) return;
|
|
6637
|
+
ap.running = true;
|
|
6638
|
+
clearLaunchTimer(ap);
|
|
6639
|
+
pushRuntimeStatus(ctx, ap, { phase: "running" });
|
|
6640
|
+
}
|
|
6641
|
+
function pushRuntimeStatus(ctx, session5, status2) {
|
|
6642
|
+
ctx.notify?.sessionRuntimeStatus?.({
|
|
6643
|
+
appId: session5.appId,
|
|
6644
|
+
...status2,
|
|
6645
|
+
...session5.pageFallback ? { pageFallback: session5.pageFallback } : {}
|
|
6646
|
+
});
|
|
6647
|
+
}
|
|
6413
6648
|
async function handleSpawn(state, ctx, event, opts) {
|
|
6414
6649
|
const appId = opts.appId;
|
|
6415
6650
|
if (!appId) throw new Error("[bridge-router] spawn requires appId");
|
|
@@ -6435,20 +6670,48 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
6435
6670
|
...selectedDevice ? deviceInfoToHostEnv(selectedDevice) : {}
|
|
6436
6671
|
});
|
|
6437
6672
|
const appConfig = await loadAppConfig(
|
|
6438
|
-
resourceServer ? resourceServer.baseUrl : `${resourceBaseUrl}${appId}/${root}
|
|
6673
|
+
resourceServer ? resourceServer.baseUrl : `${resourceBaseUrl}${appId}/${root}/`,
|
|
6674
|
+
({ url, error }) => {
|
|
6675
|
+
ctx.diagnostics?.report({
|
|
6676
|
+
severity: "error",
|
|
6677
|
+
code: "app-config-unreachable",
|
|
6678
|
+
message: `app-config.json unreachable at ${url}: ${String(error)}`,
|
|
6679
|
+
appSessionId
|
|
6680
|
+
});
|
|
6681
|
+
}
|
|
6439
6682
|
);
|
|
6440
6683
|
const manifest = buildAppManifest(appConfig, pagePath);
|
|
6441
|
-
const
|
|
6442
|
-
|
|
6684
|
+
const { resolvedPagePath, pageFallbackApplied } = resolveRootPagePath(manifest, pagePath);
|
|
6685
|
+
if (pageFallbackApplied) {
|
|
6686
|
+
reportPageNotFound(ctx, appSessionId, pagePath, resolvedPagePath);
|
|
6687
|
+
}
|
|
6688
|
+
const rootWindowConfig = resolvePageWindowConfig(appConfig, resolvedPagePath);
|
|
6689
|
+
const isTab = isTabPage(appConfig, resolvedPagePath);
|
|
6443
6690
|
const usedPool = state.pool !== null;
|
|
6444
6691
|
let poolEntryId = null;
|
|
6445
6692
|
let serviceWindow;
|
|
6446
|
-
|
|
6447
|
-
const
|
|
6693
|
+
const reportServiceHostNavigationFailed = (spawnUrl, err2) => {
|
|
6694
|
+
const message = `Failed to navigate service host to ${spawnUrl}: ${String(err2)}`;
|
|
6695
|
+
ctx.diagnostics?.report({
|
|
6696
|
+
severity: "error",
|
|
6697
|
+
code: "service-host-navigation-failed",
|
|
6698
|
+
message,
|
|
6699
|
+
appSessionId
|
|
6700
|
+
});
|
|
6701
|
+
const ap = state.appSessions.get(appSessionId);
|
|
6702
|
+
if (ap) clearLaunchTimer(ap);
|
|
6703
|
+
pushRuntimeStatus(
|
|
6704
|
+
ctx,
|
|
6705
|
+
ap ?? { appId, pageFallback: pageFallbackApplied ? { requested: pagePath, resolved: resolvedPagePath } : null },
|
|
6706
|
+
{ phase: "launch-failed", code: "service-host-navigation-failed", reason: message }
|
|
6707
|
+
);
|
|
6708
|
+
};
|
|
6709
|
+
if (state.pool) {
|
|
6710
|
+
const acquired = await state.pool.acquire(serviceHostSpec());
|
|
6448
6711
|
serviceWindow = acquired.win;
|
|
6449
6712
|
poolEntryId = acquired.entryId;
|
|
6450
6713
|
} else {
|
|
6451
|
-
|
|
6714
|
+
const freshWindowOptions = {
|
|
6452
6715
|
bridgeId,
|
|
6453
6716
|
appId,
|
|
6454
6717
|
// Same (appId, projectPath) pair the simulator WCV uses, so this project's
|
|
@@ -6458,12 +6721,16 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
6458
6721
|
// awaits above would otherwise give the service host a different path than
|
|
6459
6722
|
// the simulator WCV was built with, splitting the partition.
|
|
6460
6723
|
projectPath: workspaceProjectPath || void 0,
|
|
6461
|
-
pagePath,
|
|
6724
|
+
pagePath: resolvedPagePath,
|
|
6462
6725
|
pkgRoot,
|
|
6463
6726
|
root,
|
|
6464
6727
|
resourceBaseUrl,
|
|
6465
6728
|
hostEnvSnapshot: hostEnv,
|
|
6466
6729
|
apiNamespaces
|
|
6730
|
+
};
|
|
6731
|
+
serviceWindow = createServiceHostWindow({
|
|
6732
|
+
...freshWindowOptions,
|
|
6733
|
+
onLoadFailed: (err2) => reportServiceHostNavigationFailed(buildServiceHostSpawnUrl(freshWindowOptions), err2)
|
|
6467
6734
|
});
|
|
6468
6735
|
}
|
|
6469
6736
|
const appSession = {
|
|
@@ -6487,12 +6754,15 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
6487
6754
|
poolEntryId,
|
|
6488
6755
|
onServiceBoot: null,
|
|
6489
6756
|
listenerBag: createSessionListenerBag(),
|
|
6490
|
-
registryHandle: null
|
|
6757
|
+
registryHandle: null,
|
|
6758
|
+
running: false,
|
|
6759
|
+
launchTimer: null,
|
|
6760
|
+
pageFallback: pageFallbackApplied ? { requested: pagePath, resolved: resolvedPagePath } : null
|
|
6491
6761
|
};
|
|
6492
6762
|
const rootPage = {
|
|
6493
6763
|
bridgeId,
|
|
6494
6764
|
appSessionId,
|
|
6495
|
-
pagePath,
|
|
6765
|
+
pagePath: resolvedPagePath,
|
|
6496
6766
|
query: opts.query ?? {},
|
|
6497
6767
|
isRoot: true,
|
|
6498
6768
|
isTab,
|
|
@@ -6537,28 +6807,43 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
6537
6807
|
};
|
|
6538
6808
|
appSession.onServiceBoot = bootOnServiceLoad;
|
|
6539
6809
|
serviceWindow.webContents.on("did-finish-load", bootOnServiceLoad);
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
);
|
|
6810
|
+
const spawnUrl = buildServiceHostSpawnUrl({
|
|
6811
|
+
bridgeId,
|
|
6812
|
+
appId,
|
|
6813
|
+
pagePath: resolvedPagePath,
|
|
6814
|
+
pkgRoot,
|
|
6815
|
+
root,
|
|
6816
|
+
resourceBaseUrl,
|
|
6817
|
+
hostEnvSnapshot: hostEnv,
|
|
6818
|
+
apiNamespaces
|
|
6819
|
+
});
|
|
6820
|
+
void navigateServiceHost(serviceWindow, spawnUrl, {
|
|
6821
|
+
onLoadFailed: (err2) => reportServiceHostNavigationFailed(spawnUrl, err2)
|
|
6822
|
+
});
|
|
6553
6823
|
} else {
|
|
6554
6824
|
serviceWindow.webContents.once("did-finish-load", () => {
|
|
6555
6825
|
void bootServiceHost(state, appSession, ctx);
|
|
6556
6826
|
});
|
|
6557
6827
|
}
|
|
6828
|
+
const onServiceCrashed = () => {
|
|
6829
|
+
clearLaunchTimer(appSession);
|
|
6830
|
+
ctx.diagnostics?.report({
|
|
6831
|
+
severity: "error",
|
|
6832
|
+
code: "service-host-crashed",
|
|
6833
|
+
message: `Service host renderer process gone for appSessionId=${appSessionId}`,
|
|
6834
|
+
appSessionId
|
|
6835
|
+
});
|
|
6836
|
+
pushRuntimeStatus(ctx, appSession, { phase: "crashed", code: "service-host-crashed" });
|
|
6837
|
+
};
|
|
6838
|
+
appSession.listenerBag.on(serviceWindow.webContents, "render-process-gone", onServiceCrashed);
|
|
6839
|
+
pushRuntimeStatus(ctx, appSession, { phase: "launching" });
|
|
6840
|
+
startLaunchTimer(state, ctx, appSession);
|
|
6558
6841
|
return {
|
|
6559
6842
|
appSessionId,
|
|
6560
6843
|
bridgeId,
|
|
6561
6844
|
pagePath,
|
|
6845
|
+
resolvedPagePath,
|
|
6846
|
+
pageFallbackApplied,
|
|
6562
6847
|
serviceWcId: serviceWindow.webContents.id,
|
|
6563
6848
|
resourceBaseUrl,
|
|
6564
6849
|
manifest,
|
|
@@ -6572,6 +6857,9 @@ async function handlePageOpen(state, event, opts) {
|
|
|
6572
6857
|
throw new Error("[bridge-router] PAGE_OPEN rejected: caller not bound to app session");
|
|
6573
6858
|
}
|
|
6574
6859
|
const pagePath = normalizePagePath(opts.pagePath);
|
|
6860
|
+
if (ap.manifest.source === "app-config" && !ap.manifest.pages.includes(pagePath)) {
|
|
6861
|
+
throw new Error(`[bridge-router] PAGE_OPEN rejected: page-not-found "${pagePath}" is not in the compiled manifest`);
|
|
6862
|
+
}
|
|
6575
6863
|
const bridgeId = opts.bridgeId || newBridgeId();
|
|
6576
6864
|
const windowConfig = resolvePageWindowConfig(ap.appConfig, pagePath);
|
|
6577
6865
|
const isTab = isTabPage(ap.appConfig, pagePath);
|
|
@@ -6631,15 +6919,18 @@ function handleNavCallback(state, sender, payload) {
|
|
|
6631
6919
|
}
|
|
6632
6920
|
async function bootServiceHost(state, ap, ctx) {
|
|
6633
6921
|
if (state.appSessions.get(ap.appSessionId) !== ap) return;
|
|
6922
|
+
ctx.consoleForwarder?.notifyServiceHostReady?.(ap.appSessionId);
|
|
6634
6923
|
ap.logicInjected = await injectLogicBundle(ap);
|
|
6635
6924
|
if (!ap.logicInjected) {
|
|
6636
|
-
reportLogicLoadFailure(ap, ctx);
|
|
6925
|
+
const reason = reportLogicLoadFailure(ap, ctx);
|
|
6926
|
+
clearLaunchTimer(ap);
|
|
6927
|
+
pushRuntimeStatus(ctx, ap, { phase: "launch-failed", code: "logic-bundle-unreachable", reason });
|
|
6637
6928
|
return;
|
|
6638
6929
|
}
|
|
6639
6930
|
const rootPage = ap.pages.get(ap.appSessionId);
|
|
6640
6931
|
const rootMissing = !!rootPage && !pageInManifest(ap, rootPage.pagePath);
|
|
6641
6932
|
if (rootMissing) {
|
|
6642
|
-
reportPageNotFound(
|
|
6933
|
+
reportPageNotFound(ctx, ap.appSessionId, rootPage.pagePath);
|
|
6643
6934
|
} else if (rootPage) {
|
|
6644
6935
|
forwardToService(ap, makeLoadResource(ap, rootPage, "service"));
|
|
6645
6936
|
}
|
|
@@ -6673,16 +6964,30 @@ async function injectLogicBundle(ap) {
|
|
|
6673
6964
|
}
|
|
6674
6965
|
function reportLogicLoadFailure(ap, ctx) {
|
|
6675
6966
|
const hint = ap.appId === "unknown" ? ' appId could not be resolved (it fell back to "unknown") \u2014 the mini-program likely failed to compile or its project manifest/app config is missing.' : "";
|
|
6676
|
-
const
|
|
6677
|
-
|
|
6967
|
+
const shortReason = `[dimina-kit] Failed to load the mini-program logic bundle from ${logicBundleUrl(ap)}.`;
|
|
6968
|
+
const message = `${shortReason} The service runtime has no registered modules, so no page can mount.` + hint + ` Verify the project compiled successfully and that the resource server serves "${ap.appId}/${ap.root}/".`;
|
|
6969
|
+
ctx.diagnostics?.report({
|
|
6970
|
+
severity: "error",
|
|
6971
|
+
code: "logic-bundle-unreachable",
|
|
6972
|
+
message,
|
|
6973
|
+
appSessionId: ap.appSessionId
|
|
6974
|
+
});
|
|
6678
6975
|
ctx.guestConsole?.emit({ source: "service", level: "error", args: [message] });
|
|
6976
|
+
return shortReason;
|
|
6679
6977
|
}
|
|
6680
6978
|
function pageInManifest(ap, pagePath) {
|
|
6979
|
+
if (ap.manifest.source !== "app-config") return true;
|
|
6681
6980
|
return ap.manifest.pages.includes(normalizePagePath(pagePath));
|
|
6682
6981
|
}
|
|
6683
|
-
function reportPageNotFound(
|
|
6684
|
-
const
|
|
6685
|
-
|
|
6982
|
+
function reportPageNotFound(ctx, appSessionId, pagePath, fallbackTo) {
|
|
6983
|
+
const base = `Page[${pagePath}] not found. May be caused by: 1. Forgetting to add page route in app.json. 2. Invoking Page() in async task.`;
|
|
6984
|
+
const message = fallbackTo ? `${base} Falling back to "${fallbackTo}".` : base;
|
|
6985
|
+
ctx.diagnostics?.report({
|
|
6986
|
+
severity: "error",
|
|
6987
|
+
code: "page-not-found",
|
|
6988
|
+
message,
|
|
6989
|
+
appSessionId
|
|
6990
|
+
});
|
|
6686
6991
|
ctx.guestConsole?.emit({ source: "service", level: "error", args: [message] });
|
|
6687
6992
|
}
|
|
6688
6993
|
function maybeSendResourceLoaded(ap, page) {
|
|
@@ -6739,6 +7044,11 @@ function routeFromRender(state, ap, page, msg, ctx) {
|
|
|
6739
7044
|
handleContainerMsg(ap, page, msg, ctx, state);
|
|
6740
7045
|
}
|
|
6741
7046
|
}
|
|
7047
|
+
function reportServiceUncaughtError(ctx, ap, body) {
|
|
7048
|
+
const severity = body.level === "error" ? "error" : body.level === "warn" ? "warn" : "info";
|
|
7049
|
+
const message = Array.isArray(body.args) ? body.args.map((a) => String(a)).join(" ") : String(body.args ?? "");
|
|
7050
|
+
ctx.diagnostics?.report({ severity, code: "service-uncaught-error", message, appSessionId: ap.appSessionId });
|
|
7051
|
+
}
|
|
6742
7052
|
function handleContainerMsg(ap, page, msg, ctx, state) {
|
|
6743
7053
|
switch (msg.type) {
|
|
6744
7054
|
case "serviceResourceLoaded":
|
|
@@ -6757,22 +7067,31 @@ function handleContainerMsg(ap, page, msg, ctx, state) {
|
|
|
6757
7067
|
ap.simulatorWc.send(SIMULATOR_EVENTS.DOM_READY, { bridgeId: page.bridgeId });
|
|
6758
7068
|
}
|
|
6759
7069
|
state.emitRenderEvent({ kind: "domReady", appId: ap.appId, bridgeId: page.bridgeId });
|
|
7070
|
+
markSessionRunning(ctx, ap, page);
|
|
6760
7071
|
break;
|
|
6761
7072
|
case "invokeAPI":
|
|
6762
7073
|
void handleSimulatorApi(state, ap, page, msg.body, ctx);
|
|
6763
7074
|
break;
|
|
6764
7075
|
case "serviceHostError": {
|
|
6765
|
-
console.warn("[bridge-router] service host error:", msg.body);
|
|
6766
7076
|
const errBody = msg.body;
|
|
6767
7077
|
const errArg = errBody?.message ?? msg.body;
|
|
7078
|
+
ctx.diagnostics?.report({
|
|
7079
|
+
severity: "error",
|
|
7080
|
+
code: "service-host-error",
|
|
7081
|
+
message: String(errArg),
|
|
7082
|
+
appSessionId: ap.appSessionId
|
|
7083
|
+
});
|
|
6768
7084
|
for (const id of state.appLifecycle.listeners(ap.appSessionId, "onError")) {
|
|
6769
7085
|
sendCallback(ap, id, errArg);
|
|
6770
7086
|
}
|
|
6771
7087
|
break;
|
|
6772
7088
|
}
|
|
6773
|
-
case "consoleLog":
|
|
7089
|
+
case "consoleLog": {
|
|
6774
7090
|
ctx.guestConsole?.emit(msg.body);
|
|
7091
|
+
const body = msg.body;
|
|
7092
|
+
if (body?.source === "service") reportServiceUncaughtError(ctx, ap, body);
|
|
6775
7093
|
break;
|
|
7094
|
+
}
|
|
6776
7095
|
case "storageChanged":
|
|
6777
7096
|
ctx.onServiceStorageChanged?.(ap.appId, msg.body);
|
|
6778
7097
|
break;
|
|
@@ -6841,99 +7160,136 @@ var APP_LIFECYCLE_UNREGISTER = {
|
|
|
6841
7160
|
offAppHide: "onAppHide",
|
|
6842
7161
|
offError: "onError"
|
|
6843
7162
|
};
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
if (NAV_BAR_API_NAMES.has(name)) {
|
|
6848
|
-
if (!ap.simulatorWc.isDestroyed()) {
|
|
6849
|
-
ap.simulatorWc.send(SIMULATOR_EVENTS.NAV_BAR, {
|
|
6850
|
-
bridgeId: page.bridgeId,
|
|
6851
|
-
name,
|
|
6852
|
-
params
|
|
6853
|
-
});
|
|
6854
|
-
}
|
|
6855
|
-
const successResult = { errMsg: `${name}:ok` };
|
|
6856
|
-
sendCallback(ap, params.success, successResult);
|
|
6857
|
-
sendCallback(ap, params.complete, successResult);
|
|
6858
|
-
return;
|
|
6859
|
-
}
|
|
6860
|
-
if (NAV_ACTION_NAMES.has(name)) {
|
|
6861
|
-
const payload = {
|
|
6862
|
-
appSessionId: ap.appSessionId,
|
|
7163
|
+
function handleNavBarApi(ap, page, name, params) {
|
|
7164
|
+
if (!ap.simulatorWc.isDestroyed()) {
|
|
7165
|
+
ap.simulatorWc.send(SIMULATOR_EVENTS.NAV_BAR, {
|
|
6863
7166
|
bridgeId: page.bridgeId,
|
|
6864
7167
|
name,
|
|
6865
|
-
params
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
7168
|
+
params
|
|
7169
|
+
});
|
|
7170
|
+
}
|
|
7171
|
+
const successResult = { errMsg: `${name}:ok` };
|
|
7172
|
+
sendCallback(ap, params.success, successResult);
|
|
7173
|
+
sendCallback(ap, params.complete, successResult);
|
|
7174
|
+
}
|
|
7175
|
+
function failActionCallback(ap, params, errMsg) {
|
|
7176
|
+
const fail = { errMsg };
|
|
7177
|
+
sendCallback(ap, params.fail, fail);
|
|
7178
|
+
sendCallback(ap, params.complete, fail);
|
|
7179
|
+
}
|
|
7180
|
+
function sendActionOrFail(ap, eventName, payload, name, params) {
|
|
7181
|
+
if (!ap.simulatorWc.isDestroyed()) {
|
|
7182
|
+
ap.simulatorWc.send(eventName, payload);
|
|
6875
7183
|
return;
|
|
6876
7184
|
}
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
}
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
7185
|
+
failActionCallback(ap, params, `${name}:fail simulator window destroyed`);
|
|
7186
|
+
}
|
|
7187
|
+
function extractNavTargetPagePath(params) {
|
|
7188
|
+
const raw = params.url;
|
|
7189
|
+
if (typeof raw !== "string" || !raw) return void 0;
|
|
7190
|
+
return normalizePagePath(raw.split("?")[0] ?? raw);
|
|
7191
|
+
}
|
|
7192
|
+
function checkNavTarget(ap, name, targetPagePath) {
|
|
7193
|
+
if (ap.manifest.source !== "app-config") return { ok: true };
|
|
7194
|
+
if (!ap.manifest.pages.includes(targetPagePath)) {
|
|
7195
|
+
return { ok: false, errMsg: `${name}:fail page "${targetPagePath}" is not found`, reportNotFound: true };
|
|
7196
|
+
}
|
|
7197
|
+
if (name === "switchTab") {
|
|
7198
|
+
const inTabBar = ap.manifest.tabBar?.list.some((item) => normalizePagePath(item.pagePath) === targetPagePath) ?? false;
|
|
7199
|
+
if (!inTabBar) return { ok: false, errMsg: "switchTab:fail can not switch to no-tabBar page" };
|
|
7200
|
+
}
|
|
7201
|
+
return { ok: true };
|
|
7202
|
+
}
|
|
7203
|
+
function handleNavActionApi(ap, ctx, page, name, params) {
|
|
7204
|
+
const targetPagePath = extractNavTargetPagePath(params);
|
|
7205
|
+
if (targetPagePath !== void 0) {
|
|
7206
|
+
const verdict = checkNavTarget(ap, name, targetPagePath);
|
|
7207
|
+
if (!verdict.ok) {
|
|
7208
|
+
if (verdict.reportNotFound) reportPageNotFound(ctx, ap.appSessionId, targetPagePath);
|
|
7209
|
+
failActionCallback(ap, params, verdict.errMsg);
|
|
7210
|
+
return;
|
|
6891
7211
|
}
|
|
6892
|
-
return;
|
|
6893
7212
|
}
|
|
7213
|
+
const payload = {
|
|
7214
|
+
appSessionId: ap.appSessionId,
|
|
7215
|
+
bridgeId: page.bridgeId,
|
|
7216
|
+
name,
|
|
7217
|
+
params,
|
|
7218
|
+
callbacks: extractCallbacks(params)
|
|
7219
|
+
};
|
|
7220
|
+
sendActionOrFail(ap, SIMULATOR_EVENTS.NAV_ACTION, payload, name, params);
|
|
7221
|
+
}
|
|
7222
|
+
function handleTabActionApi(ap, page, name, params) {
|
|
7223
|
+
const payload = {
|
|
7224
|
+
appSessionId: ap.appSessionId,
|
|
7225
|
+
bridgeId: page.bridgeId,
|
|
7226
|
+
name,
|
|
7227
|
+
params,
|
|
7228
|
+
callbacks: extractCallbacks(params)
|
|
7229
|
+
};
|
|
7230
|
+
sendActionOrFail(ap, SIMULATOR_EVENTS.TAB_ACTION, payload, name, params);
|
|
7231
|
+
}
|
|
7232
|
+
function handleAppLifecycleToggle(state, ap, name, params) {
|
|
6894
7233
|
const lifecycleRegister = APP_LIFECYCLE_REGISTER[name];
|
|
6895
7234
|
if (lifecycleRegister) {
|
|
6896
7235
|
state.appLifecycle.register(ap.appSessionId, lifecycleRegister, params.success);
|
|
6897
|
-
return;
|
|
7236
|
+
return true;
|
|
6898
7237
|
}
|
|
6899
7238
|
const lifecycleUnregister = APP_LIFECYCLE_UNREGISTER[name];
|
|
6900
7239
|
if (lifecycleUnregister) {
|
|
6901
7240
|
state.appLifecycle.unregister(ap.appSessionId, lifecycleUnregister, params.success);
|
|
7241
|
+
return true;
|
|
7242
|
+
}
|
|
7243
|
+
return false;
|
|
7244
|
+
}
|
|
7245
|
+
function handlePageScrollApi(ap, page, params) {
|
|
7246
|
+
const renderWc = page.renderWc;
|
|
7247
|
+
if (renderWc && !renderWc.isDestroyed()) {
|
|
7248
|
+
void renderWc.executeJavaScript(buildPageScrollScript(params)).catch(() => {
|
|
7249
|
+
});
|
|
7250
|
+
}
|
|
7251
|
+
const successResult = { errMsg: "pageScrollTo:ok" };
|
|
7252
|
+
sendCallback(ap, params.success, successResult);
|
|
7253
|
+
sendCallback(ap, params.complete, successResult);
|
|
7254
|
+
}
|
|
7255
|
+
async function invokeSimulatorApiAndCallback(ap, name, params, invoke) {
|
|
7256
|
+
try {
|
|
7257
|
+
const result = await invoke();
|
|
7258
|
+
sendCallback(ap, params.success, result);
|
|
7259
|
+
sendCallback(ap, params.complete, result);
|
|
7260
|
+
} catch (error) {
|
|
7261
|
+
const failResult = { errMsg: `${name}:fail ${error instanceof Error ? error.message : String(error)}` };
|
|
7262
|
+
sendCallback(ap, params.fail, failResult);
|
|
7263
|
+
sendCallback(ap, params.complete, failResult);
|
|
7264
|
+
}
|
|
7265
|
+
}
|
|
7266
|
+
async function handleSimulatorApi(state, ap, page, body, ctx) {
|
|
7267
|
+
const name = String(body.name ?? "");
|
|
7268
|
+
const params = normalizeParams(body.params);
|
|
7269
|
+
if (NAV_BAR_API_NAMES.has(name)) {
|
|
7270
|
+
handleNavBarApi(ap, page, name, params);
|
|
6902
7271
|
return;
|
|
6903
7272
|
}
|
|
7273
|
+
if (NAV_ACTION_NAMES.has(name)) {
|
|
7274
|
+
handleNavActionApi(ap, ctx, page, name, params);
|
|
7275
|
+
return;
|
|
7276
|
+
}
|
|
7277
|
+
if (TAB_ACTION_NAMES.has(name)) {
|
|
7278
|
+
handleTabActionApi(ap, page, name, params);
|
|
7279
|
+
return;
|
|
7280
|
+
}
|
|
7281
|
+
if (handleAppLifecycleToggle(state, ap, name, params)) return;
|
|
6904
7282
|
if (name === "pageScrollTo") {
|
|
6905
|
-
|
|
6906
|
-
if (renderWc && !renderWc.isDestroyed()) {
|
|
6907
|
-
void renderWc.executeJavaScript(buildPageScrollScript(params)).catch(() => {
|
|
6908
|
-
});
|
|
6909
|
-
}
|
|
6910
|
-
const successResult = { errMsg: "pageScrollTo:ok" };
|
|
6911
|
-
sendCallback(ap, params.success, successResult);
|
|
6912
|
-
sendCallback(ap, params.complete, successResult);
|
|
7283
|
+
handlePageScrollApi(ap, page, params);
|
|
6913
7284
|
return;
|
|
6914
7285
|
}
|
|
6915
7286
|
if (ctx.storageApi && STORAGE_API_NAMES.has(name)) {
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
sendCallback(ap, params.success, result);
|
|
6919
|
-
sendCallback(ap, params.complete, result);
|
|
6920
|
-
} catch (error) {
|
|
6921
|
-
const failResult = { errMsg: `${name}:fail ${error instanceof Error ? error.message : String(error)}` };
|
|
6922
|
-
sendCallback(ap, params.fail, failResult);
|
|
6923
|
-
sendCallback(ap, params.complete, failResult);
|
|
6924
|
-
}
|
|
7287
|
+
const storageApi = ctx.storageApi;
|
|
7288
|
+
await invokeSimulatorApiAndCallback(ap, name, params, () => storageApi.invoke(ap.appId, name, params));
|
|
6925
7289
|
return;
|
|
6926
7290
|
}
|
|
6927
7291
|
if (ctx.simulatorApis.has(name)) {
|
|
6928
|
-
|
|
6929
|
-
const result = await ctx.simulatorApis.invoke(name, params);
|
|
6930
|
-
sendCallback(ap, params.success, result);
|
|
6931
|
-
sendCallback(ap, params.complete, result);
|
|
6932
|
-
} catch (error) {
|
|
6933
|
-
const failResult = { errMsg: `${name}:fail ${error instanceof Error ? error.message : String(error)}` };
|
|
6934
|
-
sendCallback(ap, params.fail, failResult);
|
|
6935
|
-
sendCallback(ap, params.complete, failResult);
|
|
6936
|
-
}
|
|
7292
|
+
await invokeSimulatorApiAndCallback(ap, name, params, () => ctx.simulatorApis.invoke(name, params));
|
|
6937
7293
|
return;
|
|
6938
7294
|
}
|
|
6939
7295
|
forwardApiCallToSimulator(state, ap, page, name, params);
|
|
@@ -6957,7 +7313,7 @@ function forwardApiCallToSimulator(state, ap, page, name, params) {
|
|
|
6957
7313
|
const fail = { errMsg: `${pending.name}:fail no handler (timeout)` };
|
|
6958
7314
|
sendCallback(target, pending.callbacks.fail, fail);
|
|
6959
7315
|
sendCallback(target, pending.callbacks.complete, fail);
|
|
6960
|
-
},
|
|
7316
|
+
}, apiCallWatchdogMs(name, params));
|
|
6961
7317
|
state.pendingApiCalls.set(requestId, {
|
|
6962
7318
|
appSessionId: ap.appSessionId,
|
|
6963
7319
|
callbacks,
|
|
@@ -7006,7 +7362,12 @@ function handleApiResponse(state, sender, payload) {
|
|
|
7006
7362
|
sendCallback(ap, pending.callbacks.success, payload.result);
|
|
7007
7363
|
sendCallback(ap, pending.callbacks.complete, payload.result ?? { errMsg: `${pending.name}:ok` });
|
|
7008
7364
|
} else {
|
|
7009
|
-
const
|
|
7365
|
+
const result = payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? payload.result : void 0;
|
|
7366
|
+
const resultErrMsg = typeof result?.errMsg === "string" ? result.errMsg : void 0;
|
|
7367
|
+
const fail = {
|
|
7368
|
+
...result,
|
|
7369
|
+
errMsg: payload.errMsg || resultErrMsg || `${pending.name}:fail`
|
|
7370
|
+
};
|
|
7010
7371
|
sendCallback(ap, pending.callbacks.fail, fail);
|
|
7011
7372
|
sendCallback(ap, pending.callbacks.complete, fail);
|
|
7012
7373
|
}
|
|
@@ -7152,20 +7513,14 @@ function disposePageSession(state, ap, page) {
|
|
|
7152
7513
|
ap.pages.delete(page.bridgeId);
|
|
7153
7514
|
state.pageSessions.delete(page.bridgeId);
|
|
7154
7515
|
}
|
|
7155
|
-
|
|
7156
|
-
const ap = state.appSessions.get(appSessionId);
|
|
7157
|
-
if (!ap) return;
|
|
7158
|
-
state.appSessions.delete(appSessionId);
|
|
7159
|
-
const registryHandle = ap.registryHandle;
|
|
7160
|
-
ap.registryHandle = null;
|
|
7161
|
-
void registryHandle?.dispose();
|
|
7162
|
-
state.appLifecycle.dispose(appSessionId);
|
|
7163
|
-
state.evictAppDataBridges(ap);
|
|
7516
|
+
function drainPendingApiCallsForSession(state, appSessionId) {
|
|
7164
7517
|
for (const [requestId, pending] of state.pendingApiCalls) {
|
|
7165
7518
|
if (pending.appSessionId !== appSessionId) continue;
|
|
7166
7519
|
clearTimeout(pending.timer);
|
|
7167
7520
|
state.pendingApiCalls.delete(requestId);
|
|
7168
7521
|
}
|
|
7522
|
+
}
|
|
7523
|
+
function closeSessionPages(state, ap) {
|
|
7169
7524
|
for (const page of ap.pages.values()) {
|
|
7170
7525
|
if (page.renderWc && !page.renderWc.isDestroyed()) {
|
|
7171
7526
|
state.wcIdToBridgeId.delete(page.renderWc.id);
|
|
@@ -7176,8 +7531,8 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
|
|
|
7176
7531
|
}
|
|
7177
7532
|
state.pageSessions.delete(page.bridgeId);
|
|
7178
7533
|
}
|
|
7179
|
-
|
|
7180
|
-
|
|
7534
|
+
}
|
|
7535
|
+
async function releaseServiceWindow(state, ap, opts) {
|
|
7181
7536
|
if (ap.poolEntryId !== null && state.pool && !opts.serviceAlreadyClosed) {
|
|
7182
7537
|
state.connections.reset(ap.serviceWc.id);
|
|
7183
7538
|
if (ap.onServiceBoot && !ap.serviceWindow.isDestroyed()) {
|
|
@@ -7191,6 +7546,8 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
|
|
|
7191
7546
|
} else if (!opts.serviceAlreadyClosed && !ap.serviceWindow.isDestroyed()) {
|
|
7192
7547
|
ap.serviceWindow.close();
|
|
7193
7548
|
}
|
|
7549
|
+
}
|
|
7550
|
+
function unbindSessionFromSharedMaps(state, ap, appSessionId) {
|
|
7194
7551
|
if (state.serviceWcIdToAppSessionId.get(ap.serviceWc.id) === appSessionId) {
|
|
7195
7552
|
state.serviceWcIdToAppSessionId.delete(ap.serviceWc.id);
|
|
7196
7553
|
}
|
|
@@ -7199,31 +7556,68 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
|
|
|
7199
7556
|
hosted.delete(appSessionId);
|
|
7200
7557
|
if (hosted.size === 0) state.simulatorWcIdToAppSessionIds.delete(ap.simulatorWc.id);
|
|
7201
7558
|
}
|
|
7559
|
+
}
|
|
7560
|
+
async function disposeAppSession(state, appSessionId, opts = {}) {
|
|
7561
|
+
const ap = state.appSessions.get(appSessionId);
|
|
7562
|
+
if (!ap) return;
|
|
7563
|
+
state.appSessions.delete(appSessionId);
|
|
7564
|
+
clearLaunchTimer(ap);
|
|
7565
|
+
const registryHandle = ap.registryHandle;
|
|
7566
|
+
ap.registryHandle = null;
|
|
7567
|
+
void registryHandle?.dispose();
|
|
7568
|
+
state.appLifecycle.dispose(appSessionId);
|
|
7569
|
+
state.evictAppDataBridges(ap);
|
|
7570
|
+
drainPendingApiCallsForSession(state, appSessionId);
|
|
7571
|
+
closeSessionPages(state, ap);
|
|
7572
|
+
ap.pages.clear();
|
|
7573
|
+
ap.listenerBag.dispose();
|
|
7574
|
+
await releaseServiceWindow(state, ap, opts);
|
|
7575
|
+
unbindSessionFromSharedMaps(state, ap, appSessionId);
|
|
7202
7576
|
if (ap.resourceServer) {
|
|
7203
7577
|
await ap.resourceServer.close().catch((error) => {
|
|
7204
7578
|
console.warn("[bridge-router] resource server close failed:", error);
|
|
7205
7579
|
});
|
|
7206
7580
|
}
|
|
7207
7581
|
}
|
|
7208
|
-
async function loadAppConfig(resourceBase) {
|
|
7582
|
+
async function loadAppConfig(resourceBase, onUnreachable) {
|
|
7209
7583
|
const cfgUrl = new URL("app-config.json", resourceBase.endsWith("/") ? resourceBase : `${resourceBase}/`).toString();
|
|
7210
7584
|
try {
|
|
7211
7585
|
const res = await fetch(cfgUrl);
|
|
7212
7586
|
if (!res.ok) {
|
|
7587
|
+
const error = new Error(`app-config.json fetch ${res.status} at ${cfgUrl}`);
|
|
7213
7588
|
console.warn(`[bridge-router] no app-config.json at ${cfgUrl} (${res.status})`);
|
|
7589
|
+
onUnreachable?.({ url: cfgUrl, error });
|
|
7214
7590
|
return {};
|
|
7215
7591
|
}
|
|
7216
7592
|
return await res.json();
|
|
7217
7593
|
} catch (error) {
|
|
7218
7594
|
console.warn("[bridge-router] failed to fetch/parse app-config.json:", error);
|
|
7595
|
+
onUnreachable?.({ url: cfgUrl, error });
|
|
7219
7596
|
return {};
|
|
7220
7597
|
}
|
|
7221
7598
|
}
|
|
7222
7599
|
function buildAppManifest(appConfig, fallbackEntry) {
|
|
7223
7600
|
const entry = appConfig.app?.entryPagePath || fallbackEntry;
|
|
7224
|
-
const
|
|
7601
|
+
const hasCompiledPages = !!appConfig.app?.pages?.length;
|
|
7602
|
+
const pages = hasCompiledPages ? appConfig.app.pages : [entry];
|
|
7225
7603
|
const tabBar = appConfig.app?.tabBar && Array.isArray(appConfig.app.tabBar.list) && appConfig.app.tabBar.list.length > 0 ? appConfig.app.tabBar : void 0;
|
|
7226
|
-
return {
|
|
7604
|
+
return {
|
|
7605
|
+
entryPagePath: normalizePagePath(entry),
|
|
7606
|
+
pages: pages.map(normalizePagePath),
|
|
7607
|
+
tabBar,
|
|
7608
|
+
// 'app-config' means `pages` above is the real compiled list mount gates
|
|
7609
|
+
// can trust; 'fallback' means app-config.json was unreachable and `pages`
|
|
7610
|
+
// is just the single requested page — nothing to validate membership
|
|
7611
|
+
// against, so every gate keyed on this source lets it through unchanged.
|
|
7612
|
+
source: hasCompiledPages ? "app-config" : "fallback"
|
|
7613
|
+
};
|
|
7614
|
+
}
|
|
7615
|
+
function resolveRootPagePath(manifest, requestedPagePath) {
|
|
7616
|
+
if (manifest.source !== "app-config" || manifest.pages.includes(requestedPagePath)) {
|
|
7617
|
+
return { resolvedPagePath: requestedPagePath, pageFallbackApplied: false };
|
|
7618
|
+
}
|
|
7619
|
+
const resolvedPagePath = manifest.pages.includes(manifest.entryPagePath) ? manifest.entryPagePath : manifest.pages[0];
|
|
7620
|
+
return { resolvedPagePath, pageFallbackApplied: true };
|
|
7227
7621
|
}
|
|
7228
7622
|
function resolvePageWindowConfig(appConfig, pagePath) {
|
|
7229
7623
|
const normalized = normalizePagePath(pagePath);
|
|
@@ -7513,89 +7907,98 @@ appHandlers["App.getPageStack"] = async (ctx) => {
|
|
|
7513
7907
|
}
|
|
7514
7908
|
return { pageStack };
|
|
7515
7909
|
};
|
|
7516
|
-
|
|
7517
|
-
const
|
|
7518
|
-
const
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7910
|
+
async function runNativeHostNav(bridge, serviceWc, method, args) {
|
|
7911
|
+
const arg = method === "navigateBack" ? args[0] ?? { delta: 1 } : args[0] ?? {};
|
|
7912
|
+
const since = bridge.getActiveBridgeId();
|
|
7913
|
+
await serviceWc.executeJavaScript(`wx.${method}(${JSON.stringify(arg)})`);
|
|
7914
|
+
const timeoutMs = method === "navigateBack" ? 1500 : 2e3;
|
|
7915
|
+
await waitForActivePage(bridge, {
|
|
7916
|
+
since,
|
|
7917
|
+
timeoutMs,
|
|
7918
|
+
onTimeout: () => console.warn(`[automation] ${method}: activePage signal not seen within ${timeoutMs}ms \u2014 proceeding on timeout floor`)
|
|
7919
|
+
});
|
|
7920
|
+
return { result: void 0 };
|
|
7921
|
+
}
|
|
7922
|
+
async function runNativeHostGeneric(serviceWc, method, args) {
|
|
7923
|
+
const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
|
|
7924
|
+
const result = await serviceWc.executeJavaScript(`
|
|
7925
|
+
new Promise((resolve, reject) => {
|
|
7926
|
+
try {
|
|
7927
|
+
if (typeof wx === 'undefined' || typeof wx[${JSON.stringify(method)}] !== 'function') {
|
|
7928
|
+
reject(new Error('wx.' + ${JSON.stringify(method)} + ' is not a function in service host'))
|
|
7929
|
+
return
|
|
7930
|
+
}
|
|
7931
|
+
resolve(wx.${method}(${argsStr}))
|
|
7932
|
+
} catch (e) { reject(e && e.message ? e.message : String(e)) }
|
|
7933
|
+
})
|
|
7934
|
+
`);
|
|
7935
|
+
return { result };
|
|
7936
|
+
}
|
|
7937
|
+
async function runNativeHostWxMethod(bridge, method, args) {
|
|
7938
|
+
const serviceWc = bridge.getServiceWc();
|
|
7939
|
+
if (!serviceWc) throw new Error("Service host not connected");
|
|
7940
|
+
if (NAV_METHODS.has(method) || method === "navigateBack") {
|
|
7941
|
+
return runNativeHostNav(bridge, serviceWc, method, args);
|
|
7942
|
+
}
|
|
7943
|
+
return runNativeHostGeneric(serviceWc, method, args);
|
|
7944
|
+
}
|
|
7945
|
+
async function clickNavTarget(ctx, cleanUrl) {
|
|
7946
|
+
return evalInSim(ctx, `(() => {
|
|
7947
|
+
const iframes = Array.from(document.querySelectorAll('iframe'))
|
|
7948
|
+
const visible = iframes.filter((f) => {
|
|
7949
|
+
const cs = window.getComputedStyle(f)
|
|
7950
|
+
if (cs.display === 'none' || cs.visibility === 'hidden') return false
|
|
7951
|
+
const rect = f.getBoundingClientRect()
|
|
7952
|
+
return rect.width > 0 && rect.height > 0
|
|
7953
|
+
})
|
|
7954
|
+
const target = visible[visible.length - 1] || iframes[iframes.length - 1]
|
|
7955
|
+
if (!target || !target.contentDocument) return false
|
|
7956
|
+
const path = ${JSON.stringify(cleanUrl)}
|
|
7957
|
+
const el = Array.from(target.contentDocument.querySelectorAll('[data-path]'))
|
|
7958
|
+
.find((e) => e.getAttribute('data-path') === path)
|
|
7959
|
+
if (el) { el.click(); return true }
|
|
7960
|
+
return false
|
|
7961
|
+
})()`).catch(() => false);
|
|
7962
|
+
}
|
|
7963
|
+
async function invokeNavViaWx(ctx, method, cleanUrl) {
|
|
7964
|
+
const apiName = JSON.stringify(method);
|
|
7965
|
+
const urlJson = JSON.stringify(cleanUrl);
|
|
7966
|
+
await evalInSim(
|
|
7967
|
+
ctx,
|
|
7968
|
+
`(() => {
|
|
7969
|
+
const iframes = Array.from(document.querySelectorAll('iframe'))
|
|
7970
|
+
for (const f of iframes) {
|
|
7537
7971
|
try {
|
|
7538
|
-
if (
|
|
7539
|
-
|
|
7972
|
+
if (f.contentWindow && f.contentWindow.wx && typeof f.contentWindow.wx[${apiName}] === 'function') {
|
|
7973
|
+
f.contentWindow.wx[${apiName}]({ url: ${urlJson} })
|
|
7540
7974
|
return
|
|
7541
7975
|
}
|
|
7542
|
-
|
|
7543
|
-
} catch (e) { reject(e && e.message ? e.message : String(e)) }
|
|
7544
|
-
})
|
|
7545
|
-
`);
|
|
7546
|
-
return { result: result2 };
|
|
7547
|
-
}
|
|
7548
|
-
if (["navigateTo", "redirectTo", "reLaunch", "switchTab"].includes(method)) {
|
|
7549
|
-
const opts = args[0];
|
|
7550
|
-
const url = opts?.url;
|
|
7551
|
-
if (url) {
|
|
7552
|
-
const cleanUrl = url.startsWith("/") ? url : `/${url}`;
|
|
7553
|
-
const clicked = await evalInSim(ctx, `(() => {
|
|
7554
|
-
const iframes = Array.from(document.querySelectorAll('iframe'))
|
|
7555
|
-
const visible = iframes.filter((f) => {
|
|
7556
|
-
const cs = window.getComputedStyle(f)
|
|
7557
|
-
if (cs.display === 'none' || cs.visibility === 'hidden') return false
|
|
7558
|
-
const rect = f.getBoundingClientRect()
|
|
7559
|
-
return rect.width > 0 && rect.height > 0
|
|
7560
|
-
})
|
|
7561
|
-
const target = visible[visible.length - 1] || iframes[iframes.length - 1]
|
|
7562
|
-
if (!target || !target.contentDocument) return false
|
|
7563
|
-
const path = ${JSON.stringify(cleanUrl)}
|
|
7564
|
-
const el = Array.from(target.contentDocument.querySelectorAll('[data-path]'))
|
|
7565
|
-
.find((e) => e.getAttribute('data-path') === path)
|
|
7566
|
-
if (el) { el.click(); return true }
|
|
7567
|
-
return false
|
|
7568
|
-
})()`).catch(() => false);
|
|
7569
|
-
if (!clicked) {
|
|
7570
|
-
const apiName = JSON.stringify(method);
|
|
7571
|
-
const urlJson = JSON.stringify(cleanUrl);
|
|
7572
|
-
await evalInSim(
|
|
7573
|
-
ctx,
|
|
7574
|
-
`(() => {
|
|
7575
|
-
const iframes = Array.from(document.querySelectorAll('iframe'))
|
|
7576
|
-
for (const f of iframes) {
|
|
7577
|
-
try {
|
|
7578
|
-
if (f.contentWindow && f.contentWindow.wx && typeof f.contentWindow.wx[${apiName}] === 'function') {
|
|
7579
|
-
f.contentWindow.wx[${apiName}]({ url: ${urlJson} })
|
|
7580
|
-
return
|
|
7581
|
-
}
|
|
7582
|
-
} catch (_) {}
|
|
7583
|
-
}
|
|
7584
|
-
if (typeof wx !== 'undefined' && wx && typeof wx[${apiName}] === 'function') {
|
|
7585
|
-
wx[${apiName}]({ url: ${urlJson} })
|
|
7586
|
-
}
|
|
7587
|
-
})()`
|
|
7588
|
-
);
|
|
7976
|
+
} catch (_) {}
|
|
7589
7977
|
}
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7978
|
+
if (typeof wx !== 'undefined' && wx && typeof wx[${apiName}] === 'function') {
|
|
7979
|
+
wx[${apiName}]({ url: ${urlJson} })
|
|
7980
|
+
}
|
|
7981
|
+
})()`
|
|
7982
|
+
);
|
|
7983
|
+
}
|
|
7984
|
+
async function runSimulatorNav(ctx, method, args) {
|
|
7985
|
+
const opts = args[0];
|
|
7986
|
+
const url = opts?.url;
|
|
7987
|
+
if (!url) return { result: void 0 };
|
|
7988
|
+
const cleanUrl = url.startsWith("/") ? url : `/${url}`;
|
|
7989
|
+
const clicked = await clickNavTarget(ctx, cleanUrl);
|
|
7990
|
+
if (!clicked) {
|
|
7991
|
+
await invokeNavViaWx(ctx, method, cleanUrl);
|
|
7598
7992
|
}
|
|
7993
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
7994
|
+
return { result: void 0 };
|
|
7995
|
+
}
|
|
7996
|
+
async function runSimulatorNavigateBack(ctx) {
|
|
7997
|
+
await evalInSim(ctx, "history.back()");
|
|
7998
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
7999
|
+
return { result: void 0 };
|
|
8000
|
+
}
|
|
8001
|
+
async function runSimulatorGeneric(ctx, method, args) {
|
|
7599
8002
|
const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
|
|
7600
8003
|
const result = await evalInSim(ctx, `
|
|
7601
8004
|
new Promise((resolve, reject) => {
|
|
@@ -7608,6 +8011,21 @@ appHandlers["App.callWxMethod"] = async (ctx, params) => {
|
|
|
7608
8011
|
})
|
|
7609
8012
|
`);
|
|
7610
8013
|
return { result };
|
|
8014
|
+
}
|
|
8015
|
+
appHandlers["App.callWxMethod"] = async (ctx, params) => {
|
|
8016
|
+
const method = params.method;
|
|
8017
|
+
const args = params.args || [];
|
|
8018
|
+
const bridge = ctx.bridge;
|
|
8019
|
+
if (bridge?.isNativeHost()) {
|
|
8020
|
+
return runNativeHostWxMethod(bridge, method, args);
|
|
8021
|
+
}
|
|
8022
|
+
if (NAV_METHODS.has(method)) {
|
|
8023
|
+
return runSimulatorNav(ctx, method, args);
|
|
8024
|
+
}
|
|
8025
|
+
if (method === "navigateBack") {
|
|
8026
|
+
return runSimulatorNavigateBack(ctx);
|
|
8027
|
+
}
|
|
8028
|
+
return runSimulatorGeneric(ctx, method, args);
|
|
7611
8029
|
};
|
|
7612
8030
|
appHandlers["App.callFunction"] = async (ctx, params) => {
|
|
7613
8031
|
const fnDecl = params.functionDeclaration;
|
|
@@ -7870,7 +8288,7 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
7870
8288
|
const clients = /* @__PURE__ */ new Set();
|
|
7871
8289
|
await new Promise((resolve, reject) => {
|
|
7872
8290
|
wss.once("listening", () => resolve());
|
|
7873
|
-
wss.once("error", (
|
|
8291
|
+
wss.once("error", (err2) => reject(err2));
|
|
7874
8292
|
});
|
|
7875
8293
|
const addr = wss.address();
|
|
7876
8294
|
const resolvedPort = typeof addr === "object" && addr ? addr.port : port;
|
|
@@ -7975,8 +8393,8 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
7975
8393
|
try {
|
|
7976
8394
|
const result = await handler(ctx, params);
|
|
7977
8395
|
response = { id, result };
|
|
7978
|
-
} catch (
|
|
7979
|
-
response = { id, error: { message:
|
|
8396
|
+
} catch (err2) {
|
|
8397
|
+
response = { id, error: { message: err2.message || String(err2) } };
|
|
7980
8398
|
}
|
|
7981
8399
|
}
|
|
7982
8400
|
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(response));
|
|
@@ -8513,6 +8931,96 @@ function registerContextTools(server) {
|
|
|
8513
8931
|
|
|
8514
8932
|
// src/main/services/mcp/tools/simulator-tools.ts
|
|
8515
8933
|
import { z as z5 } from "zod";
|
|
8934
|
+
function ok(text) {
|
|
8935
|
+
return { content: [{ type: "text", text }] };
|
|
8936
|
+
}
|
|
8937
|
+
function err(msg) {
|
|
8938
|
+
return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
|
|
8939
|
+
}
|
|
8940
|
+
async function clickAt(c, x, y) {
|
|
8941
|
+
await c.Input.dispatchMouseEvent({ type: "mousePressed", x, y, button: "left", clickCount: 1 });
|
|
8942
|
+
await c.Input.dispatchMouseEvent({ type: "mouseReleased", x, y, button: "left", clickCount: 1 });
|
|
8943
|
+
}
|
|
8944
|
+
async function tapCoord(c, x, y) {
|
|
8945
|
+
if (typeof x !== "number" || typeof y !== "number") return err("tap_coord requires x and y");
|
|
8946
|
+
await clickAt(c, x, y);
|
|
8947
|
+
return ok(`Tapped at (${x}, ${y})`);
|
|
8948
|
+
}
|
|
8949
|
+
function buildTapSelectorExpression(selector, index) {
|
|
8950
|
+
return `(() => {
|
|
8951
|
+
const payload = ${JSON.stringify({ selector, index })}
|
|
8952
|
+
try {
|
|
8953
|
+
const matches = Array.from(document.querySelectorAll(payload.selector))
|
|
8954
|
+
if (matches.length === 0) {
|
|
8955
|
+
return { ok: false, reason: 'no_match', message: \`selector matched no elements: \${payload.selector}\` }
|
|
8956
|
+
}
|
|
8957
|
+
if (payload.index < 0 || payload.index >= matches.length) {
|
|
8958
|
+
return { ok: false, reason: 'out_of_range', message: \`nth \${payload.index} out of range (matches: \${matches.length})\` }
|
|
8959
|
+
}
|
|
8960
|
+
const element = matches[payload.index]
|
|
8961
|
+
element.scrollIntoView({ block: 'center', inline: 'center' })
|
|
8962
|
+
const rect = element.getBoundingClientRect()
|
|
8963
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
8964
|
+
return { ok: false, reason: 'not_visible', message: \`selector matched an element, but it is not visible or rendered (zero rect): \${payload.selector}[\${payload.index}]\` }
|
|
8965
|
+
}
|
|
8966
|
+
return {
|
|
8967
|
+
ok: true,
|
|
8968
|
+
selector: payload.selector,
|
|
8969
|
+
index: payload.index,
|
|
8970
|
+
x: rect.left + rect.width / 2,
|
|
8971
|
+
y: rect.top + rect.height / 2,
|
|
8972
|
+
rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height },
|
|
8973
|
+
}
|
|
8974
|
+
} catch (error) {
|
|
8975
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
8976
|
+
return { ok: false, reason: 'selector_error', message: \`invalid selector: \${payload.selector} (\${message})\` }
|
|
8977
|
+
}
|
|
8978
|
+
})()`;
|
|
8979
|
+
}
|
|
8980
|
+
async function tapSelector(c, selector, nth) {
|
|
8981
|
+
if (!selector) return err("tap_selector requires selector");
|
|
8982
|
+
const index = nth ?? 0;
|
|
8983
|
+
const expression = buildTapSelectorExpression(selector, index);
|
|
8984
|
+
const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
|
|
8985
|
+
const value = result.result?.value;
|
|
8986
|
+
if (!value) return err(`selector evaluation returned no result: ${selector}[${index}]`);
|
|
8987
|
+
if (!value.ok) return err(value.message);
|
|
8988
|
+
await clickAt(c, value.x, value.y);
|
|
8989
|
+
return ok(`Tapped selector ${value.selector}[${value.index}] at (${value.x}, ${value.y}) within rect (${value.rect.left}, ${value.rect.top}, ${value.rect.width} x ${value.rect.height})`);
|
|
8990
|
+
}
|
|
8991
|
+
async function typeText(c, selector, nth, text) {
|
|
8992
|
+
if (!selector || typeof text !== "string") return err("type requires selector and text");
|
|
8993
|
+
const { root } = await c.DOM.getDocument({ depth: 0 });
|
|
8994
|
+
const { nodeIds } = await c.DOM.querySelectorAll({ nodeId: root.nodeId, selector });
|
|
8995
|
+
if (!nodeIds || nodeIds.length === 0) return err(`selector matched no elements: ${selector}`);
|
|
8996
|
+
const index = nth ?? 0;
|
|
8997
|
+
if (index < 0 || index >= nodeIds.length) return err(`nth ${index} out of range (matches: ${nodeIds.length})`);
|
|
8998
|
+
await c.DOM.focus({ nodeId: nodeIds[index] });
|
|
8999
|
+
try {
|
|
9000
|
+
await c.Input.insertText({ text });
|
|
9001
|
+
} catch {
|
|
9002
|
+
for (const ch of text) {
|
|
9003
|
+
await c.Input.dispatchKeyEvent({ type: "char", text: ch });
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
9006
|
+
return ok(`Typed ${text.length} char(s) into ${selector}[${index}]`);
|
|
9007
|
+
}
|
|
9008
|
+
async function scrollAt(c, x, y, deltaX, deltaY) {
|
|
9009
|
+
if (typeof x !== "number" || typeof y !== "number" || typeof deltaX !== "number" || typeof deltaY !== "number") {
|
|
9010
|
+
return err("scroll requires x, y, deltaX, deltaY");
|
|
9011
|
+
}
|
|
9012
|
+
await c.Input.dispatchMouseEvent({ type: "mouseWheel", x, y, deltaX, deltaY });
|
|
9013
|
+
return ok(`Scrolled at (${x}, ${y}) by (${deltaX}, ${deltaY})`);
|
|
9014
|
+
}
|
|
9015
|
+
async function dispatchKey(c, key) {
|
|
9016
|
+
if (!key) return err("key requires key");
|
|
9017
|
+
const isSingleChar = key.length === 1;
|
|
9018
|
+
const down = { type: "keyDown", key, code: key };
|
|
9019
|
+
if (isSingleChar) down.text = key;
|
|
9020
|
+
await c.Input.dispatchKeyEvent(down);
|
|
9021
|
+
await c.Input.dispatchKeyEvent({ type: "keyUp", key, code: key });
|
|
9022
|
+
return ok(`Dispatched key ${key}`);
|
|
9023
|
+
}
|
|
8516
9024
|
function registerSimulatorTools(server) {
|
|
8517
9025
|
server.tool("simulator_navigate", "Navigate the simulator to a URL, or reload the current page", {
|
|
8518
9026
|
url: z5.string().optional().describe("URL to navigate to"),
|
|
@@ -8542,94 +9050,20 @@ function registerSimulatorTools(server) {
|
|
|
8542
9050
|
deltaY: z5.number().optional().describe("Vertical wheel delta for `scroll`")
|
|
8543
9051
|
}, async ({ action, x, y, selector, nth, text, key, deltaX, deltaY }) => {
|
|
8544
9052
|
const c = getClient("simulator");
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
const expression = `(() => {
|
|
8559
|
-
const payload = ${JSON.stringify({ selector, index })}
|
|
8560
|
-
try {
|
|
8561
|
-
const matches = Array.from(document.querySelectorAll(payload.selector))
|
|
8562
|
-
if (matches.length === 0) {
|
|
8563
|
-
return { ok: false, reason: 'no_match', message: \`selector matched no elements: \${payload.selector}\` }
|
|
8564
|
-
}
|
|
8565
|
-
if (payload.index < 0 || payload.index >= matches.length) {
|
|
8566
|
-
return { ok: false, reason: 'out_of_range', message: \`nth \${payload.index} out of range (matches: \${matches.length})\` }
|
|
8567
|
-
}
|
|
8568
|
-
const element = matches[payload.index]
|
|
8569
|
-
element.scrollIntoView({ block: 'center', inline: 'center' })
|
|
8570
|
-
const rect = element.getBoundingClientRect()
|
|
8571
|
-
if (rect.width === 0 || rect.height === 0) {
|
|
8572
|
-
return { ok: false, reason: 'not_visible', message: \`selector matched an element, but it is not visible or rendered (zero rect): \${payload.selector}[\${payload.index}]\` }
|
|
8573
|
-
}
|
|
8574
|
-
return {
|
|
8575
|
-
ok: true,
|
|
8576
|
-
selector: payload.selector,
|
|
8577
|
-
index: payload.index,
|
|
8578
|
-
x: rect.left + rect.width / 2,
|
|
8579
|
-
y: rect.top + rect.height / 2,
|
|
8580
|
-
rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height },
|
|
8581
|
-
}
|
|
8582
|
-
} catch (error) {
|
|
8583
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
8584
|
-
return { ok: false, reason: 'selector_error', message: \`invalid selector: \${payload.selector} (\${message})\` }
|
|
8585
|
-
}
|
|
8586
|
-
})()`;
|
|
8587
|
-
const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
|
|
8588
|
-
const value = result.result?.value;
|
|
8589
|
-
if (!value) return err(`selector evaluation returned no result: ${selector}[${index}]`);
|
|
8590
|
-
if (!value.ok) return err(value.message);
|
|
8591
|
-
await clickAt(value.x, value.y);
|
|
8592
|
-
return {
|
|
8593
|
-
content: [{
|
|
8594
|
-
type: "text",
|
|
8595
|
-
text: `Tapped selector ${value.selector}[${value.index}] at (${value.x}, ${value.y}) within rect (${value.rect.left}, ${value.rect.top}, ${value.rect.width} x ${value.rect.height})`
|
|
8596
|
-
}]
|
|
8597
|
-
};
|
|
8598
|
-
}
|
|
8599
|
-
if (action === "type") {
|
|
8600
|
-
if (!selector || typeof text !== "string") return err("type requires selector and text");
|
|
8601
|
-
const { root } = await c.DOM.getDocument({ depth: 0 });
|
|
8602
|
-
const { nodeIds } = await c.DOM.querySelectorAll({ nodeId: root.nodeId, selector });
|
|
8603
|
-
if (!nodeIds || nodeIds.length === 0) return err(`selector matched no elements: ${selector}`);
|
|
8604
|
-
const index = nth ?? 0;
|
|
8605
|
-
if (index < 0 || index >= nodeIds.length) return err(`nth ${index} out of range (matches: ${nodeIds.length})`);
|
|
8606
|
-
await c.DOM.focus({ nodeId: nodeIds[index] });
|
|
8607
|
-
try {
|
|
8608
|
-
await c.Input.insertText({ text });
|
|
8609
|
-
} catch {
|
|
8610
|
-
for (const ch of text) {
|
|
8611
|
-
await c.Input.dispatchKeyEvent({ type: "char", text: ch });
|
|
8612
|
-
}
|
|
8613
|
-
}
|
|
8614
|
-
return { content: [{ type: "text", text: `Typed ${text.length} char(s) into ${selector}[${index}]` }] };
|
|
8615
|
-
}
|
|
8616
|
-
if (action === "scroll") {
|
|
8617
|
-
if (typeof x !== "number" || typeof y !== "number" || typeof deltaX !== "number" || typeof deltaY !== "number") {
|
|
8618
|
-
return err("scroll requires x, y, deltaX, deltaY");
|
|
8619
|
-
}
|
|
8620
|
-
await c.Input.dispatchMouseEvent({ type: "mouseWheel", x, y, deltaX, deltaY });
|
|
8621
|
-
return { content: [{ type: "text", text: `Scrolled at (${x}, ${y}) by (${deltaX}, ${deltaY})` }] };
|
|
8622
|
-
}
|
|
8623
|
-
if (action === "key") {
|
|
8624
|
-
if (!key) return err("key requires key");
|
|
8625
|
-
const isSingleChar = key.length === 1;
|
|
8626
|
-
const down = { type: "keyDown", key, code: key };
|
|
8627
|
-
if (isSingleChar) down.text = key;
|
|
8628
|
-
await c.Input.dispatchKeyEvent(down);
|
|
8629
|
-
await c.Input.dispatchKeyEvent({ type: "keyUp", key, code: key });
|
|
8630
|
-
return { content: [{ type: "text", text: `Dispatched key ${key}` }] };
|
|
9053
|
+
switch (action) {
|
|
9054
|
+
case "tap_coord":
|
|
9055
|
+
return tapCoord(c, x, y);
|
|
9056
|
+
case "tap_selector":
|
|
9057
|
+
return tapSelector(c, selector, nth);
|
|
9058
|
+
case "type":
|
|
9059
|
+
return typeText(c, selector, nth, text);
|
|
9060
|
+
case "scroll":
|
|
9061
|
+
return scrollAt(c, x, y, deltaX, deltaY);
|
|
9062
|
+
case "key":
|
|
9063
|
+
return dispatchKey(c, key);
|
|
9064
|
+
default:
|
|
9065
|
+
return err(`unknown action: ${String(action)}`);
|
|
8631
9066
|
}
|
|
8632
|
-
return err(`unknown action: ${action}`);
|
|
8633
9067
|
});
|
|
8634
9068
|
}
|
|
8635
9069
|
|
|
@@ -8684,13 +9118,13 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
|
|
|
8684
9118
|
res.writeHead(404).end();
|
|
8685
9119
|
}
|
|
8686
9120
|
});
|
|
8687
|
-
httpServer.on("error", (
|
|
8688
|
-
if (
|
|
9121
|
+
httpServer.on("error", (err2) => {
|
|
9122
|
+
if (err2.code === "EADDRINUSE") {
|
|
8689
9123
|
console.warn(`[MCP] Port ${mcpPort} already in use \u2014 MCP server not started`);
|
|
8690
9124
|
recordMcpFailed("port-in-use");
|
|
8691
9125
|
} else {
|
|
8692
|
-
console.error("[MCP] Server error:",
|
|
8693
|
-
recordMcpFailed(
|
|
9126
|
+
console.error("[MCP] Server error:", err2);
|
|
9127
|
+
recordMcpFailed(err2.message);
|
|
8694
9128
|
}
|
|
8695
9129
|
});
|
|
8696
9130
|
httpServer.listen(mcpPort, "127.0.0.1", () => {
|
|
@@ -8706,6 +9140,28 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
|
|
|
8706
9140
|
|
|
8707
9141
|
// src/main/services/network-forward/index.ts
|
|
8708
9142
|
import { DisposableRegistry as DisposableRegistry7, toDisposable as toDisposable5 } from "@dimina-kit/electron-deck/main";
|
|
9143
|
+
|
|
9144
|
+
// src/main/services/network-forward/dispatch-batch.ts
|
|
9145
|
+
function packDispatchBatch(queue, maxSingleChars, maxBatchChars) {
|
|
9146
|
+
const batch = [];
|
|
9147
|
+
const chunked = [];
|
|
9148
|
+
let batchChars = 0;
|
|
9149
|
+
let i = 0;
|
|
9150
|
+
for (; i < queue.length; i++) {
|
|
9151
|
+
const msg = queue[i];
|
|
9152
|
+
if (msg.length > maxSingleChars) {
|
|
9153
|
+
if (batch.length > 0) break;
|
|
9154
|
+
chunked.push(msg);
|
|
9155
|
+
continue;
|
|
9156
|
+
}
|
|
9157
|
+
if (batch.length > 0 && batchChars + msg.length > maxBatchChars) break;
|
|
9158
|
+
batch.push(msg);
|
|
9159
|
+
batchChars += msg.length;
|
|
9160
|
+
}
|
|
9161
|
+
return { batch, chunked, remaining: queue.slice(i) };
|
|
9162
|
+
}
|
|
9163
|
+
|
|
9164
|
+
// src/main/services/network-forward/index.ts
|
|
8709
9165
|
var REWRITE_REQUEST_ID_METHODS = /* @__PURE__ */ new Set([
|
|
8710
9166
|
"Network.requestWillBeSent",
|
|
8711
9167
|
"Network.requestWillBeSentExtraInfo",
|
|
@@ -8838,6 +9294,7 @@ function createNetworkForwarder(bridge) {
|
|
|
8838
9294
|
let sink = "idle";
|
|
8839
9295
|
let probeConsoleBuffer = [];
|
|
8840
9296
|
let readyTimeoutTimer = null;
|
|
9297
|
+
let probeDeadline = null;
|
|
8841
9298
|
let dispatchQueue = [];
|
|
8842
9299
|
let flushScheduled = false;
|
|
8843
9300
|
let readyRetryTimer = null;
|
|
@@ -8848,6 +9305,7 @@ function createNetworkForwarder(bridge) {
|
|
|
8848
9305
|
function beginProbing() {
|
|
8849
9306
|
if (sink === "probing") return;
|
|
8850
9307
|
sink = "probing";
|
|
9308
|
+
probeDeadline = Date.now() + DEVTOOLS_READY_TIMEOUT_MS;
|
|
8851
9309
|
if (readyTimeoutTimer) clearTimeout(readyTimeoutTimer);
|
|
8852
9310
|
readyTimeoutTimer = setTimeout(() => {
|
|
8853
9311
|
readyTimeoutTimer = null;
|
|
@@ -8856,6 +9314,7 @@ function createNetworkForwarder(bridge) {
|
|
|
8856
9314
|
}
|
|
8857
9315
|
function markReady() {
|
|
8858
9316
|
sink = "ready";
|
|
9317
|
+
probeDeadline = null;
|
|
8859
9318
|
if (readyTimeoutTimer) {
|
|
8860
9319
|
clearTimeout(readyTimeoutTimer);
|
|
8861
9320
|
readyTimeoutTimer = null;
|
|
@@ -8868,6 +9327,7 @@ function createNetworkForwarder(bridge) {
|
|
|
8868
9327
|
}
|
|
8869
9328
|
function degradeToConsole() {
|
|
8870
9329
|
sink = "degraded";
|
|
9330
|
+
probeDeadline = null;
|
|
8871
9331
|
dispatchQueue = [];
|
|
8872
9332
|
if (readyTimeoutTimer) {
|
|
8873
9333
|
clearTimeout(readyTimeoutTimer);
|
|
@@ -8922,21 +9382,8 @@ function createNetworkForwarder(bridge) {
|
|
|
8922
9382
|
return;
|
|
8923
9383
|
}
|
|
8924
9384
|
if (sink === "idle") beginProbing();
|
|
8925
|
-
const batch =
|
|
8926
|
-
|
|
8927
|
-
let i = 0;
|
|
8928
|
-
for (; i < dispatchQueue.length; i++) {
|
|
8929
|
-
const msg = dispatchQueue[i];
|
|
8930
|
-
if (msg.length > MAX_SINGLE_DISPATCH_CHARS2) {
|
|
8931
|
-
if (batch.length > 0) break;
|
|
8932
|
-
dispatchChunked(wc, msg);
|
|
8933
|
-
continue;
|
|
8934
|
-
}
|
|
8935
|
-
if (batch.length > 0 && batchChars + msg.length > MAX_BATCH_CHARS) break;
|
|
8936
|
-
batch.push(msg);
|
|
8937
|
-
batchChars += msg.length;
|
|
8938
|
-
}
|
|
8939
|
-
const remaining = dispatchQueue.slice(i);
|
|
9385
|
+
const { batch, chunked, remaining } = packDispatchBatch(dispatchQueue, MAX_SINGLE_DISPATCH_CHARS2, MAX_BATCH_CHARS);
|
|
9386
|
+
for (const msg of chunked) dispatchChunked(wc, msg);
|
|
8940
9387
|
if (batch.length === 0) {
|
|
8941
9388
|
dispatchQueue = remaining;
|
|
8942
9389
|
if (dispatchQueue.length > 0) scheduleFlush();
|
|
@@ -8952,8 +9399,8 @@ function createNetworkForwarder(bridge) {
|
|
|
8952
9399
|
}
|
|
8953
9400
|
dispatchQueue = remaining;
|
|
8954
9401
|
if (remaining.length > 0) scheduleFlush();
|
|
8955
|
-
wc.executeJavaScript(script, true).then((
|
|
8956
|
-
if (
|
|
9402
|
+
wc.executeJavaScript(script, true).then((ok2) => {
|
|
9403
|
+
if (ok2 === true) {
|
|
8957
9404
|
if (sink !== "ready") markReady();
|
|
8958
9405
|
return;
|
|
8959
9406
|
}
|
|
@@ -8985,9 +9432,17 @@ function createNetworkForwarder(bridge) {
|
|
|
8985
9432
|
}
|
|
8986
9433
|
function scheduleReadyRetry() {
|
|
8987
9434
|
if (readyRetryTimer || sink === "ready" || sink === "degraded") return;
|
|
9435
|
+
if (probeDeadline !== null && Date.now() >= probeDeadline) {
|
|
9436
|
+
degradeToConsole();
|
|
9437
|
+
return;
|
|
9438
|
+
}
|
|
8988
9439
|
readyRetryTimer = setTimeout(() => {
|
|
8989
9440
|
readyRetryTimer = null;
|
|
8990
9441
|
if (sink === "degraded") return;
|
|
9442
|
+
if (probeDeadline !== null && Date.now() >= probeDeadline) {
|
|
9443
|
+
degradeToConsole();
|
|
9444
|
+
return;
|
|
9445
|
+
}
|
|
8991
9446
|
if (dispatchQueue.length > 0) scheduleFlush();
|
|
8992
9447
|
}, READY_RETRY_MS);
|
|
8993
9448
|
}
|
|
@@ -9026,6 +9481,7 @@ function createNetworkForwarder(bridge) {
|
|
|
9026
9481
|
dispatchQueue = [];
|
|
9027
9482
|
probeConsoleBuffer = [];
|
|
9028
9483
|
sink = "idle";
|
|
9484
|
+
probeDeadline = null;
|
|
9029
9485
|
if (readyRetryTimer) {
|
|
9030
9486
|
clearTimeout(readyRetryTimer);
|
|
9031
9487
|
readyRetryTimer = null;
|
|
@@ -9048,8 +9504,8 @@ function createNetworkForwarder(bridge) {
|
|
|
9048
9504
|
if (!wc.debugger.isAttached()) {
|
|
9049
9505
|
wc.debugger.attach("1.3");
|
|
9050
9506
|
}
|
|
9051
|
-
} catch (
|
|
9052
|
-
console.warn("[network-forward] debugger.attach failed; simulator network not captured:",
|
|
9507
|
+
} catch (err2) {
|
|
9508
|
+
console.warn("[network-forward] debugger.attach failed; simulator network not captured:", err2 instanceof Error ? err2.message : err2);
|
|
9053
9509
|
return;
|
|
9054
9510
|
}
|
|
9055
9511
|
simWc = wc;
|
|
@@ -9145,8 +9601,8 @@ function createNetworkForwarder(bridge) {
|
|
|
9145
9601
|
}
|
|
9146
9602
|
});
|
|
9147
9603
|
}
|
|
9148
|
-
void wc.debugger.sendCommand("Network.enable").catch((
|
|
9149
|
-
console.warn("[network-forward] Network.enable failed:",
|
|
9604
|
+
void wc.debugger.sendCommand("Network.enable").catch((err2) => {
|
|
9605
|
+
console.warn("[network-forward] Network.enable failed:", err2 instanceof Error ? err2.message : err2);
|
|
9150
9606
|
});
|
|
9151
9607
|
}
|
|
9152
9608
|
function maybeFallback(record) {
|
|
@@ -9176,6 +9632,7 @@ function createNetworkForwarder(bridge) {
|
|
|
9176
9632
|
clearTimeout(readyRetryTimer);
|
|
9177
9633
|
readyRetryTimer = null;
|
|
9178
9634
|
}
|
|
9635
|
+
probeDeadline = null;
|
|
9179
9636
|
probeConsoleBuffer = [];
|
|
9180
9637
|
if (!devtoolsWc) {
|
|
9181
9638
|
sink = "idle";
|
|
@@ -9199,6 +9656,7 @@ function createNetworkForwarder(bridge) {
|
|
|
9199
9656
|
}
|
|
9200
9657
|
});
|
|
9201
9658
|
}
|
|
9659
|
+
sink = "idle";
|
|
9202
9660
|
beginProbing();
|
|
9203
9661
|
if (dispatchQueue.length > 0) scheduleFlush();
|
|
9204
9662
|
}
|
|
@@ -9316,41 +9774,46 @@ function setupSimulatorWxml(host, options) {
|
|
|
9316
9774
|
}
|
|
9317
9775
|
|
|
9318
9776
|
// src/shared/appdata-accumulator.ts
|
|
9319
|
-
function
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
return null;
|
|
9326
|
-
}
|
|
9777
|
+
function parseMessagePayload(message) {
|
|
9778
|
+
if (typeof message !== "string") return message;
|
|
9779
|
+
try {
|
|
9780
|
+
return JSON.parse(message);
|
|
9781
|
+
} catch {
|
|
9782
|
+
return null;
|
|
9327
9783
|
}
|
|
9784
|
+
}
|
|
9785
|
+
function decodeUpdateBatchBody(rawBody) {
|
|
9786
|
+
const body = rawBody;
|
|
9787
|
+
if (!body || typeof body !== "object") return null;
|
|
9788
|
+
if (typeof body.bridgeId !== "string" || !Array.isArray(body.updates)) return null;
|
|
9789
|
+
const out = [];
|
|
9790
|
+
for (const u of body.updates) {
|
|
9791
|
+
if (!u || typeof u.moduleId !== "string") continue;
|
|
9792
|
+
if (!u.moduleId.startsWith("page_")) continue;
|
|
9793
|
+
out.push({ mode: "patch", bridgeId: body.bridgeId, moduleId: u.moduleId, data: u.data });
|
|
9794
|
+
}
|
|
9795
|
+
return out.length > 0 ? out : null;
|
|
9796
|
+
}
|
|
9797
|
+
function decodePageInitBody(moduleId, rawBody) {
|
|
9798
|
+
const body = rawBody;
|
|
9799
|
+
if (!body || typeof body !== "object") return null;
|
|
9800
|
+
if (typeof body.bridgeId !== "string" || typeof body.path !== "string") return null;
|
|
9801
|
+
if (!body.data || typeof body.data !== "object") return null;
|
|
9802
|
+
return [{
|
|
9803
|
+
mode: "init",
|
|
9804
|
+
bridgeId: body.bridgeId,
|
|
9805
|
+
moduleId,
|
|
9806
|
+
componentPath: body.path,
|
|
9807
|
+
data: body.data
|
|
9808
|
+
}];
|
|
9809
|
+
}
|
|
9810
|
+
function decodeWorkerMessage(message) {
|
|
9811
|
+
const payload = parseMessagePayload(message);
|
|
9328
9812
|
if (!payload || typeof payload !== "object") return null;
|
|
9329
9813
|
const record = payload;
|
|
9330
|
-
if (record.type === "ub")
|
|
9331
|
-
const body = record.body;
|
|
9332
|
-
if (!body || typeof body !== "object") return null;
|
|
9333
|
-
if (typeof body.bridgeId !== "string" || !Array.isArray(body.updates)) return null;
|
|
9334
|
-
const out = [];
|
|
9335
|
-
for (const u of body.updates) {
|
|
9336
|
-
if (!u || typeof u.moduleId !== "string") continue;
|
|
9337
|
-
if (!u.moduleId.startsWith("page_")) continue;
|
|
9338
|
-
out.push({ mode: "patch", bridgeId: body.bridgeId, moduleId: u.moduleId, data: u.data });
|
|
9339
|
-
}
|
|
9340
|
-
return out.length > 0 ? out : null;
|
|
9341
|
-
}
|
|
9814
|
+
if (record.type === "ub") return decodeUpdateBatchBody(record.body);
|
|
9342
9815
|
if (typeof record.type === "string" && record.type.startsWith("page_")) {
|
|
9343
|
-
|
|
9344
|
-
if (!body || typeof body !== "object") return null;
|
|
9345
|
-
if (typeof body.bridgeId !== "string" || typeof body.path !== "string") return null;
|
|
9346
|
-
if (!body.data || typeof body.data !== "object") return null;
|
|
9347
|
-
return [{
|
|
9348
|
-
mode: "init",
|
|
9349
|
-
bridgeId: body.bridgeId,
|
|
9350
|
-
moduleId: record.type,
|
|
9351
|
-
componentPath: body.path,
|
|
9352
|
-
data: body.data
|
|
9353
|
-
}];
|
|
9816
|
+
return decodePageInitBody(record.type, record.body);
|
|
9354
9817
|
}
|
|
9355
9818
|
return null;
|
|
9356
9819
|
}
|
|
@@ -9804,38 +10267,51 @@ var EXT_MIME = {
|
|
|
9804
10267
|
".pdf": "application/pdf",
|
|
9805
10268
|
".zip": "application/zip"
|
|
9806
10269
|
};
|
|
10270
|
+
function isPng(head) {
|
|
10271
|
+
return head.length >= 8 && head[0] === 137 && head[1] === 80 && head[2] === 78 && head[3] === 71 && head[4] === 13 && head[5] === 10 && head[6] === 26 && head[7] === 10;
|
|
10272
|
+
}
|
|
10273
|
+
function isJpeg(head) {
|
|
10274
|
+
return head.length >= 3 && head[0] === 255 && head[1] === 216 && head[2] === 255;
|
|
10275
|
+
}
|
|
10276
|
+
function isGif(head) {
|
|
10277
|
+
return head.length >= 6 && head[0] === 71 && head[1] === 73 && head[2] === 70 && head[3] === 56 && (head[4] === 55 || head[4] === 57) && head[5] === 97;
|
|
10278
|
+
}
|
|
10279
|
+
function isWebp(head) {
|
|
10280
|
+
return head.length >= 12 && head[0] === 82 && head[1] === 73 && head[2] === 70 && head[3] === 70 && head[8] === 87 && head[9] === 69 && head[10] === 66 && head[11] === 80;
|
|
10281
|
+
}
|
|
10282
|
+
function isMp4(head) {
|
|
10283
|
+
return head.length >= 12 && head[4] === 102 && head[5] === 116 && head[6] === 121 && head[7] === 112;
|
|
10284
|
+
}
|
|
10285
|
+
function isPdf(head) {
|
|
10286
|
+
return head.length >= 4 && head[0] === 37 && head[1] === 80 && head[2] === 68 && head[3] === 70;
|
|
10287
|
+
}
|
|
10288
|
+
function isBmp(head) {
|
|
10289
|
+
return head.length >= 2 && head[0] === 66 && head[1] === 77;
|
|
10290
|
+
}
|
|
10291
|
+
function isZip(head) {
|
|
10292
|
+
return head.length >= 4 && head[0] === 80 && head[1] === 75 && head[2] === 3 && head[3] === 4;
|
|
10293
|
+
}
|
|
10294
|
+
function isId3Mp3(head) {
|
|
10295
|
+
return head.length >= 3 && head[0] === 73 && head[1] === 68 && head[2] === 51;
|
|
10296
|
+
}
|
|
10297
|
+
function isOgg(head) {
|
|
10298
|
+
return head.length >= 4 && head[0] === 79 && head[1] === 103 && head[2] === 103 && head[3] === 83;
|
|
10299
|
+
}
|
|
10300
|
+
var MAGIC_SNIFFERS = [
|
|
10301
|
+
{ mime: "image/png", match: isPng },
|
|
10302
|
+
{ mime: "image/jpeg", match: isJpeg },
|
|
10303
|
+
{ mime: "image/gif", match: isGif },
|
|
10304
|
+
{ mime: "image/webp", match: isWebp },
|
|
10305
|
+
{ mime: "video/mp4", match: isMp4 },
|
|
10306
|
+
{ mime: "application/pdf", match: isPdf },
|
|
10307
|
+
{ mime: "image/bmp", match: isBmp },
|
|
10308
|
+
{ mime: "application/zip", match: isZip },
|
|
10309
|
+
{ mime: "audio/mpeg", match: isId3Mp3 },
|
|
10310
|
+
{ mime: "audio/ogg", match: isOgg }
|
|
10311
|
+
];
|
|
9807
10312
|
function sniffMime(head) {
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
}
|
|
9811
|
-
if (head.length >= 3 && head[0] === 255 && head[1] === 216 && head[2] === 255) {
|
|
9812
|
-
return "image/jpeg";
|
|
9813
|
-
}
|
|
9814
|
-
if (head.length >= 6 && head[0] === 71 && head[1] === 73 && head[2] === 70 && head[3] === 56 && (head[4] === 55 || head[4] === 57) && head[5] === 97) {
|
|
9815
|
-
return "image/gif";
|
|
9816
|
-
}
|
|
9817
|
-
if (head.length >= 12 && head[0] === 82 && head[1] === 73 && head[2] === 70 && head[3] === 70 && head[8] === 87 && head[9] === 69 && head[10] === 66 && head[11] === 80) {
|
|
9818
|
-
return "image/webp";
|
|
9819
|
-
}
|
|
9820
|
-
if (head.length >= 12 && head[4] === 102 && head[5] === 116 && head[6] === 121 && head[7] === 112) {
|
|
9821
|
-
return "video/mp4";
|
|
9822
|
-
}
|
|
9823
|
-
if (head.length >= 4 && head[0] === 37 && head[1] === 80 && head[2] === 68 && head[3] === 70) {
|
|
9824
|
-
return "application/pdf";
|
|
9825
|
-
}
|
|
9826
|
-
if (head.length >= 2 && head[0] === 66 && head[1] === 77) {
|
|
9827
|
-
return "image/bmp";
|
|
9828
|
-
}
|
|
9829
|
-
if (head.length >= 4 && head[0] === 80 && head[1] === 75 && head[2] === 3 && head[3] === 4) {
|
|
9830
|
-
return "application/zip";
|
|
9831
|
-
}
|
|
9832
|
-
if (head.length >= 3 && head[0] === 73 && head[1] === 68 && head[2] === 51) {
|
|
9833
|
-
return "audio/mpeg";
|
|
9834
|
-
}
|
|
9835
|
-
if (head.length >= 4 && head[0] === 79 && head[1] === 103 && head[2] === 103 && head[3] === 83) {
|
|
9836
|
-
return "audio/ogg";
|
|
9837
|
-
}
|
|
9838
|
-
return null;
|
|
10313
|
+
const hit = MAGIC_SNIFFERS.find((sniffer) => sniffer.match(head));
|
|
10314
|
+
return hit ? hit.mime : null;
|
|
9839
10315
|
}
|
|
9840
10316
|
function extMime(realPath) {
|
|
9841
10317
|
const ext = path20.extname(realPath).toLowerCase();
|
|
@@ -9852,6 +10328,44 @@ function detectMime(realPath, head) {
|
|
|
9852
10328
|
function etagOf(mtimeMs, size) {
|
|
9853
10329
|
return `W/"${Math.floor(mtimeMs)}-${size}"`;
|
|
9854
10330
|
}
|
|
10331
|
+
function resolveRange(range, totalSize) {
|
|
10332
|
+
const { start, end } = range;
|
|
10333
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
10334
|
+
throw new RangeError(`invalid range: ${start}-${end}`);
|
|
10335
|
+
}
|
|
10336
|
+
if (start < 0) throw new RangeError(`range start out of bounds: ${start}`);
|
|
10337
|
+
if (start > end) throw new RangeError(`range start > end: ${start} > ${end}`);
|
|
10338
|
+
if (start >= totalSize) {
|
|
10339
|
+
throw new RangeError(`range start beyond file size: ${start} >= ${totalSize}`);
|
|
10340
|
+
}
|
|
10341
|
+
const clampedEnd = Math.min(end, totalSize - 1);
|
|
10342
|
+
return { sliceStart: start, sliceLen: clampedEnd - start + 1 };
|
|
10343
|
+
}
|
|
10344
|
+
async function readHeadBytes(handle, totalSize) {
|
|
10345
|
+
const head = Buffer.alloc(Math.min(12, totalSize));
|
|
10346
|
+
if (head.length > 0) {
|
|
10347
|
+
await handle.read(head, 0, head.length, 0);
|
|
10348
|
+
}
|
|
10349
|
+
return head;
|
|
10350
|
+
}
|
|
10351
|
+
async function readFullBody(handle, head, totalSize) {
|
|
10352
|
+
const bytes = Buffer.alloc(totalSize);
|
|
10353
|
+
if (totalSize === 0) return bytes;
|
|
10354
|
+
if (totalSize <= head.length) {
|
|
10355
|
+
head.copy(bytes, 0, 0, totalSize);
|
|
10356
|
+
return bytes;
|
|
10357
|
+
}
|
|
10358
|
+
head.copy(bytes, 0, 0, head.length);
|
|
10359
|
+
await handle.read(bytes, head.length, totalSize - head.length, head.length);
|
|
10360
|
+
return bytes;
|
|
10361
|
+
}
|
|
10362
|
+
async function readRangeBody(handle, sliceStart, sliceLen) {
|
|
10363
|
+
const bytes = Buffer.alloc(sliceLen);
|
|
10364
|
+
if (sliceLen > 0) {
|
|
10365
|
+
await handle.read(bytes, 0, sliceLen, sliceStart);
|
|
10366
|
+
}
|
|
10367
|
+
return bytes;
|
|
10368
|
+
}
|
|
9855
10369
|
async function readDiskFile(realPath, opts) {
|
|
9856
10370
|
const handle = await fs9.open(realPath, "r");
|
|
9857
10371
|
try {
|
|
@@ -9859,43 +10373,9 @@ async function readDiskFile(realPath, opts) {
|
|
|
9859
10373
|
const totalSize = st.size;
|
|
9860
10374
|
const mtimeMs = st.mtimeMs;
|
|
9861
10375
|
const range = opts?.range;
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
const { start, end } = range;
|
|
9866
|
-
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
9867
|
-
throw new RangeError(`invalid range: ${start}-${end}`);
|
|
9868
|
-
}
|
|
9869
|
-
if (start < 0) throw new RangeError(`range start out of bounds: ${start}`);
|
|
9870
|
-
if (start > end) throw new RangeError(`range start > end: ${start} > ${end}`);
|
|
9871
|
-
if (start >= totalSize) {
|
|
9872
|
-
throw new RangeError(`range start beyond file size: ${start} >= ${totalSize}`);
|
|
9873
|
-
}
|
|
9874
|
-
const clampedEnd = Math.min(end, totalSize - 1);
|
|
9875
|
-
sliceStart = start;
|
|
9876
|
-
sliceLen = clampedEnd - start + 1;
|
|
9877
|
-
}
|
|
9878
|
-
const head = Buffer.alloc(Math.min(12, totalSize));
|
|
9879
|
-
if (head.length > 0) {
|
|
9880
|
-
await handle.read(head, 0, head.length, 0);
|
|
9881
|
-
}
|
|
9882
|
-
let bytes;
|
|
9883
|
-
if (!range) {
|
|
9884
|
-
bytes = Buffer.alloc(totalSize);
|
|
9885
|
-
if (totalSize > 0) {
|
|
9886
|
-
if (totalSize <= head.length) {
|
|
9887
|
-
head.copy(bytes, 0, 0, totalSize);
|
|
9888
|
-
} else {
|
|
9889
|
-
head.copy(bytes, 0, 0, head.length);
|
|
9890
|
-
await handle.read(bytes, head.length, totalSize - head.length, head.length);
|
|
9891
|
-
}
|
|
9892
|
-
}
|
|
9893
|
-
} else {
|
|
9894
|
-
bytes = Buffer.alloc(sliceLen);
|
|
9895
|
-
if (sliceLen > 0) {
|
|
9896
|
-
await handle.read(bytes, 0, sliceLen, sliceStart);
|
|
9897
|
-
}
|
|
9898
|
-
}
|
|
10376
|
+
const { sliceStart, sliceLen } = range ? resolveRange(range, totalSize) : { sliceStart: 0, sliceLen: totalSize };
|
|
10377
|
+
const head = await readHeadBytes(handle, totalSize);
|
|
10378
|
+
const bytes = range ? await readRangeBody(handle, sliceStart, sliceLen) : await readFullBody(handle, head, totalSize);
|
|
9899
10379
|
return {
|
|
9900
10380
|
bytes,
|
|
9901
10381
|
mime: detectMime(realPath, head),
|
|
@@ -10069,12 +10549,12 @@ async function enforceSandbox(realPath) {
|
|
|
10069
10549
|
throw new Error("sandbox: realPath escapes the user-data base via symlink");
|
|
10070
10550
|
}
|
|
10071
10551
|
break;
|
|
10072
|
-
} catch (
|
|
10073
|
-
if (
|
|
10552
|
+
} catch (err2) {
|
|
10553
|
+
if (err2.code === "ENOENT") {
|
|
10074
10554
|
probe = path21.dirname(probe);
|
|
10075
10555
|
continue;
|
|
10076
10556
|
}
|
|
10077
|
-
throw
|
|
10557
|
+
throw err2;
|
|
10078
10558
|
}
|
|
10079
10559
|
}
|
|
10080
10560
|
return normalized;
|
|
@@ -10246,6 +10726,11 @@ function setupSimulatorTempFiles(simSession) {
|
|
|
10246
10726
|
// src/main/services/views/simulator-session-policy.ts
|
|
10247
10727
|
import { session as session3 } from "electron";
|
|
10248
10728
|
import { toDisposable as toDisposable7 } from "@dimina-kit/electron-deck/main";
|
|
10729
|
+
var CORS_OVERRIDE_HEADERS = /* @__PURE__ */ new Set([
|
|
10730
|
+
"access-control-allow-origin",
|
|
10731
|
+
"access-control-allow-headers",
|
|
10732
|
+
"access-control-allow-methods"
|
|
10733
|
+
]);
|
|
10249
10734
|
function applySimulatorWebRequestPolicy(simulatorSession) {
|
|
10250
10735
|
simulatorSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
|
10251
10736
|
const forcedReferer = getSimulatorServicewechatReferer();
|
|
@@ -10256,6 +10741,9 @@ function applySimulatorWebRequestPolicy(simulatorSession) {
|
|
|
10256
10741
|
});
|
|
10257
10742
|
simulatorSession.webRequest.onHeadersReceived((details, callback) => {
|
|
10258
10743
|
const headers = details.responseHeaders ?? {};
|
|
10744
|
+
for (const key of Object.keys(headers)) {
|
|
10745
|
+
if (CORS_OVERRIDE_HEADERS.has(key.toLowerCase())) delete headers[key];
|
|
10746
|
+
}
|
|
10259
10747
|
headers["access-control-allow-origin"] = ["*"];
|
|
10260
10748
|
headers["access-control-allow-headers"] = ["*"];
|
|
10261
10749
|
headers["access-control-allow-methods"] = ["*"];
|
|
@@ -10330,8 +10818,8 @@ function serveStaticFile(res, root, pathname) {
|
|
|
10330
10818
|
res.end("Forbidden");
|
|
10331
10819
|
return;
|
|
10332
10820
|
}
|
|
10333
|
-
nodeFs2.stat(realFile, (
|
|
10334
|
-
if (
|
|
10821
|
+
nodeFs2.stat(realFile, (err2, stat) => {
|
|
10822
|
+
if (err2 || !stat.isFile()) {
|
|
10335
10823
|
res.writeHead(404);
|
|
10336
10824
|
res.end("Not Found");
|
|
10337
10825
|
return;
|
|
@@ -10412,6 +10900,56 @@ function fsErrorStatus(e) {
|
|
|
10412
10900
|
if (code === "ENOENT") return 404;
|
|
10413
10901
|
return 500;
|
|
10414
10902
|
}
|
|
10903
|
+
async function fsStat(ctx) {
|
|
10904
|
+
const st = await statWithin(ctx.projectRoot, ctx.rel);
|
|
10905
|
+
jsonRes(ctx.res, 200, { type: st.isDirectory() ? 2 : 1, size: st.size, ctime: st.ctimeMs, mtime: st.mtimeMs });
|
|
10906
|
+
}
|
|
10907
|
+
async function fsReaddir(ctx) {
|
|
10908
|
+
const entries = await readdirWithin(ctx.projectRoot, ctx.rel);
|
|
10909
|
+
jsonRes(ctx.res, 200, entries.map((e) => [e.name, e.isDirectory() ? 2 : 1]));
|
|
10910
|
+
}
|
|
10911
|
+
async function fsRead(ctx) {
|
|
10912
|
+
const buf = await readFileBufferWithin(ctx.projectRoot, ctx.rel);
|
|
10913
|
+
ctx.res.writeHead(200, { "Content-Type": "application/octet-stream", "Content-Length": buf.length });
|
|
10914
|
+
ctx.res.end(buf);
|
|
10915
|
+
}
|
|
10916
|
+
async function fsWrite(ctx) {
|
|
10917
|
+
let buf;
|
|
10918
|
+
try {
|
|
10919
|
+
buf = await readBody(ctx.req);
|
|
10920
|
+
} catch (e) {
|
|
10921
|
+
if (e instanceof BodyTooLargeError) return jsonRes(ctx.res, 413, { error: e.message });
|
|
10922
|
+
throw e;
|
|
10923
|
+
}
|
|
10924
|
+
await writeFileWithin(ctx.projectRoot, ctx.rel, buf);
|
|
10925
|
+
ctx.res.writeHead(204);
|
|
10926
|
+
ctx.res.end();
|
|
10927
|
+
}
|
|
10928
|
+
async function fsMkdir(ctx) {
|
|
10929
|
+
await mkdirWithin(ctx.projectRoot, ctx.rel);
|
|
10930
|
+
ctx.res.writeHead(204);
|
|
10931
|
+
ctx.res.end();
|
|
10932
|
+
}
|
|
10933
|
+
async function fsDelete(ctx) {
|
|
10934
|
+
await deleteWithin(ctx.projectRoot, ctx.rel);
|
|
10935
|
+
ctx.res.writeHead(204);
|
|
10936
|
+
ctx.res.end();
|
|
10937
|
+
}
|
|
10938
|
+
async function fsRename(ctx) {
|
|
10939
|
+
const toRel = (ctx.url.searchParams.get("to") ?? "").replace(/^\/+/, "");
|
|
10940
|
+
await renameWithin(ctx.projectRoot, ctx.rel, toRel);
|
|
10941
|
+
ctx.res.writeHead(204);
|
|
10942
|
+
ctx.res.end();
|
|
10943
|
+
}
|
|
10944
|
+
var FS_ACTIONS = {
|
|
10945
|
+
stat: fsStat,
|
|
10946
|
+
readdir: fsReaddir,
|
|
10947
|
+
read: fsRead,
|
|
10948
|
+
write: fsWrite,
|
|
10949
|
+
mkdir: fsMkdir,
|
|
10950
|
+
delete: fsDelete,
|
|
10951
|
+
rename: fsRename
|
|
10952
|
+
};
|
|
10415
10953
|
async function handleFsRequest(req, res, projectRoot, url) {
|
|
10416
10954
|
const action = url.pathname.slice("/__fs/".length);
|
|
10417
10955
|
const rel = (url.searchParams.get("p") ?? "").replace(/^\/+/, "") || ".";
|
|
@@ -10421,51 +10959,12 @@ async function handleFsRequest(req, res, projectRoot, url) {
|
|
|
10421
10959
|
return jsonRes(res, rejectStatus, { error: "mutating fs action requires a non-GET same-origin request" });
|
|
10422
10960
|
}
|
|
10423
10961
|
}
|
|
10962
|
+
const handler = FS_ACTIONS[action];
|
|
10963
|
+
if (!handler) return jsonRes(res, 404, { error: "unknown fs action: " + action });
|
|
10424
10964
|
try {
|
|
10425
|
-
|
|
10426
|
-
const st = await statWithin(projectRoot, rel);
|
|
10427
|
-
return jsonRes(res, 200, { type: st.isDirectory() ? 2 : 1, size: st.size, ctime: st.ctimeMs, mtime: st.mtimeMs });
|
|
10428
|
-
}
|
|
10429
|
-
if (action === "readdir") {
|
|
10430
|
-
const entries = await readdirWithin(projectRoot, rel);
|
|
10431
|
-
return jsonRes(res, 200, entries.map((e) => [e.name, e.isDirectory() ? 2 : 1]));
|
|
10432
|
-
}
|
|
10433
|
-
if (action === "read") {
|
|
10434
|
-
const buf = await readFileBufferWithin(projectRoot, rel);
|
|
10435
|
-
res.writeHead(200, { "Content-Type": "application/octet-stream", "Content-Length": buf.length });
|
|
10436
|
-
return void res.end(buf);
|
|
10437
|
-
}
|
|
10438
|
-
if (action === "write") {
|
|
10439
|
-
let buf;
|
|
10440
|
-
try {
|
|
10441
|
-
buf = await readBody(req);
|
|
10442
|
-
} catch (e) {
|
|
10443
|
-
if (e instanceof BodyTooLargeError) return jsonRes(res, 413, { error: e.message });
|
|
10444
|
-
throw e;
|
|
10445
|
-
}
|
|
10446
|
-
await writeFileWithin(projectRoot, rel, buf);
|
|
10447
|
-
res.writeHead(204);
|
|
10448
|
-
return void res.end();
|
|
10449
|
-
}
|
|
10450
|
-
if (action === "mkdir") {
|
|
10451
|
-
await mkdirWithin(projectRoot, rel);
|
|
10452
|
-
res.writeHead(204);
|
|
10453
|
-
return void res.end();
|
|
10454
|
-
}
|
|
10455
|
-
if (action === "delete") {
|
|
10456
|
-
await deleteWithin(projectRoot, rel);
|
|
10457
|
-
res.writeHead(204);
|
|
10458
|
-
return void res.end();
|
|
10459
|
-
}
|
|
10460
|
-
if (action === "rename") {
|
|
10461
|
-
const toRel = (url.searchParams.get("to") ?? "").replace(/^\/+/, "");
|
|
10462
|
-
await renameWithin(projectRoot, rel, toRel);
|
|
10463
|
-
res.writeHead(204);
|
|
10464
|
-
return void res.end();
|
|
10465
|
-
}
|
|
10466
|
-
return jsonRes(res, 404, { error: "unknown fs action: " + action });
|
|
10965
|
+
await handler({ req, res, projectRoot, rel, url });
|
|
10467
10966
|
} catch (e) {
|
|
10468
|
-
|
|
10967
|
+
jsonRes(res, fsErrorStatus(e), { error: String(e.message), code: e.code });
|
|
10469
10968
|
}
|
|
10470
10969
|
}
|
|
10471
10970
|
async function listFilesRecursive(dir, base = "") {
|
|
@@ -10598,8 +11097,8 @@ var UpdateManager = class {
|
|
|
10598
11097
|
return { hasUpdate: true, info };
|
|
10599
11098
|
}
|
|
10600
11099
|
return { hasUpdate: false };
|
|
10601
|
-
} catch (
|
|
10602
|
-
console.warn("[UpdateManager] check failed:",
|
|
11100
|
+
} catch (err2) {
|
|
11101
|
+
console.warn("[UpdateManager] check failed:", err2);
|
|
10603
11102
|
return { hasUpdate: false };
|
|
10604
11103
|
}
|
|
10605
11104
|
}
|
|
@@ -10613,9 +11112,9 @@ var UpdateManager = class {
|
|
|
10613
11112
|
});
|
|
10614
11113
|
this.downloadedPath = filePath;
|
|
10615
11114
|
return { success: true, filePath };
|
|
10616
|
-
} catch (
|
|
10617
|
-
console.warn("[UpdateManager] download failed:",
|
|
10618
|
-
return { success: false, error: String(
|
|
11115
|
+
} catch (err2) {
|
|
11116
|
+
console.warn("[UpdateManager] download failed:", err2);
|
|
11117
|
+
return { success: false, error: String(err2) };
|
|
10619
11118
|
}
|
|
10620
11119
|
}
|
|
10621
11120
|
install() {
|
|
@@ -10719,8 +11218,8 @@ function resolveModules(config) {
|
|
|
10719
11218
|
}
|
|
10720
11219
|
async function disposeContext(ctx) {
|
|
10721
11220
|
await ctx.workspace.closeProject();
|
|
10722
|
-
await ctx.registry.dispose().catch((
|
|
10723
|
-
console.warn("[workbench] dispose registry encountered errors:",
|
|
11221
|
+
await ctx.registry.dispose().catch((err2) => {
|
|
11222
|
+
console.warn("[workbench] dispose registry encountered errors:", err2);
|
|
10724
11223
|
});
|
|
10725
11224
|
}
|
|
10726
11225
|
function createConfiguredMainWindow(config, rendererDir2) {
|
|
@@ -10936,27 +11435,10 @@ async function createDevtoolsRuntime(config = {}) {
|
|
|
10936
11435
|
appDataKeys: []
|
|
10937
11436
|
};
|
|
10938
11437
|
if (appId) {
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
if (Array.isArray(keys)) {
|
|
10944
|
-
overview.storageKeys = keys.filter((key) => typeof key === "string");
|
|
10945
|
-
overview.storageCount = keys.length;
|
|
10946
|
-
}
|
|
10947
|
-
}
|
|
10948
|
-
} catch {
|
|
10949
|
-
}
|
|
10950
|
-
try {
|
|
10951
|
-
const snapshot = context.appData?.snapshot?.(appId);
|
|
10952
|
-
if (snapshot && typeof snapshot === "object") {
|
|
10953
|
-
const entries = snapshot.entries;
|
|
10954
|
-
if (entries && typeof entries === "object") {
|
|
10955
|
-
overview.appDataKeys = Object.keys(entries);
|
|
10956
|
-
}
|
|
10957
|
-
}
|
|
10958
|
-
} catch {
|
|
10959
|
-
}
|
|
11438
|
+
const storage2 = await resolveNativeStorageOverview(context, appId);
|
|
11439
|
+
overview.storageKeys = storage2.storageKeys;
|
|
11440
|
+
overview.storageCount = storage2.storageCount;
|
|
11441
|
+
overview.appDataKeys = resolveNativeAppDataKeys(context, appId);
|
|
10960
11442
|
}
|
|
10961
11443
|
return overview;
|
|
10962
11444
|
});
|
|
@@ -11084,7 +11566,7 @@ function launch(config = {}) {
|
|
|
11084
11566
|
}
|
|
11085
11567
|
|
|
11086
11568
|
// src/main/index.ts
|
|
11087
|
-
launch().catch((
|
|
11088
|
-
console.error("[devtools] fatal: launch() failed during boot",
|
|
11569
|
+
launch().catch((err2) => {
|
|
11570
|
+
console.error("[devtools] fatal: launch() failed during boot", err2);
|
|
11089
11571
|
app16.exit(1);
|
|
11090
11572
|
});
|