@dimina-kit/devtools 0.3.1 → 0.3.2-dev.20260522110734
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +298 -133
- package/dist/main/api.d.ts +6 -2
- package/dist/main/api.js +5 -2
- package/dist/main/app/app.d.ts +1 -1
- package/dist/main/app/app.js +84 -36
- package/dist/main/app/bootstrap.js +2 -1
- package/dist/main/app/launch.d.ts +2 -2
- package/dist/main/app/launch.js +10 -8
- package/dist/main/index.bundle.js +1567 -477
- package/dist/main/ipc/app.d.ts +2 -1
- package/dist/main/ipc/app.js +5 -4
- package/dist/main/ipc/index.d.ts +5 -4
- package/dist/main/ipc/index.js +5 -4
- package/dist/main/ipc/panels.d.ts +2 -1
- package/dist/main/ipc/panels.js +16 -13
- package/dist/main/ipc/popover.d.ts +4 -1
- package/dist/main/ipc/popover.js +15 -5
- package/dist/main/ipc/projects.d.ts +6 -1
- package/dist/main/ipc/projects.js +127 -6
- package/dist/main/ipc/session.d.ts +4 -1
- package/dist/main/ipc/session.js +34 -6
- package/dist/main/ipc/settings.d.ts +4 -1
- package/dist/main/ipc/settings.js +51 -23
- package/dist/main/ipc/simulator-module.d.ts +8 -0
- package/dist/main/ipc/simulator-module.js +19 -0
- package/dist/main/ipc/simulator.d.ts +2 -1
- package/dist/main/ipc/simulator.js +23 -18
- package/dist/main/ipc/toolbar.d.ts +2 -1
- package/dist/main/ipc/toolbar.js +3 -2
- package/dist/main/menu/index.js +11 -0
- package/dist/main/services/automation/exec.d.ts +2 -0
- package/dist/main/services/automation/exec.js +7 -9
- package/dist/main/services/automation/handlers/app.js +37 -51
- package/dist/main/services/automation/handlers/element.js +27 -41
- package/dist/main/services/automation/index.js +88 -13
- package/dist/main/services/default-adapter.js +3 -3
- package/dist/main/services/mcp/index.d.ts +1 -0
- package/dist/main/services/mcp/index.js +1 -0
- package/dist/main/services/mcp/server.d.ts +2 -1
- package/dist/main/services/mcp/server.js +10 -0
- package/dist/main/services/mcp/status.d.ts +10 -0
- package/dist/main/services/mcp/status.js +14 -0
- package/dist/main/services/mcp/target-manager.js +2 -1
- package/dist/main/services/module.d.ts +18 -0
- package/dist/main/services/module.js +2 -0
- package/dist/main/services/notifications/renderer-notifier.d.ts +5 -3
- package/dist/main/services/notifications/renderer-notifier.js +3 -6
- package/dist/main/services/projects/builtin-templates.d.ts +3 -0
- package/dist/main/services/projects/builtin-templates.js +24 -0
- package/dist/main/services/projects/create-project-service.d.ts +7 -0
- package/dist/main/services/projects/create-project-service.js +84 -0
- package/dist/main/services/projects/index.d.ts +7 -0
- package/dist/main/services/projects/index.js +9 -0
- package/dist/main/services/projects/local-provider.d.ts +3 -0
- package/dist/main/services/projects/local-provider.js +25 -0
- package/dist/main/services/projects/project-repository.d.ts +1 -1
- package/dist/main/services/projects/project-repository.js +3 -0
- package/dist/main/services/projects/templates.d.ts +21 -0
- package/dist/main/services/projects/templates.js +39 -0
- package/dist/main/services/projects/thumbnail.d.ts +8 -0
- package/dist/main/services/projects/thumbnail.js +37 -0
- package/dist/main/services/projects/types.d.ts +118 -0
- package/dist/main/services/projects/types.js +19 -0
- package/dist/main/services/settings/index.d.ts +11 -0
- package/dist/main/services/settings/index.js +18 -2
- package/dist/main/services/simulator/custom-apis.d.ts +10 -0
- package/dist/main/services/simulator/custom-apis.js +26 -0
- package/dist/main/services/simulator-storage/index.d.ts +22 -1
- package/dist/main/services/simulator-storage/index.js +334 -97
- package/dist/main/services/update/update-manager.d.ts +16 -1
- package/dist/main/services/update/update-manager.js +18 -15
- package/dist/main/services/views/view-manager.d.ts +7 -7
- package/dist/main/services/views/view-manager.js +53 -29
- package/dist/main/services/window-service.d.ts +24 -0
- package/dist/main/services/window-service.js +26 -0
- package/dist/main/services/workbench-context.d.ts +49 -2
- package/dist/main/services/workbench-context.js +15 -2
- package/dist/main/services/workspace/workspace-service.d.ts +9 -6
- package/dist/main/services/workspace/workspace-service.js +71 -11
- package/dist/main/simulator-apis.d.ts +23 -0
- package/dist/main/simulator-apis.js +24 -0
- package/dist/main/utils/disposable.d.ts +13 -0
- package/dist/main/utils/disposable.js +60 -0
- package/dist/main/utils/ipc-registry.d.ts +39 -0
- package/dist/main/utils/ipc-registry.js +109 -0
- package/dist/main/utils/ipc-schema.d.ts +28 -0
- package/dist/main/utils/ipc-schema.js +38 -0
- package/dist/main/utils/paths.d.ts +6 -0
- package/dist/main/utils/paths.js +6 -0
- package/dist/main/utils/sender-policy.d.ts +25 -0
- package/dist/main/utils/sender-policy.js +44 -0
- package/dist/main/utils/theme.d.ts +24 -0
- package/dist/main/utils/theme.js +38 -1
- package/dist/main/windows/main-window/create.d.ts +6 -0
- package/dist/main/windows/main-window/create.js +66 -10
- package/dist/main/windows/main-window/events.d.ts +2 -1
- package/dist/main/windows/main-window/events.js +25 -2
- package/dist/main/windows/navigation-hardening.d.ts +24 -0
- package/dist/main/windows/navigation-hardening.js +55 -0
- package/dist/main/windows/settings-window/create.js +11 -1
- package/dist/preload/index.d.ts +22 -0
- package/dist/preload/index.js +19 -0
- package/dist/preload/instrumentation/app-data.d.ts +32 -2
- package/dist/preload/instrumentation/app-data.js +253 -79
- package/dist/preload/instrumentation/index.d.ts +2 -2
- package/dist/preload/instrumentation/index.js +2 -2
- package/dist/preload/instrumentation/wxml.d.ts +16 -4
- package/dist/preload/instrumentation/wxml.js +188 -178
- package/dist/preload/miniapp-snapshot/host.d.ts +12 -0
- package/dist/preload/miniapp-snapshot/host.js +117 -0
- package/dist/preload/miniapp-snapshot/types.d.ts +62 -0
- package/dist/preload/miniapp-snapshot/types.js +15 -0
- package/dist/preload/runtime/bridge.js +5 -24
- package/dist/preload/runtime/custom-apis.d.ts +6 -0
- package/dist/preload/runtime/custom-apis.js +43 -0
- package/dist/preload/runtime/host.d.ts +1 -0
- package/dist/preload/runtime/host.js +13 -0
- package/dist/preload/shared/expose.d.ts +15 -0
- package/dist/preload/shared/expose.js +33 -0
- package/dist/preload/shared/page-iframe.d.ts +3 -0
- package/dist/preload/shared/page-iframe.js +6 -0
- package/dist/preload/shared/types.d.ts +2 -0
- package/dist/preload/windows/main.cjs +47 -0
- package/dist/preload/windows/main.cjs.map +7 -0
- package/dist/preload/windows/simulator.js +501 -272
- package/dist/renderer/assets/constants-DI382mTP.js +2 -0
- package/dist/renderer/assets/index-D8VFYBh-.js +46 -0
- package/dist/renderer/assets/{input-DxfYnzX_.js → input-CoWcfa8u.js} +2 -2
- package/dist/renderer/assets/ipc-transport-Cd2xPrsa.js +9 -0
- package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +1 -0
- package/dist/renderer/assets/popover-Nq5gT9DO.js +2 -0
- package/dist/renderer/assets/select-By5KH-UG.js +2 -0
- package/dist/renderer/assets/{settings-DteGraEU.js → settings-Dj_N70qR.js} +2 -2
- package/dist/renderer/assets/settings-api-Do0_Smq1.js +2 -0
- package/dist/renderer/assets/workbenchSettings-CazSLGYt.js +8 -0
- package/dist/renderer/entries/main/index.html +13 -5
- package/dist/renderer/entries/popover/index.html +8 -5
- package/dist/renderer/entries/settings/index.html +7 -5
- package/dist/renderer/entries/workbench-settings/index.html +7 -4
- package/dist/shared/ipc-channels.d.ts +46 -13
- package/dist/shared/ipc-channels.js +60 -14
- package/dist/shared/ipc-schemas.d.ts +118 -0
- package/dist/shared/ipc-schemas.js +133 -0
- package/dist/shared/simulator-route.d.ts +72 -0
- package/dist/shared/simulator-route.js +169 -0
- package/dist/shared/types.d.ts +82 -8
- package/dist/simulator/assets/simulator-CZc88Jeh.js +9 -0
- package/dist/simulator/simulator.html +6 -43
- package/package.json +16 -27
- package/templates/blank/app.js +6 -0
- package/templates/blank/app.json +11 -0
- package/templates/blank/app.wxss +5 -0
- package/templates/blank/pages/index/index.js +6 -0
- package/templates/blank/pages/index/index.json +3 -0
- package/templates/blank/pages/index/index.wxml +3 -0
- package/templates/blank/pages/index/index.wxss +11 -0
- package/templates/blank/project.config.json +13 -0
- package/templates/blank/sitemap.json +9 -0
- package/templates/taro-todo/app.js +1 -0
- package/templates/taro-todo/app.json +1 -0
- package/templates/taro-todo/app.wxss +1 -0
- package/templates/taro-todo/base.wxml +851 -0
- package/templates/taro-todo/common.js +1 -0
- package/templates/taro-todo/comp.js +1 -0
- package/templates/taro-todo/comp.json +1 -0
- package/templates/taro-todo/comp.wxml +2 -0
- package/templates/taro-todo/custom-wrapper.js +1 -0
- package/templates/taro-todo/custom-wrapper.json +1 -0
- package/templates/taro-todo/custom-wrapper.wxml +4 -0
- package/templates/taro-todo/pages/index/index.js +2 -0
- package/templates/taro-todo/pages/index/index.json +1 -0
- package/templates/taro-todo/pages/index/index.wxml +2 -0
- package/templates/taro-todo/pages/index/index.wxss +1 -0
- package/templates/taro-todo/project.config.json +44 -0
- package/templates/taro-todo/runtime.js +1 -0
- package/templates/taro-todo/taro.js +2 -0
- package/templates/taro-todo/utils.wxs +39 -0
- package/templates/taro-todo/vendors.js +2 -0
- package/dist/preload/instrumentation/disposable.d.ts +0 -11
- package/dist/preload/instrumentation/disposable.js +0 -15
- package/dist/preload/windows/main.d.ts +0 -6
- package/dist/preload/windows/main.js +0 -6
- package/dist/renderer/assets/index-D1p6crtf.js +0 -46
- package/dist/renderer/assets/ipc-transport-ClIvDcYY.js +0 -9
- package/dist/renderer/assets/ipc-transport-EiWxu_hG.css +0 -1
- package/dist/renderer/assets/popover-CtXcBJtM.js +0 -2
- package/dist/renderer/assets/select-DMfp49f1.js +0 -2
- package/dist/renderer/assets/settings-api-CY84UMpN.js +0 -2
- package/dist/renderer/assets/workbenchSettings-DjELLnSJ.js +0 -8
- package/dist/simulator/assets/simulator-C1eHXnuq.js +0 -9
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { WebContentsView, webContents } from 'electron';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { mainPreloadPath } from '../../utils/paths.js';
|
|
4
|
+
import { applyNavigationHardening } from '../../windows/navigation-hardening.js';
|
|
3
5
|
import * as layout from '../layout/index.js';
|
|
4
6
|
import { getDefaultTab } from '../workbench-context.js';
|
|
5
7
|
/**
|
|
@@ -22,9 +24,9 @@ export function createViewManager(ctx) {
|
|
|
22
24
|
function destroyViewInternal(view) {
|
|
23
25
|
if (!view)
|
|
24
26
|
return;
|
|
25
|
-
if (!ctx.mainWindow.isDestroyed()) {
|
|
27
|
+
if (!ctx.windows.mainWindow.isDestroyed()) {
|
|
26
28
|
try {
|
|
27
|
-
ctx.mainWindow.contentView.removeChildView(view);
|
|
29
|
+
ctx.windows.mainWindow.contentView.removeChildView(view);
|
|
28
30
|
}
|
|
29
31
|
catch { /* already removed */ }
|
|
30
32
|
}
|
|
@@ -36,21 +38,21 @@ export function createViewManager(ctx) {
|
|
|
36
38
|
catch { /* ignore */ }
|
|
37
39
|
}
|
|
38
40
|
function applySimulatorBounds(simWidth) {
|
|
39
|
-
if (!simulatorView || ctx.mainWindow.isDestroyed())
|
|
41
|
+
if (!simulatorView || ctx.windows.mainWindow.isDestroyed())
|
|
40
42
|
return;
|
|
41
|
-
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
43
|
+
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
42
44
|
simulatorView.setBounds(layout.computeSimulatorBounds(w, h, simWidth));
|
|
43
45
|
}
|
|
44
46
|
function applySettingsBounds() {
|
|
45
|
-
if (!settingsView || ctx.mainWindow.isDestroyed())
|
|
47
|
+
if (!settingsView || ctx.windows.mainWindow.isDestroyed())
|
|
46
48
|
return;
|
|
47
|
-
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
49
|
+
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
48
50
|
settingsView.setBounds(layout.computeSettingsBounds(w, h));
|
|
49
51
|
}
|
|
50
52
|
function applyPopoverBounds() {
|
|
51
|
-
if (!popoverView || ctx.mainWindow.isDestroyed())
|
|
53
|
+
if (!popoverView || ctx.windows.mainWindow.isDestroyed())
|
|
52
54
|
return;
|
|
53
|
-
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
55
|
+
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
54
56
|
popoverView.setBounds(layout.computePopoverBounds(w, h));
|
|
55
57
|
}
|
|
56
58
|
// ── ViewManager methods ─────────────────────────────────────────────────
|
|
@@ -103,7 +105,7 @@ export function createViewManager(ctx) {
|
|
|
103
105
|
`).catch(() => { });
|
|
104
106
|
});
|
|
105
107
|
if (getDefaultTab(ctx) === 'simulator') {
|
|
106
|
-
ctx.mainWindow.contentView.addChildView(simulatorView);
|
|
108
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
107
109
|
simulatorViewAdded = true;
|
|
108
110
|
applySimulatorBounds(simWidth);
|
|
109
111
|
}
|
|
@@ -124,7 +126,7 @@ export function createViewManager(ctx) {
|
|
|
124
126
|
if (!simulatorView)
|
|
125
127
|
return;
|
|
126
128
|
if (!simulatorViewAdded) {
|
|
127
|
-
ctx.mainWindow.contentView.addChildView(simulatorView);
|
|
129
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
128
130
|
simulatorViewAdded = true;
|
|
129
131
|
}
|
|
130
132
|
applySimulatorBounds(simWidth);
|
|
@@ -132,7 +134,7 @@ export function createViewManager(ctx) {
|
|
|
132
134
|
function hideSimulator() {
|
|
133
135
|
if (simulatorView && simulatorViewAdded) {
|
|
134
136
|
try {
|
|
135
|
-
ctx.mainWindow.contentView.removeChildView(simulatorView);
|
|
137
|
+
ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
|
|
136
138
|
}
|
|
137
139
|
catch (e) {
|
|
138
140
|
console.error('[workbench] hideSimulator error', e);
|
|
@@ -143,12 +145,20 @@ export function createViewManager(ctx) {
|
|
|
143
145
|
async function showSettings() {
|
|
144
146
|
if (!settingsView) {
|
|
145
147
|
settingsView = new WebContentsView({
|
|
146
|
-
webPreferences: {
|
|
148
|
+
webPreferences: {
|
|
149
|
+
nodeIntegration: false,
|
|
150
|
+
contextIsolation: true,
|
|
151
|
+
sandbox: false,
|
|
152
|
+
preload: mainPreloadPath,
|
|
153
|
+
},
|
|
147
154
|
});
|
|
155
|
+
// Overlay loads mainPreloadPath, so the same navigation rules as the
|
|
156
|
+
// main window apply — see navigation-hardening.ts.
|
|
157
|
+
applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
|
|
148
158
|
await settingsView.webContents.loadFile(path.join(ctx.rendererDir, 'entries/settings/index.html'));
|
|
149
159
|
}
|
|
150
160
|
if (!settingsViewAdded) {
|
|
151
|
-
ctx.mainWindow.contentView.addChildView(settingsView);
|
|
161
|
+
ctx.windows.mainWindow.contentView.addChildView(settingsView);
|
|
152
162
|
settingsViewAdded = true;
|
|
153
163
|
}
|
|
154
164
|
applySettingsBounds();
|
|
@@ -156,7 +166,7 @@ export function createViewManager(ctx) {
|
|
|
156
166
|
function hideSettings() {
|
|
157
167
|
if (settingsView && settingsViewAdded) {
|
|
158
168
|
try {
|
|
159
|
-
ctx.mainWindow.contentView.removeChildView(settingsView);
|
|
169
|
+
ctx.windows.mainWindow.contentView.removeChildView(settingsView);
|
|
160
170
|
}
|
|
161
171
|
catch { /* ignore */ }
|
|
162
172
|
settingsViewAdded = false;
|
|
@@ -165,11 +175,18 @@ export function createViewManager(ctx) {
|
|
|
165
175
|
function showPopover(data) {
|
|
166
176
|
hidePopover();
|
|
167
177
|
const popover = new WebContentsView({
|
|
168
|
-
webPreferences: {
|
|
178
|
+
webPreferences: {
|
|
179
|
+
nodeIntegration: false,
|
|
180
|
+
contextIsolation: true,
|
|
181
|
+
sandbox: false,
|
|
182
|
+
preload: mainPreloadPath,
|
|
183
|
+
},
|
|
169
184
|
});
|
|
185
|
+
// Popover overlay loads mainPreloadPath — same navigation rules apply.
|
|
186
|
+
applyNavigationHardening(popover.webContents, ctx.rendererDir);
|
|
170
187
|
popoverView = popover;
|
|
171
188
|
popover.setBackgroundColor('#00000000');
|
|
172
|
-
ctx.mainWindow.contentView.addChildView(popover);
|
|
189
|
+
ctx.windows.mainWindow.contentView.addChildView(popover);
|
|
173
190
|
applyPopoverBounds();
|
|
174
191
|
popover.webContents.once('did-finish-load', () => {
|
|
175
192
|
ctx.notify.popoverInit(popover, data);
|
|
@@ -194,16 +211,6 @@ export function createViewManager(ctx) {
|
|
|
194
211
|
function disposeAll() {
|
|
195
212
|
detachSimulator();
|
|
196
213
|
}
|
|
197
|
-
function logChildViews(_label) {
|
|
198
|
-
// Debug helper — intentionally a no-op in production. Kept on the
|
|
199
|
-
// ViewManager interface so call sites and tests stay stable.
|
|
200
|
-
}
|
|
201
|
-
function positionSimulator(simWidth) {
|
|
202
|
-
applySimulatorBounds(simWidth);
|
|
203
|
-
}
|
|
204
|
-
function positionSettings() {
|
|
205
|
-
applySettingsBounds();
|
|
206
|
-
}
|
|
207
214
|
function resize(simWidth) {
|
|
208
215
|
lastSimWidth = simWidth;
|
|
209
216
|
if (simulatorViewAdded)
|
|
@@ -233,10 +240,13 @@ export function createViewManager(ctx) {
|
|
|
233
240
|
hidePopover,
|
|
234
241
|
repositionAll,
|
|
235
242
|
disposeAll,
|
|
236
|
-
logChildViews,
|
|
237
|
-
positionSimulator,
|
|
238
|
-
positionSettings,
|
|
239
243
|
getSimulatorWebContentsId: () => simulatorWebContentsId,
|
|
244
|
+
getSimulatorWebContents: () => {
|
|
245
|
+
if (simulatorWebContentsId == null)
|
|
246
|
+
return null;
|
|
247
|
+
const wc = webContents.fromId(simulatorWebContentsId);
|
|
248
|
+
return wc && !wc.isDestroyed() ? wc : null;
|
|
249
|
+
},
|
|
240
250
|
getLastSimWidth: () => lastSimWidth,
|
|
241
251
|
isSimulatorAdded: () => simulatorViewAdded,
|
|
242
252
|
hasSimulatorView: () => simulatorView !== null,
|
|
@@ -247,6 +257,20 @@ export function createViewManager(ctx) {
|
|
|
247
257
|
return null;
|
|
248
258
|
return settingsView.webContents;
|
|
249
259
|
},
|
|
260
|
+
getSettingsWebContentsId: () => {
|
|
261
|
+
if (!settingsView)
|
|
262
|
+
return null;
|
|
263
|
+
if (settingsView.webContents.isDestroyed())
|
|
264
|
+
return null;
|
|
265
|
+
return settingsView.webContents.id;
|
|
266
|
+
},
|
|
267
|
+
getPopoverWebContentsId: () => {
|
|
268
|
+
if (!popoverView)
|
|
269
|
+
return null;
|
|
270
|
+
if (popoverView.webContents.isDestroyed())
|
|
271
|
+
return null;
|
|
272
|
+
return popoverView.webContents.id;
|
|
273
|
+
},
|
|
250
274
|
resize,
|
|
251
275
|
setVisible,
|
|
252
276
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BrowserWindow } from 'electron';
|
|
2
|
+
/**
|
|
3
|
+
* Owns every top-level BrowserWindow the workbench is responsible for.
|
|
4
|
+
*
|
|
5
|
+
* This service is the single source of truth for the main and settings
|
|
6
|
+
* windows; callers reach them via `ctx.windows.mainWindow` /
|
|
7
|
+
* `ctx.windows.settingsWindow`.
|
|
8
|
+
*/
|
|
9
|
+
export interface WindowService {
|
|
10
|
+
/** The primary devtools window. Created during app bootstrap. */
|
|
11
|
+
readonly mainWindow: BrowserWindow;
|
|
12
|
+
/** Standalone workbench-settings BrowserWindow, or null when closed. */
|
|
13
|
+
readonly settingsWindow: BrowserWindow | null;
|
|
14
|
+
/** Update the tracked settings window (null when closed/destroyed). */
|
|
15
|
+
setSettingsWindow(win: BrowserWindow | null): void;
|
|
16
|
+
/** Close the settings window if it is open and clear the reference. */
|
|
17
|
+
closeSettingsWindow(): void;
|
|
18
|
+
/** True when `webContentsId` belongs to the (alive) main window renderer. */
|
|
19
|
+
isMainSender(webContentsId: number): boolean;
|
|
20
|
+
/** True when `webContentsId` belongs to the (alive) settings window renderer. */
|
|
21
|
+
isSettingsWindowSender(webContentsId: number): boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function createWindowService(mainWindow: BrowserWindow): WindowService;
|
|
24
|
+
//# sourceMappingURL=window-service.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function createWindowService(mainWindow) {
|
|
2
|
+
let settingsWin = null;
|
|
3
|
+
return {
|
|
4
|
+
get mainWindow() {
|
|
5
|
+
return mainWindow;
|
|
6
|
+
},
|
|
7
|
+
get settingsWindow() {
|
|
8
|
+
return settingsWin;
|
|
9
|
+
},
|
|
10
|
+
setSettingsWindow(win) {
|
|
11
|
+
settingsWin = win;
|
|
12
|
+
},
|
|
13
|
+
closeSettingsWindow() {
|
|
14
|
+
if (settingsWin && !settingsWin.isDestroyed())
|
|
15
|
+
settingsWin.close();
|
|
16
|
+
settingsWin = null;
|
|
17
|
+
},
|
|
18
|
+
isMainSender(id) {
|
|
19
|
+
return !mainWindow.isDestroyed() && mainWindow.webContents.id === id;
|
|
20
|
+
},
|
|
21
|
+
isSettingsWindowSender(id) {
|
|
22
|
+
return !!settingsWin && !settingsWin.isDestroyed() && settingsWin.webContents.id === id;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=window-service.js.map
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import type { BrowserWindow } from 'electron';
|
|
2
2
|
import type { CompilationAdapter } from '../../shared/types.js';
|
|
3
|
+
import { DisposableRegistry } from '../utils/disposable.js';
|
|
4
|
+
import type { SenderPolicy } from '../utils/ipc-registry.js';
|
|
3
5
|
import { type RendererNotifier } from './notifications/renderer-notifier.js';
|
|
4
6
|
import { type ViewManager } from './views/view-manager.js';
|
|
7
|
+
import { type WindowService } from './window-service.js';
|
|
5
8
|
import { type WorkspaceService } from './workspace/workspace-service.js';
|
|
9
|
+
import { sanitizeTemplates } from './projects/templates.js';
|
|
10
|
+
import type { ProjectsProvider, ProjectTemplate } from './projects/types.js';
|
|
11
|
+
import type { CustomCreateProjectDialogResult } from '../../shared/types.js';
|
|
6
12
|
/**
|
|
7
13
|
* Shared mutable state for the workbench application.
|
|
8
14
|
* Passed to each IPC module so they can read/write shared state without closures.
|
|
9
15
|
*/
|
|
10
16
|
export interface WorkbenchContext {
|
|
11
|
-
mainWindow: BrowserWindow;
|
|
12
17
|
adapter: CompilationAdapter;
|
|
13
18
|
/** Absolute path to the preload script loaded into the simulator webview */
|
|
14
19
|
preloadPath: string;
|
|
15
20
|
/** Absolute path to the renderer dist directory */
|
|
16
21
|
rendererDir: string;
|
|
17
|
-
workbenchSettingsWindow: BrowserWindow | null;
|
|
18
22
|
/** Built-in panel IDs to display (default: all) */
|
|
19
23
|
panels: string[];
|
|
20
24
|
/** Custom API namespace names (e.g. ['qd']) passed to the simulator */
|
|
@@ -37,10 +41,44 @@ export interface WorkbenchContext {
|
|
|
37
41
|
};
|
|
38
42
|
/** Unified lifecycle manager for all overlay WebContentsViews */
|
|
39
43
|
views: ViewManager;
|
|
44
|
+
/** Owns BrowserWindow instances (main + settings) and their sender-id checks. */
|
|
45
|
+
windows: WindowService;
|
|
40
46
|
/** Unified main → renderer event dispatcher */
|
|
41
47
|
notify: RendererNotifier;
|
|
42
48
|
/** Single source of truth for project + session + per-project settings */
|
|
43
49
|
workspace: WorkspaceService;
|
|
50
|
+
/**
|
|
51
|
+
* Pluggable backend for the project list. Defaults to a LocalProjectsProvider
|
|
52
|
+
* that persists to `<userData>/dimina-projects.json`. Hosts can inject a
|
|
53
|
+
* custom implementation (e.g. read from a remote workspace).
|
|
54
|
+
*/
|
|
55
|
+
projectsProvider: ProjectsProvider;
|
|
56
|
+
/**
|
|
57
|
+
* Merged template catalog used by the create-project flow. Combines the
|
|
58
|
+
* built-in templates with `WorkbenchAppConfig.projectTemplates` according
|
|
59
|
+
* to `WorkbenchAppConfig.builtinTemplates`. Keeps any `generate`
|
|
60
|
+
* functions intact — for IPC delivery, `sanitizeTemplates` is applied at
|
|
61
|
+
* the IPC boundary.
|
|
62
|
+
*/
|
|
63
|
+
projectTemplates: ProjectTemplate[];
|
|
64
|
+
/**
|
|
65
|
+
* Optional host hook for the "新建项目" dialog. When present, the
|
|
66
|
+
* `projects:openCreateDialog` IPC returns whatever this hook resolves to;
|
|
67
|
+
* when absent, the IPC returns null and the renderer shows the built-in
|
|
68
|
+
* dialog.
|
|
69
|
+
*/
|
|
70
|
+
customCreateProjectDialog?: (ctx: {
|
|
71
|
+
parentWindow: BrowserWindow;
|
|
72
|
+
templates: ProjectTemplate[];
|
|
73
|
+
}) => Promise<CustomCreateProjectDialogResult>;
|
|
74
|
+
/**
|
|
75
|
+
* Trust predicate consulted by `IpcRegistry` for every incoming IPC.
|
|
76
|
+
* Resolves the currently-trusted senders (main renderer + overlays)
|
|
77
|
+
* lazily so it stays correct as windows/views come and go.
|
|
78
|
+
*/
|
|
79
|
+
senderPolicy: SenderPolicy;
|
|
80
|
+
/** Aggregates dispose handlers for every IPC handler, listener, watcher, and CDP session registered by the workbench. */
|
|
81
|
+
registry: DisposableRegistry;
|
|
44
82
|
}
|
|
45
83
|
export interface CreateContextOptions {
|
|
46
84
|
mainWindow: BrowserWindow;
|
|
@@ -52,8 +90,17 @@ export interface CreateContextOptions {
|
|
|
52
90
|
appName?: string;
|
|
53
91
|
toolbarActions?: WorkbenchContext['toolbarActions'];
|
|
54
92
|
brandingProvider?: WorkbenchContext['brandingProvider'];
|
|
93
|
+
/** Host-supplied project list backend. Defaults to LocalProjectsProvider. */
|
|
94
|
+
projectsProvider?: ProjectsProvider;
|
|
95
|
+
/** Templates injected by the host; same-id overrides a built-in. */
|
|
96
|
+
projectTemplates?: ProjectTemplate[];
|
|
97
|
+
/** Built-in template policy (default 'all'). */
|
|
98
|
+
builtinTemplates?: 'all' | 'none' | readonly string[];
|
|
99
|
+
/** Host-supplied "新建项目" dialog hook. */
|
|
100
|
+
customCreateProjectDialog?: WorkbenchContext['customCreateProjectDialog'];
|
|
55
101
|
}
|
|
56
102
|
export declare function hasBuiltinPanel(ctx: Pick<WorkbenchContext, 'panels'>, panelId: string): boolean;
|
|
57
103
|
export declare function getDefaultTab(ctx: Pick<WorkbenchContext, 'panels'>): string;
|
|
58
104
|
export declare function createWorkbenchContext(opts: CreateContextOptions): WorkbenchContext;
|
|
105
|
+
export { sanitizeTemplates };
|
|
59
106
|
//# sourceMappingURL=workbench-context.d.ts.map
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import { DisposableRegistry } from '../utils/disposable.js';
|
|
2
|
+
import { createWorkbenchSenderPolicy } from '../utils/sender-policy.js';
|
|
1
3
|
import { defaultAdapter } from './default-adapter.js';
|
|
2
4
|
import { createRendererNotifier, } from './notifications/renderer-notifier.js';
|
|
3
5
|
import { createViewManager } from './views/view-manager.js';
|
|
6
|
+
import { createWindowService } from './window-service.js';
|
|
4
7
|
import { createWorkspaceService, } from './workspace/workspace-service.js';
|
|
8
|
+
import { createLocalProjectsProvider } from './projects/local-provider.js';
|
|
9
|
+
import { resolveTemplates, sanitizeTemplates } from './projects/templates.js';
|
|
10
|
+
import { BUILTIN_TEMPLATES } from './projects/builtin-templates.js';
|
|
5
11
|
export function hasBuiltinPanel(ctx, panelId) {
|
|
6
12
|
return ctx.panels.includes(panelId);
|
|
7
13
|
}
|
|
@@ -14,7 +20,6 @@ export function getDefaultTab(ctx) {
|
|
|
14
20
|
}
|
|
15
21
|
export function createWorkbenchContext(opts) {
|
|
16
22
|
const ctx = {
|
|
17
|
-
mainWindow: opts.mainWindow,
|
|
18
23
|
adapter: opts.adapter ?? defaultAdapter,
|
|
19
24
|
preloadPath: opts.preloadPath,
|
|
20
25
|
rendererDir: opts.rendererDir,
|
|
@@ -23,11 +28,19 @@ export function createWorkbenchContext(opts) {
|
|
|
23
28
|
appName: opts.appName ?? 'Dimina DevTools',
|
|
24
29
|
toolbarActions: opts.toolbarActions,
|
|
25
30
|
brandingProvider: opts.brandingProvider,
|
|
26
|
-
workbenchSettingsWindow: null,
|
|
27
31
|
};
|
|
32
|
+
ctx.registry = new DisposableRegistry();
|
|
33
|
+
ctx.windows = createWindowService(opts.mainWindow);
|
|
28
34
|
ctx.views = createViewManager(ctx);
|
|
29
35
|
ctx.notify = createRendererNotifier(ctx);
|
|
36
|
+
ctx.projectsProvider = opts.projectsProvider ?? createLocalProjectsProvider();
|
|
37
|
+
ctx.projectTemplates = resolveTemplates(BUILTIN_TEMPLATES, opts.projectTemplates ?? [], opts.builtinTemplates ?? 'all');
|
|
38
|
+
ctx.customCreateProjectDialog = opts.customCreateProjectDialog;
|
|
30
39
|
ctx.workspace = createWorkspaceService(ctx);
|
|
40
|
+
ctx.senderPolicy = createWorkbenchSenderPolicy(ctx);
|
|
31
41
|
return ctx;
|
|
32
42
|
}
|
|
43
|
+
// Re-export at this stable module path so callers don't have to know about
|
|
44
|
+
// the projects/ subtree layout.
|
|
45
|
+
export { sanitizeTemplates };
|
|
33
46
|
//# sourceMappingURL=workbench-context.js.map
|
|
@@ -25,10 +25,11 @@ export interface OpenProjectResult {
|
|
|
25
25
|
* rather than touching the repository or adapter directly.
|
|
26
26
|
*/
|
|
27
27
|
export interface WorkspaceService {
|
|
28
|
-
listProjects(): Project[]
|
|
29
|
-
addProject(dirPath: string): Project
|
|
30
|
-
removeProject(dirPath: string): void
|
|
31
|
-
|
|
28
|
+
listProjects(): Promise<Project[]>;
|
|
29
|
+
addProject(dirPath: string): Promise<Project>;
|
|
30
|
+
removeProject(dirPath: string): Promise<void>;
|
|
31
|
+
hasProject(dirPath: string): Promise<boolean>;
|
|
32
|
+
validateProjectDir(dirPath: string): Promise<string | null>;
|
|
32
33
|
openProject(projectPath: string): Promise<OpenProjectResult>;
|
|
33
34
|
closeProject(): Promise<void>;
|
|
34
35
|
getSession(): {
|
|
@@ -38,9 +39,11 @@ export interface WorkspaceService {
|
|
|
38
39
|
} | null;
|
|
39
40
|
getProjectPath(): string;
|
|
40
41
|
hasActiveSession(): boolean;
|
|
42
|
+
captureThumbnail(projectPath: string): Promise<string | null>;
|
|
43
|
+
getThumbnail(projectPath: string): Promise<string | null>;
|
|
41
44
|
getProjectPages(projectPath: string): ProjectPages;
|
|
42
|
-
getCompileConfig(projectPath: string): CompileConfig
|
|
43
|
-
saveCompileConfig(projectPath: string, config: CompileConfig): void
|
|
45
|
+
getCompileConfig(projectPath: string): Promise<CompileConfig>;
|
|
46
|
+
saveCompileConfig(projectPath: string, config: CompileConfig): Promise<void>;
|
|
44
47
|
getProjectSettings(projectPath: string): ProjectSettings;
|
|
45
48
|
updateProjectSettings(projectPath: string, patch: Partial<ProjectSettings>): void;
|
|
46
49
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as repo from '../projects/project-repository.js';
|
|
2
|
+
import { DEFAULT_COMPILE_CONFIG } from '../projects/types.js';
|
|
2
3
|
import { clearSimulatorServicewechatReferer, setSimulatorServicewechatReferer, } from '../simulator/referer.js';
|
|
4
|
+
import { loadWorkbenchSettings } from '../settings/index.js';
|
|
3
5
|
/** Build a workspace service bound to the given workbench context. */
|
|
4
6
|
export function createWorkspaceService(ctx) {
|
|
5
7
|
let currentSession = null;
|
|
6
8
|
let currentProjectPath = '';
|
|
7
|
-
function sendStatus(status, message) {
|
|
8
|
-
ctx.notify.projectStatus({ status, message });
|
|
9
|
+
function sendStatus(status, message, hotReload) {
|
|
10
|
+
ctx.notify.projectStatus(hotReload ? { status, message, hotReload: true } : { status, message });
|
|
9
11
|
}
|
|
10
12
|
function bestEffort(label, fn) {
|
|
11
13
|
try {
|
|
@@ -36,11 +38,25 @@ export function createWorkspaceService(ctx) {
|
|
|
36
38
|
setSimulatorServicewechatReferer(appInfo.appId, typeof appInfo.version === 'string' ? appInfo.version : undefined);
|
|
37
39
|
}
|
|
38
40
|
}
|
|
41
|
+
// Delegate to the host-injected (or default LocalProjectsProvider)
|
|
42
|
+
// ProjectsProvider on the context. When the host omits an optional method,
|
|
43
|
+
// we apply a safe documented default — never silently fall back to
|
|
44
|
+
// `repo.*`, because a remote provider's project paths are not on the local
|
|
45
|
+
// filesystem.
|
|
46
|
+
const provider = ctx.projectsProvider;
|
|
39
47
|
return {
|
|
40
|
-
listProjects: () =>
|
|
41
|
-
addProject: (dirPath) =>
|
|
42
|
-
removeProject: (dirPath) =>
|
|
43
|
-
|
|
48
|
+
listProjects: async () => provider.listProjects(),
|
|
49
|
+
addProject: async (dirPath) => provider.addProject(dirPath),
|
|
50
|
+
removeProject: async (dirPath) => {
|
|
51
|
+
await provider.removeProject(dirPath);
|
|
52
|
+
},
|
|
53
|
+
hasProject: async (dirPath) => {
|
|
54
|
+
const projects = await provider.listProjects();
|
|
55
|
+
return projects.some((p) => p.path === dirPath);
|
|
56
|
+
},
|
|
57
|
+
validateProjectDir: async (dirPath) => provider.validateProjectDir
|
|
58
|
+
? provider.validateProjectDir(dirPath)
|
|
59
|
+
: null,
|
|
44
60
|
async openProject(projectPath) {
|
|
45
61
|
clearSimulatorServicewechatReferer();
|
|
46
62
|
await disposeSession();
|
|
@@ -52,18 +68,22 @@ export function createWorkspaceService(ctx) {
|
|
|
52
68
|
// `fetch(…).then(r=>r.text()).then(JSON.parse)` pipeline dies on the
|
|
53
69
|
// dev server's HTML SPA fallback with
|
|
54
70
|
// `SyntaxError: Unexpected token '<', "<!doctype "…`.
|
|
55
|
-
const dirError =
|
|
71
|
+
const dirError = provider.validateProjectDir
|
|
72
|
+
? await provider.validateProjectDir(projectPath)
|
|
73
|
+
: null;
|
|
56
74
|
if (dirError) {
|
|
57
75
|
sendStatus('error', dirError);
|
|
58
76
|
return { success: false, error: dirError };
|
|
59
77
|
}
|
|
60
78
|
sendStatus('compiling', '正在编译...');
|
|
79
|
+
const { compile } = loadWorkbenchSettings();
|
|
61
80
|
let session;
|
|
62
81
|
try {
|
|
63
82
|
session = await ctx.adapter.openProject({
|
|
64
83
|
projectPath,
|
|
65
84
|
sourcemap: true,
|
|
66
|
-
|
|
85
|
+
watch: compile.watch,
|
|
86
|
+
onRebuild: () => sendStatus('ready', '编译完成,已热更新', true),
|
|
67
87
|
onBuildError: (err) => sendStatus('error', String(err)),
|
|
68
88
|
});
|
|
69
89
|
}
|
|
@@ -74,7 +94,10 @@ export function createWorkspaceService(ctx) {
|
|
|
74
94
|
}
|
|
75
95
|
currentSession = session;
|
|
76
96
|
currentProjectPath = projectPath;
|
|
77
|
-
bestEffort('updateLastOpened', () =>
|
|
97
|
+
bestEffort('updateLastOpened', () => {
|
|
98
|
+
if (provider.updateLastOpened)
|
|
99
|
+
provider.updateLastOpened(projectPath);
|
|
100
|
+
});
|
|
78
101
|
bestEffort('sendStatus', () => sendStatus('ready', '编译完成'));
|
|
79
102
|
bestEffort('applyReferer', () => applyRefererFromSession(session));
|
|
80
103
|
return {
|
|
@@ -92,9 +115,46 @@ export function createWorkspaceService(ctx) {
|
|
|
92
115
|
getSession: () => currentSession,
|
|
93
116
|
getProjectPath: () => currentProjectPath,
|
|
94
117
|
hasActiveSession: () => currentSession !== null,
|
|
118
|
+
async captureThumbnail(projectPath) {
|
|
119
|
+
const wc = ctx.views.getSimulatorWebContents();
|
|
120
|
+
if (!wc)
|
|
121
|
+
return null;
|
|
122
|
+
try {
|
|
123
|
+
const image = await wc.capturePage();
|
|
124
|
+
const dataUrl = `data:image/png;base64,${image.toPNG().toString('base64')}`;
|
|
125
|
+
if (provider.saveThumbnail) {
|
|
126
|
+
await provider.saveThumbnail(projectPath, dataUrl);
|
|
127
|
+
}
|
|
128
|
+
// Always hand the renderer back the freshly-captured frame so the
|
|
129
|
+
// UI updates immediately even if the host's saveThumbnail is
|
|
130
|
+
// async or stores out-of-band.
|
|
131
|
+
return dataUrl;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
async getThumbnail(projectPath) {
|
|
138
|
+
if (provider.getThumbnail) {
|
|
139
|
+
return (await provider.getThumbnail(projectPath)) ?? null;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
},
|
|
143
|
+
// `getProjectPages` reads the project's own `app.json` from disk and is
|
|
144
|
+
// independent of the project registry — it stays on the repo helper.
|
|
95
145
|
getProjectPages: (projectPath) => repo.getProjectPages(projectPath),
|
|
96
|
-
getCompileConfig: (projectPath) =>
|
|
97
|
-
|
|
146
|
+
getCompileConfig: async (projectPath) => (provider.getCompileConfig
|
|
147
|
+
? await provider.getCompileConfig(projectPath)
|
|
148
|
+
: DEFAULT_COMPILE_CONFIG),
|
|
149
|
+
saveCompileConfig: async (projectPath, config) => {
|
|
150
|
+
if (provider.saveCompileConfig) {
|
|
151
|
+
await provider.saveCompileConfig(projectPath, config);
|
|
152
|
+
}
|
|
153
|
+
// No persistence when the host opts out; the renderer's edits then
|
|
154
|
+
// do not survive a reload, matching the documented contract.
|
|
155
|
+
},
|
|
156
|
+
// Per-project settings (uploadWithSourceMap etc.) live in the project's
|
|
157
|
+
// own `project.config.json`, not the registry — keep direct repo calls.
|
|
98
158
|
getProjectSettings: (projectPath) => repo.getProjectSettings(projectPath),
|
|
99
159
|
updateProjectSettings: (projectPath, patch) => repo.updateProjectSettings(projectPath, patch),
|
|
100
160
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type SimulatorApiHandler } from './services/simulator/custom-apis.js';
|
|
2
|
+
export type { SimulatorApiHandler } from './services/simulator/custom-apis.js';
|
|
3
|
+
/**
|
|
4
|
+
* Register a handler for a simulator API callable from mini-program code.
|
|
5
|
+
*
|
|
6
|
+
* The handler runs in the Electron main process (Node.js context) and may
|
|
7
|
+
* use any main-process capability (filesystem, network, native deps). When
|
|
8
|
+
* the mini-program invokes `wx.<name>(params)` inside the simulator, the
|
|
9
|
+
* simulator forwards the call over IPC and resolves to the handler's return
|
|
10
|
+
* value. Errors thrown by the handler propagate back as a rejection.
|
|
11
|
+
*
|
|
12
|
+
* Re-registering the same name silently replaces the previous handler.
|
|
13
|
+
* The returned disposer removes only the registration it created — if the
|
|
14
|
+
* name was overwritten by a later `registerSimulatorApi`, calling the
|
|
15
|
+
* earlier disposer is a no-op.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* registerSimulatorApi('myCompany.login', async ({ username }) => {
|
|
19
|
+
* return await callInternalAuth(username)
|
|
20
|
+
* })
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerSimulatorApi(name: string, handler: SimulatorApiHandler): () => void;
|
|
23
|
+
//# sourceMappingURL=simulator-apis.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { simulatorApiRegistry } from './services/simulator/custom-apis.js';
|
|
2
|
+
/**
|
|
3
|
+
* Register a handler for a simulator API callable from mini-program code.
|
|
4
|
+
*
|
|
5
|
+
* The handler runs in the Electron main process (Node.js context) and may
|
|
6
|
+
* use any main-process capability (filesystem, network, native deps). When
|
|
7
|
+
* the mini-program invokes `wx.<name>(params)` inside the simulator, the
|
|
8
|
+
* simulator forwards the call over IPC and resolves to the handler's return
|
|
9
|
+
* value. Errors thrown by the handler propagate back as a rejection.
|
|
10
|
+
*
|
|
11
|
+
* Re-registering the same name silently replaces the previous handler.
|
|
12
|
+
* The returned disposer removes only the registration it created — if the
|
|
13
|
+
* name was overwritten by a later `registerSimulatorApi`, calling the
|
|
14
|
+
* earlier disposer is a no-op.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* registerSimulatorApi('myCompany.login', async ({ username }) => {
|
|
18
|
+
* return await callInternalAuth(username)
|
|
19
|
+
* })
|
|
20
|
+
*/
|
|
21
|
+
export function registerSimulatorApi(name, handler) {
|
|
22
|
+
return simulatorApiRegistry.register(name, handler);
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=simulator-apis.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface Disposable {
|
|
2
|
+
dispose(): void | Promise<void>;
|
|
3
|
+
}
|
|
4
|
+
export type DisposeFn = () => void | Promise<void>;
|
|
5
|
+
export declare function toDisposable(fn: DisposeFn): Disposable;
|
|
6
|
+
export declare class DisposableRegistry implements Disposable {
|
|
7
|
+
private entries;
|
|
8
|
+
private _disposed;
|
|
9
|
+
add(d: Disposable | DisposeFn): Disposable;
|
|
10
|
+
disposeAll(): Promise<void>;
|
|
11
|
+
dispose(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=disposable.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export function toDisposable(fn) {
|
|
2
|
+
let done = false;
|
|
3
|
+
return {
|
|
4
|
+
dispose() {
|
|
5
|
+
if (done)
|
|
6
|
+
return;
|
|
7
|
+
done = true;
|
|
8
|
+
return fn();
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export class DisposableRegistry {
|
|
13
|
+
entries = [];
|
|
14
|
+
_disposed = false;
|
|
15
|
+
add(d) {
|
|
16
|
+
if (this._disposed) {
|
|
17
|
+
throw new Error('cannot add to disposed registry');
|
|
18
|
+
}
|
|
19
|
+
const fn = typeof d === 'function' ? d : () => d.dispose();
|
|
20
|
+
const entry = { fn, released: false };
|
|
21
|
+
this.entries.push(entry);
|
|
22
|
+
return {
|
|
23
|
+
dispose: () => {
|
|
24
|
+
if (entry.released)
|
|
25
|
+
return;
|
|
26
|
+
entry.released = true;
|
|
27
|
+
const i = this.entries.indexOf(entry);
|
|
28
|
+
if (i >= 0)
|
|
29
|
+
this.entries.splice(i, 1);
|
|
30
|
+
return fn();
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async disposeAll() {
|
|
35
|
+
if (this._disposed)
|
|
36
|
+
return;
|
|
37
|
+
this._disposed = true;
|
|
38
|
+
const items = this.entries.slice().reverse();
|
|
39
|
+
this.entries = [];
|
|
40
|
+
const errors = [];
|
|
41
|
+
for (const entry of items) {
|
|
42
|
+
if (entry.released)
|
|
43
|
+
continue;
|
|
44
|
+
entry.released = true;
|
|
45
|
+
try {
|
|
46
|
+
await entry.fn();
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
errors.push(e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (errors.length > 0) {
|
|
53
|
+
throw new AggregateError(errors, 'DisposableRegistry encountered errors during disposeAll');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
dispose() {
|
|
57
|
+
return this.disposeAll();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=disposable.js.map
|