@dimina-kit/devtools 0.3.1-dev.20260517100756 → 0.3.1-dev.20260518114324

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.
Files changed (83) hide show
  1. package/README.md +138 -7
  2. package/dist/main/app/app.js +8 -0
  3. package/dist/main/index.bundle.js +374 -58
  4. package/dist/main/ipc/projects.d.ts +3 -1
  5. package/dist/main/ipc/projects.js +88 -4
  6. package/dist/main/ipc/session.js +9 -1
  7. package/dist/main/ipc/settings.js +8 -6
  8. package/dist/main/menu/index.js +11 -0
  9. package/dist/main/services/projects/builtin-templates.d.ts +3 -0
  10. package/dist/main/services/projects/builtin-templates.js +24 -0
  11. package/dist/main/services/projects/create-project-service.d.ts +7 -0
  12. package/dist/main/services/projects/create-project-service.js +84 -0
  13. package/dist/main/services/projects/index.d.ts +7 -0
  14. package/dist/main/services/projects/index.js +9 -0
  15. package/dist/main/services/projects/local-provider.d.ts +3 -0
  16. package/dist/main/services/projects/local-provider.js +25 -0
  17. package/dist/main/services/projects/templates.d.ts +21 -0
  18. package/dist/main/services/projects/templates.js +39 -0
  19. package/dist/main/services/projects/thumbnail.d.ts +8 -0
  20. package/dist/main/services/projects/thumbnail.js +37 -0
  21. package/dist/main/services/projects/types.d.ts +118 -0
  22. package/dist/main/services/projects/types.js +19 -0
  23. package/dist/main/services/settings/index.d.ts +7 -0
  24. package/dist/main/services/settings/index.js +4 -0
  25. package/dist/main/services/workbench-context.d.ts +35 -0
  26. package/dist/main/services/workbench-context.js +9 -0
  27. package/dist/main/services/workspace/workspace-service.d.ts +9 -7
  28. package/dist/main/services/workspace/workspace-service.js +81 -9
  29. package/dist/main/utils/sender-policy.d.ts +7 -5
  30. package/dist/main/utils/sender-policy.js +8 -6
  31. package/dist/main/windows/main-window/create.js +1 -1
  32. package/dist/preload/runtime/custom-apis.js +32 -3
  33. package/dist/preload/windows/simulator.js +26 -5
  34. package/dist/renderer/assets/index-Czz_bp6b.js +46 -0
  35. package/dist/renderer/assets/{input-C5M5Wxn6.js → input-DqVQNCx_.js} +2 -2
  36. package/dist/renderer/assets/{ipc-transport-CUsl-fS2.js → ipc-transport-CCHrCtDr.js} +2 -2
  37. package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +1 -0
  38. package/dist/renderer/assets/{popover-BSBPVO0o.js → popover-BRg4XI_w.js} +2 -2
  39. package/dist/renderer/assets/{select-B1tF3UAc.js → select-CvILHHZU.js} +2 -2
  40. package/dist/renderer/assets/{settings-7w54H6p-.js → settings-BFNfnYvU.js} +2 -2
  41. package/dist/renderer/assets/{settings-api-BjJalghB.js → settings-api-DOAgmBst.js} +2 -2
  42. package/dist/renderer/assets/{workbenchSettings-q2_-nc8t.js → workbenchSettings-DjVMgMMH.js} +2 -2
  43. package/dist/renderer/entries/main/index.html +5 -5
  44. package/dist/renderer/entries/popover/index.html +5 -5
  45. package/dist/renderer/entries/settings/index.html +5 -5
  46. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  47. package/dist/shared/ipc-channels.d.ts +20 -1
  48. package/dist/shared/ipc-channels.js +27 -0
  49. package/dist/shared/ipc-schemas.d.ts +5 -0
  50. package/dist/shared/ipc-schemas.js +5 -0
  51. package/dist/shared/types.d.ts +80 -0
  52. package/package.json +9 -4
  53. package/templates/blank/app.js +6 -0
  54. package/templates/blank/app.json +11 -0
  55. package/templates/blank/app.wxss +5 -0
  56. package/templates/blank/pages/index/index.js +6 -0
  57. package/templates/blank/pages/index/index.json +3 -0
  58. package/templates/blank/pages/index/index.wxml +3 -0
  59. package/templates/blank/pages/index/index.wxss +11 -0
  60. package/templates/blank/project.config.json +13 -0
  61. package/templates/blank/sitemap.json +9 -0
  62. package/templates/taro-todo/app.js +1 -0
  63. package/templates/taro-todo/app.json +1 -0
  64. package/templates/taro-todo/app.wxss +1 -0
  65. package/templates/taro-todo/base.wxml +851 -0
  66. package/templates/taro-todo/common.js +1 -0
  67. package/templates/taro-todo/comp.js +1 -0
  68. package/templates/taro-todo/comp.json +1 -0
  69. package/templates/taro-todo/comp.wxml +2 -0
  70. package/templates/taro-todo/custom-wrapper.js +1 -0
  71. package/templates/taro-todo/custom-wrapper.json +1 -0
  72. package/templates/taro-todo/custom-wrapper.wxml +4 -0
  73. package/templates/taro-todo/pages/index/index.js +2 -0
  74. package/templates/taro-todo/pages/index/index.json +1 -0
  75. package/templates/taro-todo/pages/index/index.wxml +2 -0
  76. package/templates/taro-todo/pages/index/index.wxss +1 -0
  77. package/templates/taro-todo/project.config.json +44 -0
  78. package/templates/taro-todo/runtime.js +1 -0
  79. package/templates/taro-todo/taro.js +2 -0
  80. package/templates/taro-todo/utils.wxs +39 -0
  81. package/templates/taro-todo/vendors.js +2 -0
  82. package/dist/renderer/assets/index-DW_0thr-.js +0 -46
  83. package/dist/renderer/assets/ipc-transport-CltRuvhi.css +0 -1
@@ -14,6 +14,7 @@ const DEFAULTS = {
14
14
  watch: true,
15
15
  },
16
16
  theme: 'system',
17
+ lastCreateBaseDir: null,
17
18
  };
18
19
  function getSettingsFile() {
19
20
  return path.join(app.getPath('userData'), 'dimina-workbench-settings.json');
@@ -37,6 +38,9 @@ export function loadWorkbenchSettings() {
37
38
  watch: data.compile?.watch ?? DEFAULTS.compile.watch,
38
39
  },
39
40
  theme,
41
+ lastCreateBaseDir: typeof data.lastCreateBaseDir === 'string'
42
+ ? data.lastCreateBaseDir
43
+ : DEFAULTS.lastCreateBaseDir,
40
44
  };
41
45
  }
42
46
  catch {
@@ -6,6 +6,8 @@ import { type RendererNotifier } from './notifications/renderer-notifier.js';
6
6
  import { type ViewManager } from './views/view-manager.js';
7
7
  import { type WindowService } from './window-service.js';
8
8
  import { type WorkspaceService } from './workspace/workspace-service.js';
9
+ import { sanitizeTemplates } from './projects/templates.js';
10
+ import type { CreateProjectInput, ProjectsProvider, ProjectTemplate } from './projects/types.js';
9
11
  /**
10
12
  * Shared mutable state for the workbench application.
11
13
  * Passed to each IPC module so they can read/write shared state without closures.
@@ -48,6 +50,30 @@ export interface WorkbenchContext {
48
50
  notify: RendererNotifier;
49
51
  /** Single source of truth for project + session + per-project settings */
50
52
  workspace: WorkspaceService;
53
+ /**
54
+ * Pluggable backend for the project list. Defaults to a LocalProjectsProvider
55
+ * that persists to `<userData>/dimina-projects.json`. Hosts can inject a
56
+ * custom implementation (e.g. read from a remote workspace).
57
+ */
58
+ projectsProvider: ProjectsProvider;
59
+ /**
60
+ * Merged template catalog used by the create-project flow. Combines the
61
+ * built-in templates with `WorkbenchAppConfig.projectTemplates` according
62
+ * to `WorkbenchAppConfig.builtinTemplates`. Keeps any `generate`
63
+ * functions intact — for IPC delivery, `sanitizeTemplates` is applied at
64
+ * the IPC boundary.
65
+ */
66
+ projectTemplates: ProjectTemplate[];
67
+ /**
68
+ * Optional host hook for the "新建项目" dialog. When present, the
69
+ * `projects:openCreateDialog` IPC returns whatever this hook resolves to;
70
+ * when absent, the IPC returns null and the renderer shows the built-in
71
+ * dialog.
72
+ */
73
+ customCreateProjectDialog?: (ctx: {
74
+ parentWindow: BrowserWindow;
75
+ templates: ProjectTemplate[];
76
+ }) => Promise<CreateProjectInput | null>;
51
77
  /**
52
78
  * Trust predicate consulted by `IpcRegistry` for every incoming IPC.
53
79
  * Resolves the currently-trusted senders (main renderer + overlays)
@@ -67,8 +93,17 @@ export interface CreateContextOptions {
67
93
  appName?: string;
68
94
  toolbarActions?: WorkbenchContext['toolbarActions'];
69
95
  brandingProvider?: WorkbenchContext['brandingProvider'];
96
+ /** Host-supplied project list backend. Defaults to LocalProjectsProvider. */
97
+ projectsProvider?: ProjectsProvider;
98
+ /** Templates injected by the host; same-id overrides a built-in. */
99
+ projectTemplates?: ProjectTemplate[];
100
+ /** Built-in template policy (default 'all'). */
101
+ builtinTemplates?: 'all' | 'none' | readonly string[];
102
+ /** Host-supplied "新建项目" dialog hook. */
103
+ customCreateProjectDialog?: WorkbenchContext['customCreateProjectDialog'];
70
104
  }
71
105
  export declare function hasBuiltinPanel(ctx: Pick<WorkbenchContext, 'panels'>, panelId: string): boolean;
72
106
  export declare function getDefaultTab(ctx: Pick<WorkbenchContext, 'panels'>): string;
73
107
  export declare function createWorkbenchContext(opts: CreateContextOptions): WorkbenchContext;
108
+ export { sanitizeTemplates };
74
109
  //# sourceMappingURL=workbench-context.d.ts.map
@@ -5,6 +5,9 @@ import { createRendererNotifier, } from './notifications/renderer-notifier.js';
5
5
  import { createViewManager } from './views/view-manager.js';
6
6
  import { createWindowService } from './window-service.js';
7
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';
8
11
  export function hasBuiltinPanel(ctx, panelId) {
9
12
  return ctx.panels.includes(panelId);
10
13
  }
@@ -32,8 +35,14 @@ export function createWorkbenchContext(opts) {
32
35
  ctx.windows = createWindowService(ctx.mainWindow);
33
36
  ctx.views = createViewManager(ctx);
34
37
  ctx.notify = createRendererNotifier(ctx);
38
+ ctx.projectsProvider = opts.projectsProvider ?? createLocalProjectsProvider();
39
+ ctx.projectTemplates = resolveTemplates(BUILTIN_TEMPLATES, opts.projectTemplates ?? [], opts.builtinTemplates ?? 'all');
40
+ ctx.customCreateProjectDialog = opts.customCreateProjectDialog;
35
41
  ctx.workspace = createWorkspaceService(ctx);
36
42
  ctx.senderPolicy = createWorkbenchSenderPolicy(ctx);
37
43
  return ctx;
38
44
  }
45
+ // Re-export at this stable module path so callers don't have to know about
46
+ // the projects/ subtree layout.
47
+ export { sanitizeTemplates };
39
48
  //# sourceMappingURL=workbench-context.js.map
@@ -25,11 +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
- hasProject(dirPath: string): boolean;
32
- validateProjectDir(dirPath: string): string | null;
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>;
33
33
  openProject(projectPath: string): Promise<OpenProjectResult>;
34
34
  closeProject(): Promise<void>;
35
35
  getSession(): {
@@ -39,9 +39,11 @@ export interface WorkspaceService {
39
39
  } | null;
40
40
  getProjectPath(): string;
41
41
  hasActiveSession(): boolean;
42
+ captureThumbnail(projectPath: string): Promise<string | null>;
43
+ getThumbnail(projectPath: string): Promise<string | null>;
42
44
  getProjectPages(projectPath: string): ProjectPages;
43
- getCompileConfig(projectPath: string): CompileConfig;
44
- saveCompileConfig(projectPath: string, config: CompileConfig): void;
45
+ getCompileConfig(projectPath: string): Promise<CompileConfig>;
46
+ saveCompileConfig(projectPath: string, config: CompileConfig): Promise<void>;
45
47
  getProjectSettings(projectPath: string): ProjectSettings;
46
48
  updateProjectSettings(projectPath: string, patch: Partial<ProjectSettings>): void;
47
49
  }
@@ -1,4 +1,6 @@
1
+ import { webContents } from 'electron';
1
2
  import * as repo from '../projects/project-repository.js';
3
+ import { DEFAULT_COMPILE_CONFIG } from '../projects/types.js';
2
4
  import { clearSimulatorServicewechatReferer, setSimulatorServicewechatReferer, } from '../simulator/referer.js';
3
5
  import { loadWorkbenchSettings } from '../settings/index.js';
4
6
  /** Build a workspace service bound to the given workbench context. */
@@ -37,12 +39,37 @@ export function createWorkspaceService(ctx) {
37
39
  setSimulatorServicewechatReferer(appInfo.appId, typeof appInfo.version === 'string' ? appInfo.version : undefined);
38
40
  }
39
41
  }
40
- return {
42
+ // Delegate to the host-injected (or default) ProjectsProvider on the
43
+ // context. When the host omits an optional method, we apply a safe
44
+ // documented default — never silently fall back to `repo.*`, because a
45
+ // remote provider's project paths are not on the local filesystem.
46
+ //
47
+ // The `??` here is a back-compat path for hand-rolled WorkbenchContext
48
+ // values (used in a couple of legacy tests). createWorkbenchContext
49
+ // always installs LocalProjectsProvider, so production never enters this
50
+ // branch.
51
+ const provider = ctx.projectsProvider ?? {
41
52
  listProjects: () => repo.listProjects(),
42
- addProject: (dirPath) => repo.addProject(dirPath),
43
- removeProject: (dirPath) => repo.removeProject(dirPath),
44
- hasProject: (dirPath) => repo.hasProject(dirPath),
45
- validateProjectDir: (dirPath) => repo.validateProjectDir(dirPath),
53
+ addProject: (p) => repo.addProject(p),
54
+ removeProject: (p) => repo.removeProject(p),
55
+ validateProjectDir: (p) => repo.validateProjectDir(p),
56
+ updateLastOpened: (p) => repo.updateLastOpened(p),
57
+ getCompileConfig: (p) => repo.getCompileConfig(p),
58
+ saveCompileConfig: (p, c) => repo.saveCompileConfig(p, c),
59
+ };
60
+ return {
61
+ listProjects: async () => provider.listProjects(),
62
+ addProject: async (dirPath) => provider.addProject(dirPath),
63
+ removeProject: async (dirPath) => {
64
+ await provider.removeProject(dirPath);
65
+ },
66
+ hasProject: async (dirPath) => {
67
+ const projects = await provider.listProjects();
68
+ return projects.some((p) => p.path === dirPath);
69
+ },
70
+ validateProjectDir: async (dirPath) => provider.validateProjectDir
71
+ ? provider.validateProjectDir(dirPath)
72
+ : null,
46
73
  async openProject(projectPath) {
47
74
  clearSimulatorServicewechatReferer();
48
75
  await disposeSession();
@@ -54,7 +81,9 @@ export function createWorkspaceService(ctx) {
54
81
  // `fetch(…).then(r=>r.text()).then(JSON.parse)` pipeline dies on the
55
82
  // dev server's HTML SPA fallback with
56
83
  // `SyntaxError: Unexpected token '<', "<!doctype "…`.
57
- const dirError = repo.validateProjectDir(projectPath);
84
+ const dirError = provider.validateProjectDir
85
+ ? await provider.validateProjectDir(projectPath)
86
+ : null;
58
87
  if (dirError) {
59
88
  sendStatus('error', dirError);
60
89
  return { success: false, error: dirError };
@@ -78,7 +107,10 @@ export function createWorkspaceService(ctx) {
78
107
  }
79
108
  currentSession = session;
80
109
  currentProjectPath = projectPath;
81
- bestEffort('updateLastOpened', () => repo.updateLastOpened(projectPath));
110
+ bestEffort('updateLastOpened', () => {
111
+ if (provider.updateLastOpened)
112
+ provider.updateLastOpened(projectPath);
113
+ });
82
114
  bestEffort('sendStatus', () => sendStatus('ready', '编译完成'));
83
115
  bestEffort('applyReferer', () => applyRefererFromSession(session));
84
116
  return {
@@ -96,9 +128,49 @@ export function createWorkspaceService(ctx) {
96
128
  getSession: () => currentSession,
97
129
  getProjectPath: () => currentProjectPath,
98
130
  hasActiveSession: () => currentSession !== null,
131
+ async captureThumbnail(projectPath) {
132
+ const simWcId = ctx.views.getSimulatorWebContentsId();
133
+ if (!simWcId)
134
+ return null;
135
+ const wc = webContents.fromId(simWcId);
136
+ if (!wc || wc.isDestroyed())
137
+ return null;
138
+ try {
139
+ const image = await wc.capturePage();
140
+ const dataUrl = `data:image/png;base64,${image.toPNG().toString('base64')}`;
141
+ if (provider.saveThumbnail) {
142
+ await provider.saveThumbnail(projectPath, dataUrl);
143
+ }
144
+ // Always hand the renderer back the freshly-captured frame so the
145
+ // UI updates immediately even if the host's saveThumbnail is
146
+ // async or stores out-of-band.
147
+ return dataUrl;
148
+ }
149
+ catch {
150
+ return null;
151
+ }
152
+ },
153
+ async getThumbnail(projectPath) {
154
+ if (provider.getThumbnail) {
155
+ return (await provider.getThumbnail(projectPath)) ?? null;
156
+ }
157
+ return null;
158
+ },
159
+ // `getProjectPages` reads the project's own `app.json` from disk and is
160
+ // independent of the project registry — it stays on the repo helper.
99
161
  getProjectPages: (projectPath) => repo.getProjectPages(projectPath),
100
- getCompileConfig: (projectPath) => repo.getCompileConfig(projectPath),
101
- saveCompileConfig: (projectPath, config) => repo.saveCompileConfig(projectPath, config),
162
+ getCompileConfig: async (projectPath) => (provider.getCompileConfig
163
+ ? await provider.getCompileConfig(projectPath)
164
+ : DEFAULT_COMPILE_CONFIG),
165
+ saveCompileConfig: async (projectPath, config) => {
166
+ if (provider.saveCompileConfig) {
167
+ await provider.saveCompileConfig(projectPath, config);
168
+ }
169
+ // No persistence when the host opts out; the renderer's edits then
170
+ // do not survive a reload, matching the documented contract.
171
+ },
172
+ // Per-project settings (uploadWithSourceMap etc.) live in the project's
173
+ // own `project.config.json`, not the registry — keep direct repo calls.
102
174
  getProjectSettings: (projectPath) => repo.getProjectSettings(projectPath),
103
175
  updateProjectSettings: (projectPath, patch) => repo.updateProjectSettings(projectPath, patch),
104
176
  };
@@ -10,11 +10,13 @@ import type { SenderPolicy } from './ipc-registry.js';
10
10
  * - the settings overlay view (when open)
11
11
  * - the popover overlay view (when open)
12
12
  *
13
- * The simulator webview is intentionally NOT on this list: its preload
14
- * (`src/preload/windows/simulator.ts`) uses `ipcRenderer.sendToHost`
15
- * exclusively, never `invoke`/`send`, so it can't legitimately reach an
16
- * ipcMain handler. Including it would only widen the attack surface if
17
- * the guest were ever compromised.
13
+ * The simulator webview is intentionally NOT on this list. Anything it
14
+ * needs from main (currently just the custom-apis bridge — see
15
+ * `installCustomApisBridge` in `src/preload/runtime/custom-apis.ts` and the
16
+ * matching `useCustomApiProxy` host hook) proxies through the trusted
17
+ * main-window renderer via `ipcRenderer.sendToHost` + `<webview>.send`.
18
+ * Keeping the guest off this list contains the blast radius if the
19
+ * simulator content is ever compromised.
18
20
  *
19
21
  * Any other sender — including a stale/destroyed sender or an unknown
20
22
  * iframe — is rejected.
@@ -8,11 +8,13 @@
8
8
  * - the settings overlay view (when open)
9
9
  * - the popover overlay view (when open)
10
10
  *
11
- * The simulator webview is intentionally NOT on this list: its preload
12
- * (`src/preload/windows/simulator.ts`) uses `ipcRenderer.sendToHost`
13
- * exclusively, never `invoke`/`send`, so it can't legitimately reach an
14
- * ipcMain handler. Including it would only widen the attack surface if
15
- * the guest were ever compromised.
11
+ * The simulator webview is intentionally NOT on this list. Anything it
12
+ * needs from main (currently just the custom-apis bridge — see
13
+ * `installCustomApisBridge` in `src/preload/runtime/custom-apis.ts` and the
14
+ * matching `useCustomApiProxy` host hook) proxies through the trusted
15
+ * main-window renderer via `ipcRenderer.sendToHost` + `<webview>.send`.
16
+ * Keeping the guest off this list contains the blast radius if the
17
+ * simulator content is ever compromised.
16
18
  *
17
19
  * Any other sender — including a stale/destroyed sender or an unknown
18
20
  * iframe — is rejected.
@@ -35,7 +37,7 @@ export function createWorkbenchSenderPolicy(ctx) {
35
37
  const popoverViewId = ctx.views.getPopoverWebContentsId();
36
38
  if (popoverViewId != null && sender.id === popoverViewId)
37
39
  return true;
38
- // simulator preload uses sendToHost only; never reaches ipcMain handlers.
40
+ // simulator <webview> proxies through main-window renderer (see file header).
39
41
  return false;
40
42
  };
41
43
  }
@@ -29,7 +29,7 @@ function configureSimulatorSession(simulatorPreloadPath) {
29
29
  headers['access-control-allow-headers'] = ['*'];
30
30
  headers['access-control-allow-methods'] = ['*'];
31
31
  headers['Cross-Origin-Opener-Policy'] = ['same-origin'];
32
- headers['Cross-Origin-Embedder-Policy'] = ['require-corp'];
32
+ headers['Cross-Origin-Embedder-Policy'] = ['credentialless'];
33
33
  callback({ responseHeaders: headers });
34
34
  });
35
35
  }
@@ -1,9 +1,38 @@
1
1
  import { contextBridge, ipcRenderer } from 'electron';
2
- import { SimulatorCustomApiChannel } from '../../shared/ipc-channels.js';
2
+ import { SimulatorCustomApiBridgeChannel } from '../../shared/ipc-channels.js';
3
+ // The simulator <webview> is intentionally kept off the workbench sender-policy
4
+ // white-list, so it cannot reach `ipcMain.handle` directly. Instead the bridge
5
+ // asks the trusted main-window renderer to proxy the call: webview sends via
6
+ // `ipcRenderer.sendToHost`, host does the `ipcInvoke`, and posts the result
7
+ // back through `<webview>.send`. Requests and responses are correlated by id
8
+ // so concurrent invokes do not tangle.
3
9
  function buildBridge() {
10
+ let nextId = 1;
11
+ const pending = new Map();
12
+ ipcRenderer.on(SimulatorCustomApiBridgeChannel.Response, (_event, payload) => {
13
+ const entry = pending.get(payload.id);
14
+ if (!entry)
15
+ return;
16
+ pending.delete(payload.id);
17
+ if ('error' in payload) {
18
+ entry.reject(new Error(payload.error));
19
+ }
20
+ else {
21
+ entry.resolve(payload.result);
22
+ }
23
+ });
24
+ const send = (req) => {
25
+ return new Promise((resolve, reject) => {
26
+ pending.set(req.id, {
27
+ resolve: (value) => resolve(value),
28
+ reject,
29
+ });
30
+ ipcRenderer.sendToHost(SimulatorCustomApiBridgeChannel.Request, req);
31
+ });
32
+ };
4
33
  return {
5
- list: () => ipcRenderer.invoke(SimulatorCustomApiChannel.List),
6
- invoke: (name, params) => ipcRenderer.invoke(SimulatorCustomApiChannel.Invoke, name, params),
34
+ list: () => send({ id: nextId++, op: 'list' }),
35
+ invoke: (name, params) => send({ id: nextId++, op: 'invoke', name, params }),
7
36
  };
8
37
  }
9
38
  export function installCustomApisBridge() {
@@ -187,9 +187,9 @@ var SimulatorChannel = {
187
187
  AppData: "simulator:appdata",
188
188
  AppDataAll: "simulator:appdata-all"
189
189
  };
190
- var SimulatorCustomApiChannel = {
191
- List: "simulator:custom-apis:list",
192
- Invoke: "simulator:custom-apis:invoke"
190
+ var SimulatorCustomApiBridgeChannel = {
191
+ Request: "simulator:custom-apis:bridge-request",
192
+ Response: "simulator:custom-apis:bridge-response"
193
193
  };
194
194
  var BridgeChannel = {
195
195
  WxmlRefreshRequest: "wxml:refresh:request",
@@ -199,9 +199,30 @@ var BridgeChannel = {
199
199
 
200
200
  // src/preload/runtime/custom-apis.ts
201
201
  function buildBridge() {
202
+ let nextId = 1;
203
+ const pending = /* @__PURE__ */ new Map();
204
+ import_electron2.ipcRenderer.on(SimulatorCustomApiBridgeChannel.Response, (_event, payload) => {
205
+ const entry = pending.get(payload.id);
206
+ if (!entry) return;
207
+ pending.delete(payload.id);
208
+ if ("error" in payload) {
209
+ entry.reject(new Error(payload.error));
210
+ } else {
211
+ entry.resolve(payload.result);
212
+ }
213
+ });
214
+ const send = (req) => {
215
+ return new Promise((resolve, reject) => {
216
+ pending.set(req.id, {
217
+ resolve: (value) => resolve(value),
218
+ reject
219
+ });
220
+ import_electron2.ipcRenderer.sendToHost(SimulatorCustomApiBridgeChannel.Request, req);
221
+ });
222
+ };
202
223
  return {
203
- list: () => import_electron2.ipcRenderer.invoke(SimulatorCustomApiChannel.List),
204
- invoke: (name, params) => import_electron2.ipcRenderer.invoke(SimulatorCustomApiChannel.Invoke, name, params)
224
+ list: () => send({ id: nextId++, op: "list" }),
225
+ invoke: (name, params) => send({ id: nextId++, op: "invoke", name, params })
205
226
  };
206
227
  }
207
228
  function installCustomApisBridge() {