@dimina-kit/devtools 0.4.0-dev.20260625063832 → 0.4.0-dev.20260629114208
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 +2 -0
- package/dist/main/index.bundle.js +230 -144
- package/dist/main/ipc/bridge-router.d.ts +21 -0
- package/dist/main/ipc/bridge-router.js +63 -3
- package/dist/main/services/automation/index.js +0 -1
- package/dist/main/services/elements-forward/index.js +81 -72
- package/dist/main/services/mcp/target-manager.d.ts +3 -3
- package/dist/main/services/views/miniapp-partition.d.ts +18 -8
- package/dist/main/services/views/miniapp-partition.js +34 -13
- package/dist/main/services/views/view-manager.js +59 -36
- package/dist/main/services/workbench-coi-server.d.ts +7 -0
- package/dist/main/services/workbench-coi-server.js +6 -0
- package/dist/main/services/workbench-context.d.ts +4 -2
- package/dist/main/services/workbench-context.js +1 -0
- package/dist/main/services/workspace/op-lock.d.ts +26 -0
- package/dist/main/services/workspace/op-lock.js +23 -0
- package/dist/main/services/workspace/workspace-service.d.ts +7 -0
- package/dist/main/services/workspace/workspace-service.js +197 -103
- package/dist/main/windows/service-host-window/create.d.ts +5 -1
- package/dist/main/windows/service-host-window/create.js +6 -3
- package/dist/native-host/render/render.js +485 -449
- package/dist/native-host/service/service.js +1 -1
- package/dist/renderer/assets/index-BMwmmaSp.js +46 -0
- package/dist/renderer/entries/main/index.html +1 -1
- package/dist/shared/types.d.ts +15 -0
- package/dist/vscode-workbench/assets/__vite-browser-external-B0DTNerG.js +1 -0
- package/dist/vscode-workbench/assets/__vite-browser-external-BuLjVMfN.js +1 -0
- package/dist/vscode-workbench/assets/__vite-browser-external-thkx7I19.js +1 -0
- package/dist/vscode-workbench/assets/{dist-SFZ2HNEz.js → dist-CS7SQP3K.js} +4 -4
- package/dist/vscode-workbench/assets/dist-VSgdjUlW.js +8 -0
- package/dist/vscode-workbench/assets/editor.worker-DmMg51ee.js +41 -0
- package/dist/vscode-workbench/assets/extensionHost.worker-BMpl1cpB.js +207 -0
- package/dist/vscode-workbench/assets/{iconv-lite-umd-izd5l6fa.js → iconv-lite-umd-D3q-1-o6.js} +1 -1
- package/dist/vscode-workbench/assets/{iconv-lite-umd-COfjYshr.js → iconv-lite-umd-Jn9RTrsv.js} +2 -2
- package/dist/vscode-workbench/assets/index-zjigpZ25.js +2842 -0
- package/dist/vscode-workbench/assets/{jschardet-Bf-VMK4F.js → jschardet-CjCVc2Gw.js} +1 -1
- package/dist/vscode-workbench/assets/{jschardet-Dh5TyKsQ.js → jschardet-Cu4ufeHq.js} +1 -1
- package/dist/vscode-workbench/assets/{layout.contribution.darwin-cz2ttHTW.js → layout.contribution.darwin-CoH1BZtz.js} +1 -1
- package/dist/vscode-workbench/assets/{layout.contribution.linux-fEtdzbcY.js → layout.contribution.linux-Di2FF-CG.js} +1 -1
- package/dist/vscode-workbench/assets/{layout.contribution.win-BFyOKI8c.js → layout.contribution.win-moiNStil.js} +1 -1
- package/dist/vscode-workbench/assets/{main-Bh8TI5LP.js → main-DzHL45xo.js} +4 -4
- package/dist/vscode-workbench/assets/{main2-Bds9Fhfn.js → main2-CWVd-Hyp.js} +1 -1
- package/dist/vscode-workbench/assets/{worker-D9ti6A0r.js → worker-CRTb-Arc.js} +7 -7
- package/dist/vscode-workbench/assets/{worker-DQtXDXIZ.js → worker-CVB8WPuM.js} +2 -2
- package/dist/vscode-workbench/index.html +1 -4
- package/package.json +7 -37
- package/dist/renderer/assets/index-6XUSo6Su.js +0 -46
- package/dist/vscode-workbench/assets/__vite-browser-external-BUwiolNy.js +0 -1
- package/dist/vscode-workbench/assets/__vite-browser-external-CmG9GjjO.js +0 -1
- package/dist/vscode-workbench/assets/__vite-browser-external-DGJPTKLg.js +0 -1
- package/dist/vscode-workbench/assets/_commonjsHelpers-qcUHfnrg.js +0 -1
- package/dist/vscode-workbench/assets/chunk-B3K2TuZy.js +0 -1
- package/dist/vscode-workbench/assets/chunk-DECur_0Z.js +0 -1
- package/dist/vscode-workbench/assets/dist-tesJ1Kd2.js +0 -8
- package/dist/vscode-workbench/assets/editor.worker-BC1qOyXG.js +0 -41
- package/dist/vscode-workbench/assets/extension.api-BWA2aPAU.js +0 -1
- package/dist/vscode-workbench/assets/extensionHost.worker-Dvu35anN.js +0 -207
- package/dist/vscode-workbench/assets/index-CQ5xP0pp.js +0 -1656
- package/dist/vscode-workbench/assets/localExtensionHost-Ccz7QUdA.js +0 -1188
- package/dist/vscode-workbench/assets/main-BDgdQmvs.js +0 -4
- package/dist/vscode-workbench/assets/main2-DwfDVleQ.js +0 -1
- package/dist/vscode-workbench/assets/preload-helper-xBbMyY7u.js +0 -1
- /package/dist/vscode-workbench/assets/{_.contribution-C9aS_G-o.js → _.contribution-B46wcGKQ.js} +0 -0
- /package/dist/vscode-workbench/assets/{_commonjsHelpers-BPKu-ps5.js → _commonjsHelpers-DMmVv2OA.js} +0 -0
package/dist/main/app/app.js
CHANGED
|
@@ -151,6 +151,7 @@ function createContext(config, mainWindow, rendererDir) {
|
|
|
151
151
|
rendererDir,
|
|
152
152
|
appName: config.appName,
|
|
153
153
|
apiNamespaces: config.apiNamespaces,
|
|
154
|
+
fileTypes: config.fileTypes,
|
|
154
155
|
brandingProvider: config.brandingProvider,
|
|
155
156
|
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
156
157
|
// are structurally compatible with the main-process equivalents —
|
|
@@ -558,6 +559,7 @@ export async function createDevtoolsRuntime(config = {}) {
|
|
|
558
559
|
rootDir: bundleDir,
|
|
559
560
|
getProjectRoot: () => context.workspace.getProjectPath(),
|
|
560
561
|
extensionsDir: config.editorViewConfig?.extensionsDir,
|
|
562
|
+
getFileTypes: () => context.fileTypes,
|
|
561
563
|
});
|
|
562
564
|
// Return the close promise so the registry awaits server shutdown on dispose
|
|
563
565
|
// instead of fire-and-forgetting it (a dangling http server would keep the
|
|
@@ -1653,6 +1653,9 @@ function buildElementsHookScript() {
|
|
|
1653
1653
|
return 'partial';
|
|
1654
1654
|
}catch(e){ return 'error:' + (e && e.message); }})()`;
|
|
1655
1655
|
}
|
|
1656
|
+
function buildReconcileScript() {
|
|
1657
|
+
return `(function(){var status; try { status = ${buildElementsHookScript()}; } catch(e){ status = 'error:'+(e&&e.message); }var batch = []; try { if (globalThis.__diminaElementsOutbound) batch = globalThis.__diminaElementsOutbound.splice(0); } catch(_){}return { status: status, batch: batch };})()`;
|
|
1658
|
+
}
|
|
1656
1659
|
var PROBE_DEVTOOLS_API = `(window.DevToolsAPI && typeof window.DevToolsAPI.dispatchMessage === 'function')`;
|
|
1657
1660
|
var MAX_SINGLE_DISPATCH_CHARS = 1e6;
|
|
1658
1661
|
var CHUNK_CHARS = 256 * 1024;
|
|
@@ -1668,13 +1671,11 @@ function buildDocumentUpdatedScript() {
|
|
|
1668
1671
|
return buildDispatchScript(msg);
|
|
1669
1672
|
}
|
|
1670
1673
|
var DRAIN_INTERVAL_MS = 150;
|
|
1671
|
-
var INSTALL_POLL_TRIES = 200;
|
|
1672
|
-
var INSTALL_POLL_INTERVAL_MS = 50;
|
|
1673
1674
|
function installElementsForward(deps) {
|
|
1674
1675
|
const { devtoolsWc, bridge } = deps;
|
|
1675
1676
|
let disposed = false;
|
|
1676
|
-
let
|
|
1677
|
-
let
|
|
1677
|
+
let reconcileTimer = null;
|
|
1678
|
+
let lateCleanup = null;
|
|
1678
1679
|
const isActiveWcId = (id) => {
|
|
1679
1680
|
const a = activeRenderWc();
|
|
1680
1681
|
return a != null && a.id === id;
|
|
@@ -1683,13 +1684,9 @@ function installElementsForward(deps) {
|
|
|
1683
1684
|
const selfAttached = /* @__PURE__ */ new Set();
|
|
1684
1685
|
const stop = () => {
|
|
1685
1686
|
disposed = true;
|
|
1686
|
-
if (
|
|
1687
|
-
clearInterval(
|
|
1688
|
-
|
|
1689
|
-
}
|
|
1690
|
-
if (drainTimer) {
|
|
1691
|
-
clearInterval(drainTimer);
|
|
1692
|
-
drainTimer = null;
|
|
1687
|
+
if (reconcileTimer) {
|
|
1688
|
+
clearInterval(reconcileTimer);
|
|
1689
|
+
reconcileTimer = null;
|
|
1693
1690
|
}
|
|
1694
1691
|
for (const cleanup of wiredGuests.values()) {
|
|
1695
1692
|
try {
|
|
@@ -1699,6 +1696,14 @@ function installElementsForward(deps) {
|
|
|
1699
1696
|
}
|
|
1700
1697
|
wiredGuests.clear();
|
|
1701
1698
|
detachSelfAttached();
|
|
1699
|
+
if (lateCleanup) {
|
|
1700
|
+
const c = lateCleanup;
|
|
1701
|
+
lateCleanup = null;
|
|
1702
|
+
try {
|
|
1703
|
+
c();
|
|
1704
|
+
} catch {
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1702
1707
|
};
|
|
1703
1708
|
function detachSelfAttached() {
|
|
1704
1709
|
for (const wcId of selfAttached) {
|
|
@@ -1908,46 +1913,26 @@ function installElementsForward(deps) {
|
|
|
1908
1913
|
const unsubscribeRenderEvents = bridge.onRenderEvent(onRenderEvent);
|
|
1909
1914
|
const onReady = () => {
|
|
1910
1915
|
if (disposed) return;
|
|
1911
|
-
if (
|
|
1912
|
-
clearInterval(
|
|
1913
|
-
|
|
1916
|
+
if (reconcileTimer) {
|
|
1917
|
+
clearInterval(reconcileTimer);
|
|
1918
|
+
reconcileTimer = null;
|
|
1914
1919
|
}
|
|
1915
|
-
|
|
1916
|
-
clearInterval(drainTimer);
|
|
1917
|
-
drainTimer = null;
|
|
1918
|
-
}
|
|
1919
|
-
let tries = 0;
|
|
1920
|
-
installTimer = setInterval(() => {
|
|
1921
|
-
tries++;
|
|
1920
|
+
reconcileTimer = setInterval(() => {
|
|
1922
1921
|
if (disposed || devtoolsWc.isDestroyed()) {
|
|
1923
1922
|
stop();
|
|
1924
1923
|
return;
|
|
1925
1924
|
}
|
|
1926
|
-
devtoolsWc.executeJavaScript(
|
|
1925
|
+
devtoolsWc.executeJavaScript(buildReconcileScript()).then((res) => {
|
|
1927
1926
|
if (disposed) return;
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
clearInterval(installTimer);
|
|
1931
|
-
installTimer = null;
|
|
1932
|
-
}
|
|
1927
|
+
const r = res ?? {};
|
|
1928
|
+
if (r.status === "installed") {
|
|
1933
1929
|
const wc = activeRenderWc();
|
|
1934
1930
|
if (wc) primeGuest(wc);
|
|
1935
1931
|
pushDocumentUpdated();
|
|
1936
1932
|
}
|
|
1933
|
+
handleOutbound(r.batch);
|
|
1937
1934
|
}).catch(() => {
|
|
1938
1935
|
});
|
|
1939
|
-
if (tries > INSTALL_POLL_TRIES && installTimer) {
|
|
1940
|
-
clearInterval(installTimer);
|
|
1941
|
-
installTimer = null;
|
|
1942
|
-
}
|
|
1943
|
-
}, INSTALL_POLL_INTERVAL_MS);
|
|
1944
|
-
drainTimer = setInterval(() => {
|
|
1945
|
-
if (disposed || devtoolsWc.isDestroyed()) {
|
|
1946
|
-
stop();
|
|
1947
|
-
return;
|
|
1948
|
-
}
|
|
1949
|
-
devtoolsWc.executeJavaScript("(globalThis.__diminaElementsOutbound ? __diminaElementsOutbound.splice(0) : [])").then(handleOutbound).catch(() => {
|
|
1950
|
-
});
|
|
1951
1936
|
}, DRAIN_INTERVAL_MS);
|
|
1952
1937
|
};
|
|
1953
1938
|
devtoolsWc.on("dom-ready", onReady);
|
|
@@ -1958,7 +1943,7 @@ function installElementsForward(deps) {
|
|
|
1958
1943
|
else devtoolsWc.once("destroyed", stop);
|
|
1959
1944
|
} catch {
|
|
1960
1945
|
}
|
|
1961
|
-
|
|
1946
|
+
lateCleanup = () => {
|
|
1962
1947
|
try {
|
|
1963
1948
|
unsubscribeRenderEvents();
|
|
1964
1949
|
} catch {
|
|
@@ -1971,8 +1956,8 @@ function installElementsForward(deps) {
|
|
|
1971
1956
|
devtoolsWc.removeListener("dom-ready", onReady);
|
|
1972
1957
|
} catch {
|
|
1973
1958
|
}
|
|
1974
|
-
stop();
|
|
1975
1959
|
};
|
|
1960
|
+
return stop;
|
|
1976
1961
|
}
|
|
1977
1962
|
|
|
1978
1963
|
// src/main/services/service-console/console-api.ts
|
|
@@ -2190,15 +2175,15 @@ function createSimulatorApiRegistry() {
|
|
|
2190
2175
|
import * as electron from "electron";
|
|
2191
2176
|
var SHARED_MINIAPP_PARTITION = "persist:simulator";
|
|
2192
2177
|
var PARTITION_PREFIX = "persist:miniapp-";
|
|
2193
|
-
function miniappPartitionKey(appId) {
|
|
2178
|
+
function miniappPartitionKey(appId, projectPath) {
|
|
2194
2179
|
const safe = appId.replace(/[^A-Za-z0-9_-]/g, "");
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
return
|
|
2180
|
+
const base = safe === appId && safe.length > 0 ? safe : safe.length > 0 ? `${safe}-${djb2(appId).toString(36)}` : djb2(appId).toString(36);
|
|
2181
|
+
if (!projectPath) return base;
|
|
2182
|
+
return `${base}-p${djb2(projectPath).toString(36)}`;
|
|
2198
2183
|
}
|
|
2199
|
-
function miniappPartition(appId) {
|
|
2184
|
+
function miniappPartition(appId, projectPath) {
|
|
2200
2185
|
if (!appId) return SHARED_MINIAPP_PARTITION;
|
|
2201
|
-
return `${PARTITION_PREFIX}${miniappPartitionKey(appId)}`;
|
|
2186
|
+
return `${PARTITION_PREFIX}${miniappPartitionKey(appId, projectPath)}`;
|
|
2202
2187
|
}
|
|
2203
2188
|
function djb2(input) {
|
|
2204
2189
|
let h = 5381;
|
|
@@ -2938,13 +2923,14 @@ function createViewManager(ctx) {
|
|
|
2938
2923
|
stopElementsForward?.();
|
|
2939
2924
|
} catch {
|
|
2940
2925
|
}
|
|
2941
|
-
|
|
2926
|
+
const thisForward = installElementsForward({ devtoolsWc, bridge: ctx.bridge, connections: ctx.connections });
|
|
2927
|
+
stopElementsForward = thisForward;
|
|
2942
2928
|
devtoolsWc.once("destroyed", () => {
|
|
2943
2929
|
try {
|
|
2944
|
-
|
|
2930
|
+
thisForward();
|
|
2945
2931
|
} catch {
|
|
2946
2932
|
}
|
|
2947
|
-
stopElementsForward = null;
|
|
2933
|
+
if (stopElementsForward === thisForward) stopElementsForward = null;
|
|
2948
2934
|
});
|
|
2949
2935
|
}
|
|
2950
2936
|
devtoolsWc.once("dom-ready", () => {
|
|
@@ -2987,6 +2973,24 @@ function createViewManager(ctx) {
|
|
|
2987
2973
|
nativeCustomApiBridgeHandler = null;
|
|
2988
2974
|
}
|
|
2989
2975
|
}
|
|
2976
|
+
function tearDownNativeSimulatorView(label) {
|
|
2977
|
+
if (!nativeSimulatorView) return;
|
|
2978
|
+
if (nativeSimulatorViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
|
|
2979
|
+
try {
|
|
2980
|
+
ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
|
|
2981
|
+
} catch {
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
try {
|
|
2985
|
+
if (!nativeSimulatorView.webContents.isDestroyed()) {
|
|
2986
|
+
ctx.bridge?.disposeSessionsForSimulator?.(nativeSimulatorView.webContents.id)?.catch((err) => console.warn(`[view-manager] dispose sessions (${label}) failed:`, err));
|
|
2987
|
+
nativeSimulatorView.webContents.close();
|
|
2988
|
+
}
|
|
2989
|
+
} catch {
|
|
2990
|
+
}
|
|
2991
|
+
nativeSimulatorView = null;
|
|
2992
|
+
nativeSimulatorViewAdded = false;
|
|
2993
|
+
}
|
|
2990
2994
|
function attachNativeCustomApiBridge(simWc) {
|
|
2991
2995
|
detachNativeCustomApiBridge();
|
|
2992
2996
|
const apis = ctx.simulatorApis;
|
|
@@ -3015,27 +3019,14 @@ function createViewManager(ctx) {
|
|
|
3015
3019
|
settleNativeSimulatorReady = null;
|
|
3016
3020
|
if (nativeSimulatorView) {
|
|
3017
3021
|
detachNativeCustomApiBridge();
|
|
3018
|
-
|
|
3019
|
-
try {
|
|
3020
|
-
ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
|
|
3021
|
-
} catch {
|
|
3022
|
-
}
|
|
3023
|
-
nativeSimulatorViewAdded = false;
|
|
3024
|
-
}
|
|
3025
|
-
try {
|
|
3026
|
-
if (!nativeSimulatorView.webContents.isDestroyed()) {
|
|
3027
|
-
nativeSimulatorView.webContents.close();
|
|
3028
|
-
}
|
|
3029
|
-
} catch {
|
|
3030
|
-
}
|
|
3031
|
-
nativeSimulatorView = null;
|
|
3022
|
+
tearDownNativeSimulatorView("relaunch");
|
|
3032
3023
|
}
|
|
3033
3024
|
nativeSimulatorProjectPath = null;
|
|
3034
3025
|
const ready = new Promise((resolve) => {
|
|
3035
3026
|
settleNativeSimulatorReady = resolve;
|
|
3036
3027
|
});
|
|
3037
3028
|
const route = parseRoute(simulatorUrl);
|
|
3038
|
-
const partition = miniappPartition(route?.appId);
|
|
3029
|
+
const partition = miniappPartition(route?.appId, ctx.workspace?.getProjectPath());
|
|
3039
3030
|
configureMiniappSession(partition);
|
|
3040
3031
|
const view = new WebContentsView2({
|
|
3041
3032
|
webPreferences: {
|
|
@@ -3145,22 +3136,7 @@ function createViewManager(ctx) {
|
|
|
3145
3136
|
stopElementsForward = null;
|
|
3146
3137
|
ctx.networkForward?.detachSimulator();
|
|
3147
3138
|
ctx.networkForward?.setDevtoolsHost(null);
|
|
3148
|
-
|
|
3149
|
-
if (nativeSimulatorViewAdded && !ctx.windows.mainWindow.isDestroyed()) {
|
|
3150
|
-
try {
|
|
3151
|
-
ctx.windows.mainWindow.contentView.removeChildView(nativeSimulatorView);
|
|
3152
|
-
} catch {
|
|
3153
|
-
}
|
|
3154
|
-
}
|
|
3155
|
-
try {
|
|
3156
|
-
if (!nativeSimulatorView.webContents.isDestroyed()) {
|
|
3157
|
-
nativeSimulatorView.webContents.close();
|
|
3158
|
-
}
|
|
3159
|
-
} catch {
|
|
3160
|
-
}
|
|
3161
|
-
nativeSimulatorView = null;
|
|
3162
|
-
nativeSimulatorViewAdded = false;
|
|
3163
|
-
}
|
|
3139
|
+
tearDownNativeSimulatorView("close");
|
|
3164
3140
|
hidePopover();
|
|
3165
3141
|
destroyViewInternal(settingsView);
|
|
3166
3142
|
settingsView = null;
|
|
@@ -3616,13 +3592,38 @@ function getSimulatorServicewechatReferer() {
|
|
|
3616
3592
|
return cachedRefererUrl;
|
|
3617
3593
|
}
|
|
3618
3594
|
|
|
3595
|
+
// src/main/services/workspace/op-lock.ts
|
|
3596
|
+
function createOpLock() {
|
|
3597
|
+
let chain = Promise.resolve();
|
|
3598
|
+
let requestSeq = 0;
|
|
3599
|
+
let ownerSeq = 0;
|
|
3600
|
+
return {
|
|
3601
|
+
nextSeq: () => ++requestSeq,
|
|
3602
|
+
takeOwnership: (seq) => {
|
|
3603
|
+
if (seq > ownerSeq) ownerSeq = seq;
|
|
3604
|
+
},
|
|
3605
|
+
isOwner: (seq) => seq === ownerSeq,
|
|
3606
|
+
acquire: () => {
|
|
3607
|
+
let release;
|
|
3608
|
+
const next = new Promise((resolve) => {
|
|
3609
|
+
release = resolve;
|
|
3610
|
+
});
|
|
3611
|
+
const prior = chain;
|
|
3612
|
+
chain = prior.then(() => next);
|
|
3613
|
+
return prior.then(() => release);
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
|
|
3619
3618
|
// src/main/services/workspace/workspace-service.ts
|
|
3620
|
-
var SessionAppInfoSchema = z2.looseObject({ appId: z2.string() });
|
|
3619
|
+
var SessionAppInfoSchema = z2.looseObject({ appId: z2.string().min(1) });
|
|
3621
3620
|
function createWorkspaceService(ctx) {
|
|
3622
3621
|
let currentSession = null;
|
|
3623
3622
|
let currentProjectPath = "";
|
|
3623
|
+
let closing = false;
|
|
3624
3624
|
let lastClosedProjectPath = "";
|
|
3625
3625
|
let logGeneration = 0;
|
|
3626
|
+
const opLock = createOpLock();
|
|
3626
3627
|
function sendStatus(status2, message, hotReload) {
|
|
3627
3628
|
ctx.notify.projectStatus(hotReload ? { status: status2, message, hotReload: true } : { status: status2, message });
|
|
3628
3629
|
}
|
|
@@ -3648,6 +3649,65 @@ function createWorkspaceService(ctx) {
|
|
|
3648
3649
|
console.error("[workspace] session close failed:", err);
|
|
3649
3650
|
}
|
|
3650
3651
|
}
|
|
3652
|
+
async function runOpenTeardown(mySeq) {
|
|
3653
|
+
const release = await opLock.acquire();
|
|
3654
|
+
try {
|
|
3655
|
+
if (!opLock.isOwner(mySeq)) return true;
|
|
3656
|
+
clearSimulatorServicewechatReferer();
|
|
3657
|
+
if (currentSession !== null) {
|
|
3658
|
+
ctx.views.detachWorkbench();
|
|
3659
|
+
ctx.views.detachSimulator();
|
|
3660
|
+
}
|
|
3661
|
+
logGeneration++;
|
|
3662
|
+
await disposeSession();
|
|
3663
|
+
currentProjectPath = "";
|
|
3664
|
+
lastClosedProjectPath = "";
|
|
3665
|
+
return false;
|
|
3666
|
+
} finally {
|
|
3667
|
+
release();
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
async function runOpenCommit(mySeq, session5, projectPath) {
|
|
3671
|
+
const release = await opLock.acquire();
|
|
3672
|
+
try {
|
|
3673
|
+
if (!opLock.isOwner(mySeq)) return false;
|
|
3674
|
+
currentSession = session5;
|
|
3675
|
+
currentProjectPath = projectPath;
|
|
3676
|
+
return true;
|
|
3677
|
+
} finally {
|
|
3678
|
+
release();
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
async function runCompile(projectPath, sessionGeneration) {
|
|
3682
|
+
const { compile } = loadWorkbenchSettings();
|
|
3683
|
+
try {
|
|
3684
|
+
const session5 = await ctx.adapter.openProject({
|
|
3685
|
+
projectPath,
|
|
3686
|
+
sourcemap: true,
|
|
3687
|
+
fileTypes: ctx.fileTypes,
|
|
3688
|
+
watch: compile.watch,
|
|
3689
|
+
onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
|
|
3690
|
+
onBuildError: (err) => sendStatus("error", String(err)),
|
|
3691
|
+
onLog: (entry) => {
|
|
3692
|
+
if (sessionGeneration !== logGeneration) return;
|
|
3693
|
+
ctx.notify.compileLog({ stream: entry.stream, text: entry.text, at: Date.now() });
|
|
3694
|
+
}
|
|
3695
|
+
});
|
|
3696
|
+
return { session: session5 };
|
|
3697
|
+
} catch (err) {
|
|
3698
|
+
clearSimulatorServicewechatReferer();
|
|
3699
|
+
return { error: String(err) };
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
async function rejectInvalidAppId(session5) {
|
|
3703
|
+
if (SessionAppInfoSchema.safeParse(session5.appInfo).success) return null;
|
|
3704
|
+
try {
|
|
3705
|
+
await session5.close();
|
|
3706
|
+
} catch (closeErr) {
|
|
3707
|
+
console.warn("[workspace] closing appId-less adapter session failed (non-fatal):", closeErr);
|
|
3708
|
+
}
|
|
3709
|
+
return "adapter returned session.appInfo without a string appId \u2014 the CompilationAdapter must supply appInfo.appId";
|
|
3710
|
+
}
|
|
3651
3711
|
function applyRefererFromSession(session5) {
|
|
3652
3712
|
const appInfo = session5.appInfo;
|
|
3653
3713
|
if (appInfo && typeof appInfo.appId === "string" && appInfo.appId.length > 0) {
|
|
@@ -3670,6 +3730,7 @@ function createWorkspaceService(ctx) {
|
|
|
3670
3730
|
},
|
|
3671
3731
|
validateProjectDir: async (dirPath) => provider.validateProjectDir ? provider.validateProjectDir(dirPath) : null,
|
|
3672
3732
|
async openProject(projectPath) {
|
|
3733
|
+
const mySeq = opLock.nextSeq();
|
|
3673
3734
|
if (ctx.onBeforeOpenProject) {
|
|
3674
3735
|
try {
|
|
3675
3736
|
await ctx.onBeforeOpenProject(projectPath);
|
|
@@ -3679,65 +3740,36 @@ function createWorkspaceService(ctx) {
|
|
|
3679
3740
|
return { success: false, error };
|
|
3680
3741
|
}
|
|
3681
3742
|
}
|
|
3682
|
-
|
|
3683
|
-
if (
|
|
3684
|
-
|
|
3743
|
+
opLock.takeOwnership(mySeq);
|
|
3744
|
+
if (await runOpenTeardown(mySeq)) {
|
|
3745
|
+
return { success: false, error: "superseded by a newer project open/close" };
|
|
3685
3746
|
}
|
|
3686
|
-
logGeneration++;
|
|
3687
|
-
await disposeSession();
|
|
3688
|
-
currentProjectPath = "";
|
|
3689
|
-
lastClosedProjectPath = "";
|
|
3690
3747
|
const dirError = provider.validateProjectDir ? await provider.validateProjectDir(projectPath) : null;
|
|
3691
3748
|
if (dirError) {
|
|
3692
3749
|
sendStatus("error", dirError);
|
|
3693
3750
|
return { success: false, error: dirError };
|
|
3694
3751
|
}
|
|
3695
3752
|
sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
|
|
3696
|
-
const { compile } = loadWorkbenchSettings();
|
|
3697
3753
|
const sessionGeneration = ++logGeneration;
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
// (a closed/replaced session's worker flushing its buffers) are
|
|
3711
|
-
// dropped via the generation check.
|
|
3712
|
-
onLog: (entry) => {
|
|
3713
|
-
if (sessionGeneration !== logGeneration) return;
|
|
3714
|
-
ctx.notify.compileLog({
|
|
3715
|
-
stream: entry.stream,
|
|
3716
|
-
text: entry.text,
|
|
3717
|
-
at: Date.now()
|
|
3718
|
-
});
|
|
3719
|
-
}
|
|
3720
|
-
});
|
|
3721
|
-
} catch (err) {
|
|
3722
|
-
clearSimulatorServicewechatReferer();
|
|
3723
|
-
sendStatus("error", String(err));
|
|
3724
|
-
return { success: false, error: String(err) };
|
|
3725
|
-
}
|
|
3726
|
-
if (!SessionAppInfoSchema.safeParse(session5.appInfo).success) {
|
|
3754
|
+
const compiled = await runCompile(projectPath, sessionGeneration);
|
|
3755
|
+
if ("error" in compiled) {
|
|
3756
|
+
sendStatus("error", compiled.error);
|
|
3757
|
+
return { success: false, error: compiled.error };
|
|
3758
|
+
}
|
|
3759
|
+
const session5 = compiled.session;
|
|
3760
|
+
const appIdError = await rejectInvalidAppId(session5);
|
|
3761
|
+
if (appIdError) {
|
|
3762
|
+
sendStatus("error", appIdError);
|
|
3763
|
+
return { success: false, error: appIdError };
|
|
3764
|
+
}
|
|
3765
|
+
if (!await runOpenCommit(mySeq, session5, projectPath)) {
|
|
3727
3766
|
try {
|
|
3728
3767
|
await session5.close();
|
|
3729
3768
|
} catch (closeErr) {
|
|
3730
|
-
console.warn(
|
|
3731
|
-
"[workspace] closing appId-less adapter session failed (non-fatal):",
|
|
3732
|
-
closeErr
|
|
3733
|
-
);
|
|
3769
|
+
console.warn("[workspace] closing superseded open session failed (non-fatal):", closeErr);
|
|
3734
3770
|
}
|
|
3735
|
-
|
|
3736
|
-
sendStatus("error", error);
|
|
3737
|
-
return { success: false, error };
|
|
3771
|
+
return { success: false, error: "superseded by a newer project open/close" };
|
|
3738
3772
|
}
|
|
3739
|
-
currentSession = session5;
|
|
3740
|
-
currentProjectPath = projectPath;
|
|
3741
3773
|
bestEffort("updateLastOpened", () => {
|
|
3742
3774
|
if (provider.updateLastOpened) provider.updateLastOpened(projectPath);
|
|
3743
3775
|
});
|
|
@@ -3750,26 +3782,43 @@ function createWorkspaceService(ctx) {
|
|
|
3750
3782
|
};
|
|
3751
3783
|
},
|
|
3752
3784
|
async closeProject() {
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
await
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3785
|
+
const mySeq = opLock.nextSeq();
|
|
3786
|
+
opLock.takeOwnership(mySeq);
|
|
3787
|
+
const release = await opLock.acquire();
|
|
3788
|
+
try {
|
|
3789
|
+
if (!opLock.isOwner(mySeq)) return;
|
|
3790
|
+
closing = true;
|
|
3791
|
+
logGeneration++;
|
|
3792
|
+
clearSimulatorServicewechatReferer();
|
|
3793
|
+
await disposeSession();
|
|
3794
|
+
if (currentProjectPath !== "") lastClosedProjectPath = currentProjectPath;
|
|
3795
|
+
currentProjectPath = "";
|
|
3796
|
+
ctx.views.disposeAll();
|
|
3797
|
+
} finally {
|
|
3798
|
+
closing = false;
|
|
3799
|
+
release();
|
|
3800
|
+
}
|
|
3759
3801
|
},
|
|
3760
3802
|
getSession: () => currentSession,
|
|
3761
3803
|
getProjectPath: () => currentProjectPath,
|
|
3762
3804
|
getLastClosedProjectPath: () => lastClosedProjectPath,
|
|
3763
3805
|
hasActiveSession: () => currentSession !== null,
|
|
3806
|
+
isClosing: () => closing,
|
|
3764
3807
|
async captureThumbnail(projectPath) {
|
|
3765
3808
|
if (!currentSession || projectPath !== currentProjectPath) return null;
|
|
3766
|
-
const
|
|
3767
|
-
if (!
|
|
3809
|
+
const simulatorWc = ctx.views.getSimulatorWebContents();
|
|
3810
|
+
if (!simulatorWc) return null;
|
|
3768
3811
|
if (ctx.views.getSimulatorProjectPath() !== projectPath) return null;
|
|
3769
3812
|
const session5 = currentSession;
|
|
3813
|
+
const bridge = ctx.bridge;
|
|
3814
|
+
const nativeHost = bridge?.isNativeHost() ?? false;
|
|
3815
|
+
const renderGuest = nativeHost ? bridge?.getActiveRenderWc() ?? null : null;
|
|
3816
|
+
if (nativeHost && !renderGuest) return null;
|
|
3817
|
+
const captureTarget = renderGuest ?? simulatorWc;
|
|
3818
|
+
if (captureTarget.isDestroyed()) return null;
|
|
3770
3819
|
try {
|
|
3771
|
-
const image = await
|
|
3772
|
-
if (currentSession !== session5 || currentProjectPath !== projectPath || ctx.views.getSimulatorWebContents() !==
|
|
3820
|
+
const image = await captureTarget.capturePage();
|
|
3821
|
+
if (currentSession !== session5 || currentProjectPath !== projectPath || ctx.views.getSimulatorWebContents() !== simulatorWc || ctx.views.getSimulatorProjectPath() !== projectPath || (nativeHost ? bridge?.getActiveRenderWc() ?? null : simulatorWc) !== captureTarget) {
|
|
3773
3822
|
return null;
|
|
3774
3823
|
}
|
|
3775
3824
|
const dataUrl = `data:image/png;base64,${image.toPNG().toString("base64")}`;
|
|
@@ -3900,6 +3949,7 @@ function createWorkbenchContext(opts) {
|
|
|
3900
3949
|
preloadPath: opts.preloadPath,
|
|
3901
3950
|
rendererDir: opts.rendererDir,
|
|
3902
3951
|
apiNamespaces: opts.apiNamespaces ?? [],
|
|
3952
|
+
fileTypes: opts.fileTypes ?? {},
|
|
3903
3953
|
appName: opts.appName ?? "Dimina DevTools",
|
|
3904
3954
|
brandingProvider: opts.brandingProvider
|
|
3905
3955
|
};
|
|
@@ -4702,12 +4752,15 @@ function navigateServiceHost(win, url) {
|
|
|
4702
4752
|
}
|
|
4703
4753
|
return loaded;
|
|
4704
4754
|
}
|
|
4705
|
-
function serviceHostSpec(appId) {
|
|
4755
|
+
function serviceHostSpec(appId, projectPath) {
|
|
4706
4756
|
return {
|
|
4707
4757
|
// Per-project partition when an appId is known (so this project's service
|
|
4708
4758
|
// host shares storage ONLY with its own render side); the shared partition
|
|
4709
4759
|
// for the pre-warm pool's default spec (no appId — see KNOWN BLOCKER below).
|
|
4710
|
-
|
|
4760
|
+
// projectPath joins the key so same-appId/different-path projects isolate;
|
|
4761
|
+
// it must match the simulator WCV's partition (view-manager passes the same
|
|
4762
|
+
// (appId, projectPath)).
|
|
4763
|
+
partition: appId ? miniappPartition(appId, projectPath) : SERVICE_HOST_PARTITION,
|
|
4711
4764
|
preloadPath: serviceHostPreloadPath,
|
|
4712
4765
|
size: { width: 980, height: 720 },
|
|
4713
4766
|
contextIsolation: false,
|
|
@@ -4717,7 +4770,7 @@ function serviceHostSpec(appId) {
|
|
|
4717
4770
|
};
|
|
4718
4771
|
}
|
|
4719
4772
|
function createServiceHostWindow(opts) {
|
|
4720
|
-
const win = constructServiceHostWindow({ appId: opts.appId, partition: miniappPartition(opts.appId) });
|
|
4773
|
+
const win = constructServiceHostWindow({ appId: opts.appId, partition: miniappPartition(opts.appId, opts.projectPath) });
|
|
4721
4774
|
void navigateServiceHost(win, buildServiceHostSpawnUrl(opts));
|
|
4722
4775
|
return win;
|
|
4723
4776
|
}
|
|
@@ -5625,9 +5678,14 @@ function resolveCurrentApp(state, ctx, appId) {
|
|
|
5625
5678
|
for (const ap of state.appSessions.values()) if (ap.appId === activeAppId) match = ap;
|
|
5626
5679
|
if (match) return match;
|
|
5627
5680
|
}
|
|
5681
|
+
if (ctx.workspace?.isClosing?.()) return void 0;
|
|
5628
5682
|
let last;
|
|
5629
|
-
|
|
5630
|
-
|
|
5683
|
+
const distinctAppIds = /* @__PURE__ */ new Set();
|
|
5684
|
+
for (const ap of state.appSessions.values()) {
|
|
5685
|
+
last = ap;
|
|
5686
|
+
distinctAppIds.add(ap.appId);
|
|
5687
|
+
}
|
|
5688
|
+
return distinctAppIds.size <= 1 ? last : void 0;
|
|
5631
5689
|
}
|
|
5632
5690
|
function installBridgeRouter(ctx) {
|
|
5633
5691
|
const state = {
|
|
@@ -5731,6 +5789,14 @@ function installBridgeRouter(ctx) {
|
|
|
5731
5789
|
}
|
|
5732
5790
|
}
|
|
5733
5791
|
},
|
|
5792
|
+
disposeSessionsForSimulator: (simulatorWcId) => {
|
|
5793
|
+
const ids = [];
|
|
5794
|
+
for (const [id, ap] of state.appSessions) {
|
|
5795
|
+
if (ap.simulatorWc.id === simulatorWcId) ids.push(id);
|
|
5796
|
+
}
|
|
5797
|
+
return Promise.all(ids.map((id) => disposeAppSession(state, id))).then(() => {
|
|
5798
|
+
});
|
|
5799
|
+
},
|
|
5734
5800
|
debugTap: state.debugTap
|
|
5735
5801
|
};
|
|
5736
5802
|
ctx.bridge = bridgeHandle;
|
|
@@ -5944,6 +6010,13 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
5944
6010
|
serviceWindow = createServiceHostWindow({
|
|
5945
6011
|
bridgeId,
|
|
5946
6012
|
appId,
|
|
6013
|
+
// Same (appId, projectPath) pair the simulator WCV uses, so this project's
|
|
6014
|
+
// service host and render guests land on ONE partition while a different
|
|
6015
|
+
// project with the same appId is isolated. Use the path captured at spawn
|
|
6016
|
+
// ENTRY (workspaceProjectPath), not a re-read: a project switch during the
|
|
6017
|
+
// awaits above would otherwise give the service host a different path than
|
|
6018
|
+
// the simulator WCV was built with, splitting the partition.
|
|
6019
|
+
projectPath: workspaceProjectPath || void 0,
|
|
5947
6020
|
pagePath,
|
|
5948
6021
|
pkgRoot,
|
|
5949
6022
|
root,
|
|
@@ -6004,6 +6077,9 @@ async function handleSpawn(state, ctx, event, opts) {
|
|
|
6004
6077
|
void disposeAppSession(state, appSessionId);
|
|
6005
6078
|
};
|
|
6006
6079
|
simulatorWc.once("destroyed", onSimulatorDestroyed);
|
|
6080
|
+
if (simulatorWc.isDestroyed()) {
|
|
6081
|
+
void disposeAppSession(state, appSessionId);
|
|
6082
|
+
}
|
|
6007
6083
|
if (usedPool) {
|
|
6008
6084
|
const bootOnServiceLoad = () => {
|
|
6009
6085
|
if (serviceWindow.isDestroyed()) return;
|
|
@@ -6518,6 +6594,10 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
|
|
|
6518
6594
|
for (const page of ap.pages.values()) {
|
|
6519
6595
|
if (page.renderWc && !page.renderWc.isDestroyed()) {
|
|
6520
6596
|
state.wcIdToBridgeId.delete(page.renderWc.id);
|
|
6597
|
+
try {
|
|
6598
|
+
page.renderWc.close();
|
|
6599
|
+
} catch {
|
|
6600
|
+
}
|
|
6521
6601
|
}
|
|
6522
6602
|
state.pageSessions.delete(page.bridgeId);
|
|
6523
6603
|
}
|
|
@@ -9792,6 +9872,10 @@ async function startWorkbenchCoiServer(options) {
|
|
|
9792
9872
|
});
|
|
9793
9873
|
return;
|
|
9794
9874
|
}
|
|
9875
|
+
if (url.pathname === "/__filetypes") {
|
|
9876
|
+
jsonRes(res, 200, options.getFileTypes?.() ?? {});
|
|
9877
|
+
return;
|
|
9878
|
+
}
|
|
9795
9879
|
if (url.pathname.startsWith("/__contrib/")) {
|
|
9796
9880
|
if (!contribRoot) {
|
|
9797
9881
|
res.writeHead(404);
|
|
@@ -10017,6 +10101,7 @@ function createContext(config, mainWindow, rendererDir2) {
|
|
|
10017
10101
|
rendererDir: rendererDir2,
|
|
10018
10102
|
appName: config.appName,
|
|
10019
10103
|
apiNamespaces: config.apiNamespaces,
|
|
10104
|
+
fileTypes: config.fileTypes,
|
|
10020
10105
|
brandingProvider: config.brandingProvider,
|
|
10021
10106
|
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
10022
10107
|
// are structurally compatible with the main-process equivalents —
|
|
@@ -10291,7 +10376,8 @@ async function createDevtoolsRuntime(config = {}) {
|
|
|
10291
10376
|
const coiServer = await startWorkbenchCoiServer({
|
|
10292
10377
|
rootDir: bundleDir,
|
|
10293
10378
|
getProjectRoot: () => context.workspace.getProjectPath(),
|
|
10294
|
-
extensionsDir: config.editorViewConfig?.extensionsDir
|
|
10379
|
+
extensionsDir: config.editorViewConfig?.extensionsDir,
|
|
10380
|
+
getFileTypes: () => context.fileTypes
|
|
10295
10381
|
});
|
|
10296
10382
|
context.registry.add(() => coiServer.close());
|
|
10297
10383
|
context.registry.add(() => context.views.detachWorkbench());
|
|
@@ -49,6 +49,27 @@ export interface BridgeRouterHandle {
|
|
|
49
49
|
getDevice(): NativeDeviceInfo | null;
|
|
50
50
|
/** Cache the selected device + push DEVICE_CHANGE to the live simulator WC(s). */
|
|
51
51
|
setDevice(device: NativeDeviceInfo): void;
|
|
52
|
+
/**
|
|
53
|
+
* Deterministically tear down every app session bound to the given simulator
|
|
54
|
+
* WebContentsView id (project close / DeviceShell respawn). The session
|
|
55
|
+
* mappings are cleared, the render-host guest webContents are closed, and the
|
|
56
|
+
* service-host window is closed/released — synchronously, instead of waiting
|
|
57
|
+
* on the WCV's async `'destroyed'` cascade — so the next project never
|
|
58
|
+
* re-resolves or screenshots the outgoing project's render guest. Idempotent
|
|
59
|
+
* with the `simulatorWc.once('destroyed')` hook (`disposeAppSession`
|
|
60
|
+
* early-returns once a session is gone). Optional so partial test mocks of the
|
|
61
|
+
* handle need not stub it.
|
|
62
|
+
*
|
|
63
|
+
* The synchronous prefix of the underlying disposeAppSession clears every map
|
|
64
|
+
* + closes the render guests + the service window before it awaits, so the
|
|
65
|
+
* bridge is clean the moment this is called. The returned promise resolves
|
|
66
|
+
* after the async tail (pool.release / resourceServer.close) so the lifecycle
|
|
67
|
+
* owner can sequence a reopen after full teardown. NOTE: disposeAppSession
|
|
68
|
+
* catches and logs pool/resource-release failures internally, so the promise
|
|
69
|
+
* resolves rather than rejecting on those — it is a completion signal, not an
|
|
70
|
+
* error channel.
|
|
71
|
+
*/
|
|
72
|
+
disposeSessionsForSimulator?(simulatorWcId: number): Promise<void>;
|
|
52
73
|
/**
|
|
53
74
|
* The flag-gated debugTap (§7) over the bridge message stream. Exposed so a
|
|
54
75
|
* hidden devtools panel / automation can read `.entries()` when
|