@dlient/plugin-sdk 3.0.5 → 3.0.6
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/index.mjs +63 -12
- package/dist/index.mjs.map +1 -1
- package/dist/protocol.d.ts +21 -1
- package/dist/vite-config.d.ts +6 -0
- package/dist/vite-config.mjs +49 -1
- package/dist/vite-config.mjs.map +1 -1
- package/dist/worker.d.ts +20 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7,17 +7,21 @@ function localizeText(text, locale, fallback = "") {
|
|
|
7
7
|
return text[locale] ?? text["default"] ?? fallback;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
const POOL_CTX_KEY = "__dlientPoolCtx";
|
|
11
|
+
const RPC_TABLE_KEY = "__dlientRpcTable";
|
|
12
|
+
function createWorkerRpc(pluginId, generation, options) {
|
|
11
13
|
const handlers = /* @__PURE__ */ new Map();
|
|
12
14
|
const streamHandlers = /* @__PURE__ */ new Set();
|
|
13
15
|
let messageId = 0;
|
|
14
16
|
const pending = /* @__PURE__ */ new Map();
|
|
15
17
|
let directPort = null;
|
|
16
18
|
const inFlight = /* @__PURE__ */ new Map();
|
|
19
|
+
let snapshotHandler;
|
|
20
|
+
let restoreHandler;
|
|
21
|
+
const poolCtx = globalThis[POOL_CTX_KEY];
|
|
22
|
+
const ctl = options?.controlPort ?? poolCtx?.controlPort ?? (typeof process !== "undefined" ? process.parentPort : void 0);
|
|
17
23
|
const sendMessage = (msg) => {
|
|
18
|
-
|
|
19
|
-
process.parentPort.postMessage(msg);
|
|
20
|
-
}
|
|
24
|
+
ctl?.postMessage(msg);
|
|
21
25
|
};
|
|
22
26
|
const sendDirect = (msg) => {
|
|
23
27
|
if (!directPort) return;
|
|
@@ -38,6 +42,16 @@ function createWorkerRpc(pluginId, generation) {
|
|
|
38
42
|
break;
|
|
39
43
|
}
|
|
40
44
|
};
|
|
45
|
+
const setupDirectPort = (port) => {
|
|
46
|
+
directPort = port;
|
|
47
|
+
port.on("message", (payload) => handleDirectMessage(payload));
|
|
48
|
+
port.start?.();
|
|
49
|
+
sendMessage({ type: "direct-port-ready", pluginId });
|
|
50
|
+
};
|
|
51
|
+
const directInit = options?.directPort ?? poolCtx?.directPort;
|
|
52
|
+
if (directInit) setupDirectPort(directInit);
|
|
53
|
+
const rpcTable = globalThis[RPC_TABLE_KEY] ??= {};
|
|
54
|
+
rpcTable[pluginId] = { setDirectPort: setupDirectPort };
|
|
41
55
|
function runDirectHandler(msg, isStream) {
|
|
42
56
|
const handler = handlers.get(msg.method);
|
|
43
57
|
if (!handler) {
|
|
@@ -70,9 +84,9 @@ function createWorkerRpc(pluginId, generation) {
|
|
|
70
84
|
inFlight.delete(msg.request_id);
|
|
71
85
|
});
|
|
72
86
|
}
|
|
73
|
-
if (
|
|
87
|
+
if (ctl) {
|
|
74
88
|
let lastSeenAt = Date.now();
|
|
75
|
-
|
|
89
|
+
ctl.on("message", (event) => {
|
|
76
90
|
const e = event;
|
|
77
91
|
const msg = e.data;
|
|
78
92
|
lastSeenAt = Date.now();
|
|
@@ -83,12 +97,7 @@ function createWorkerRpc(pluginId, generation) {
|
|
|
83
97
|
}
|
|
84
98
|
case "port-setup": {
|
|
85
99
|
const [port] = e.ports ?? [];
|
|
86
|
-
if (port)
|
|
87
|
-
directPort = port;
|
|
88
|
-
port.on("message", (payload) => handleDirectMessage(payload));
|
|
89
|
-
if (typeof port.start === "function") port.start();
|
|
90
|
-
sendMessage({ type: "direct-port-ready", pluginId });
|
|
91
|
-
}
|
|
100
|
+
if (port) setupDirectPort(port);
|
|
92
101
|
break;
|
|
93
102
|
}
|
|
94
103
|
case "rpc-call":
|
|
@@ -97,8 +106,34 @@ function createWorkerRpc(pluginId, generation) {
|
|
|
97
106
|
case "host-api-response":
|
|
98
107
|
handleHostApiResponse(msg);
|
|
99
108
|
break;
|
|
109
|
+
case "snapshot-request": {
|
|
110
|
+
const req = msg;
|
|
111
|
+
if (snapshotHandler) {
|
|
112
|
+
Promise.resolve().then(() => snapshotHandler()).then((snapshot) => {
|
|
113
|
+
const res = { type: "snapshot-response", pluginId, request_id: req.request_id, snapshot };
|
|
114
|
+
sendMessage(res);
|
|
115
|
+
}).catch((err) => {
|
|
116
|
+
const res = {
|
|
117
|
+
type: "snapshot-response",
|
|
118
|
+
pluginId,
|
|
119
|
+
request_id: req.request_id,
|
|
120
|
+
error: err instanceof Error ? err.message : String(err)
|
|
121
|
+
};
|
|
122
|
+
sendMessage(res);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "snapshot-restore": {
|
|
128
|
+
const res = msg;
|
|
129
|
+
if (restoreHandler) {
|
|
130
|
+
void Promise.resolve().then(() => restoreHandler(res.snapshot)).catch((err) => console.error(`[plugin:${pluginId}] snapshot restore failed:`, err));
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
100
134
|
}
|
|
101
135
|
});
|
|
136
|
+
ctl.start?.();
|
|
102
137
|
setInterval(() => {
|
|
103
138
|
sendMessage({ type: "heartbeat", pluginId, ts: Date.now() });
|
|
104
139
|
}, 1e4);
|
|
@@ -149,6 +184,16 @@ function createWorkerRpc(pluginId, generation) {
|
|
|
149
184
|
}
|
|
150
185
|
}
|
|
151
186
|
};
|
|
187
|
+
handlers.set("webview:create", ([opts]) => callHostApiImpl("webview.create", [opts]));
|
|
188
|
+
handlers.set("webview:update", ([opts]) => callHostApiImpl("webview.update", [opts]));
|
|
189
|
+
handlers.set("webview:destroy", ([viewId]) => callHostApiImpl("webview.destroy", [String(viewId ?? "")]));
|
|
190
|
+
handlers.set("webview:webContents:call", ([opts]) => callHostApiImpl("webview.webContentsCall", [opts]));
|
|
191
|
+
handlers.set("onWebContentsEvent", ([payload]) => {
|
|
192
|
+
const { viewId, name, args } = payload ?? {};
|
|
193
|
+
if (!viewId || !name) return;
|
|
194
|
+
const msg = { type: "push", pluginId, event: "webview:event", data: { viewId, name, args } };
|
|
195
|
+
sendMessage(msg);
|
|
196
|
+
});
|
|
152
197
|
return {
|
|
153
198
|
getPluginId: () => pluginId,
|
|
154
199
|
getGeneration: () => generation,
|
|
@@ -156,6 +201,12 @@ function createWorkerRpc(pluginId, generation) {
|
|
|
156
201
|
handlers.set(method, handler);
|
|
157
202
|
streamHandlers.delete(method);
|
|
158
203
|
},
|
|
204
|
+
registerSnapshotHandler: (handler) => {
|
|
205
|
+
snapshotHandler = handler;
|
|
206
|
+
},
|
|
207
|
+
registerRestoreHandler: (handler) => {
|
|
208
|
+
restoreHandler = handler;
|
|
209
|
+
},
|
|
159
210
|
registerStreamHandler: (method, handler) => {
|
|
160
211
|
handlers.set(method, handler);
|
|
161
212
|
streamHandlers.add(method);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/types.ts","../src/worker.ts"],"sourcesContent":["/**\n * 领域模型:PluginManifest / PluginPermission / PluginRuntime / PluginInstalled / PluginType。\n * manifest 对应插件 package.json 的 `dlient` 子对象。\n * 该文件是类型与工具函数的事实来源(自 core 迁入),core 反向引用本包。\n */\n\n/** 本地化文案:字符串(默认值)或多语言映射(键为 locale,如 'zh-CN'/'en-US';'default' 键兜底) */\nexport type LocalizedText = string | Record<string, string>\n\n/**\n * expose 方法调用权限(docs/v3/plugin-view.md §2.2):\n * - private 仅本插件自己;\n * - default(缺省) 宿主(主进程侧)+ 本插件自己;\n * - system system 插件可默认调用,非 system 插件不可调;\n * - public 所有插件,无需 dependencies 声明;\n * - install-confirm 安装时用户确认(永久有效),须在 dependencies 中声明;\n * - runtime-confirm 使用时用户确认(1 天内有效),须在 dependencies 中声明。\n * 支持组合表达式:'|' = 或(满足任一),'&' = 且(需全部满足)。\n * 例:`runtime-confirm | system` = system 直接调用,其它插件需运行时确认。\n */\nexport type ExposeAccess =\n | 'private'\n | 'default'\n | 'system'\n | 'public'\n | 'install-confirm'\n | 'runtime-confirm'\n\n/** expose 方法条目 */\nexport interface PluginExposeMethod {\n /** 方法说明(确认框 / 上传校验文案用) */\n description?: string\n /** 调用权限表达式(缺省 'default') */\n access?: string\n}\n\nexport interface PluginManifest {\n id: string // 插件唯一 ID\n version: string // semver\n /** 显示名:字符串或多语言映射(键为 locale,如 'zh-CN'/'en-US';'default' 兜底) */\n name: LocalizedText\n /** 描述:字符串或多语言映射(同上) */\n description?: LocalizedText\n /** dist 目录相对插件根目录的路径(默认 'dist';开发插件可为任意外部目录的构建输出) */\n dist?: string\n /**\n * 产物打包方式(方案 build.md 2.3):\n * - 'plain'(缺省):多文件 dist 目录,dlientV3:// 协议直接读磁盘文件;\n * - 'asar':发布时 dist 打成 <pluginDir>/plugin.asar,协议与 worker fork 从 asar 内读取。\n */\n packaging?: 'plain' | 'asar'\n /**\n * 插件类型:\n * - app:独立应用,可在基座中直接启动(启动后打开页面);\n * - full / worker / ui:提供能力给其它插件调用的常规插件。\n * 缺省 'ui'。\n */\n type?: PluginType\n /** 是否为系统级插件(不可卸载;基座首次启动时从服务器下载并安装) */\n system?: boolean\n /** 来源:market=市场;local=本地;dev=开发中(缺省由加载上下文决定) */\n source?: PluginSource\n /** 图标:相对插件根目录的路径(如 \"icon.svg\" / \"assets/icon.svg\") */\n icon?: string\n /** 插件标签(市场展示 / 检索;如 [\"ai\", \"chat\"]) */\n tags?: string[]\n /** 所属组织(组织标识,形如 \"@xxx\") */\n organization?: string\n requires?: string[] // 依赖的插件 ID\n platforms?: PluginPlatform[] // 支持平台(缺省=全平台)\n permissions?: PluginPermission[] // 申请的宿主能力\n /** 对外暴露给其它插件调用的方法(plugin.invoke 校验;access 见 ExposeAccess) */\n expose?: Record<string, PluginExposeMethod>\n /** 声明要调用的其它插件方法(plugin.invoke 校验) */\n dependencies?: Record<string, string[]>\n engines?: {\n dlient?: string\n electron?: string\n }\n author?: string\n homepage?: string\n repository?: string\n}\n\nexport type PluginPlatform = 'darwin' | 'win32' | 'linux'\n\nexport type PluginPermission =\n | 'webview.create'\n | 'webview.navigate'\n | 'fs.read'\n | 'fs.write'\n | 'fs.delete'\n | 'os.openExternal'\n | 'clipboard.read'\n | 'clipboard.write'\n | 'notifications'\n | 'system.getIdleState'\n // app.* 宿主能力\n | 'app.data' // 插件数据隔离存储(USER_DATA/plugin-data/<pluginId>/)\n | 'app.setAutoLaunch' // 开机自启动\n | 'app.shortcut.register' // 快捷键注册\n | 'app.crypt' // 每插件独立密钥加解密(主进程内部派生,插件间隔离)\n | 'app.window' // 窗口控制(无头模式自绘标题栏:关闭窗口等)\n | 'plugins.dev' // 开发插件管理(外部目录注册 / 移除 / 热重载)\n\nexport interface PluginRuntime {\n id: string\n version: string\n status: 'stopped' | 'starting' | 'running' | 'error' | 'stopping'\n generation: number\n enabled: boolean\n error?: string\n startTime?: number\n}\n\nexport interface PluginInstalled {\n id: string\n version: string\n installedAt: number\n enabled: boolean\n updateAvailable?: string\n}\n\nexport type PluginType = 'full' | 'worker' | 'ui' | 'app'\n\n/** 插件来源:market=市场安装;local=本地导入;dev=开发中 */\nexport type PluginSource = 'market' | 'local' | 'dev'\n\n/** 缺省推断:显式声明优先;未声明按 'ui'(无 worker 依赖,最安全) */\nexport function inferPluginType(manifest: PluginManifest): PluginType {\n return manifest.type ?? 'ui'\n}\n\n/** 解析本地化文案:locale 无匹配时回退 'default' 键 → 字符串本身 → fallback */\nexport function localizeText(text: LocalizedText | undefined, locale: string, fallback = ''): string {\n if (!text) return fallback\n if (typeof text === 'string') return text\n return text[locale] ?? text['default'] ?? fallback\n}\n","/**\n * createWorkerRpc - 插件 worker(utilityProcess)端辅助。\n * 双通道:\n * - parentPort 主通道:init / rpc-call(主进程 invokePlugin 转发)/ host-api / 心跳 / port-setup\n * - directPort 直连通道(MessageChannelMain):渲染层 preload 直连\n * request/listen → 执行 registerHandler/registerStreamHandler → 自动按 request_id 回传\n * 插件开发者不感知 port 与 request_id:handler(args, ctx) 即可。\n */\n\nimport type {\n PluginMessage,\n RpcResponseMessage,\n HostApiCallMessage,\n HostApiResponseMessage,\n PushMessage,\n DirectRequestMessage,\n DirectListenMessage,\n DirectPortMessage,\n} from './protocol'\n\n/** Electron utilityProcess 的 parentPort(Node 全局 Process 类型无此成员,此处扩展) */\ninterface UtilityParentPort {\n postMessage(message: unknown, transfer?: unknown[]): void\n on(event: 'message', listener: (message: unknown) => void): void\n}\n\ndeclare global {\n namespace NodeJS {\n interface Process {\n parentPort?: UtilityParentPort\n }\n }\n}\n\ntype WorkerRpcHandler = (args: unknown[], ctx?: HandlerContext) => unknown | Promise<unknown>\n\n/** handler 执行上下文(directPort 直连调用时填充) */\nexport interface HandlerContext {\n from_plugin_id: string\n request_id: string\n /** 请求携带的大块数据(transfer 零拷贝) */\n data?: ArrayBuffer\n /** 流式发射(仅 registerStreamHandler / listen 模式可用) */\n emit?: (chunk?: unknown, opts?: { data?: ArrayBuffer }) => void\n /** 取消信号(UI 发 cancel 时 abort) */\n signal: AbortSignal\n}\n\n/** rpc.api:按模块分层的宿主能力调用入口(Proxy 动态生成,无需预先枚举) */\nexport type HostApi = Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>>\n\nexport interface WorkerRpc {\n registerHandler: (method: string, handler: WorkerRpcHandler) => void\n /** 流式 handler:ctx.emit(chunk) 多次,return 后自动发 stream-done */\n registerStreamHandler: (method: string, handler: WorkerRpcHandler) => void\n /** 跨插件调用:经主进程 plugin.invoke(expose + dependencies 校验) */\n callPlugin: (pluginId: string, method: string, args?: unknown[]) => Promise<unknown>\n /** 宿主能力调用(字符串方法名方式) */\n callHostApi: (method: string, args?: unknown[]) => Promise<unknown>\n /**\n * 宿主能力分层调用:rpc.api.{module}.{method}(...args),\n * 等价 rpc.callHostApi('{module}.{method}', args)。\n * 参数格式与 Electron 官方 API 一致(模块与接口见 docs/v3/worker_api.md)。\n */\n api: HostApi\n /** 推送事件给绑定本插件的渲染层 view(经主进程转发) */\n push: (event: string, data?: unknown) => void\n getPluginId: () => string\n getGeneration: () => number\n log: (level: 'info' | 'warn' | 'error' | 'debug', message: string, data?: unknown) => void\n}\n\nexport function createWorkerRpc(pluginId: string, generation: number): WorkerRpc {\n const handlers = new Map<string, WorkerRpcHandler>()\n const streamHandlers = new Set<string>()\n let messageId = 0\n const pending = new Map<string, { resolve: (value: unknown) => void; reject: (err: Error) => void }>()\n /** directPort(渲染层直连通道);port-setup 消息时设置 */\n let directPort: { postMessage: (msg: unknown, transfer?: unknown[]) => void; on: (event: string, cb: (...args: unknown[]) => void) => void; start?: () => void } | null = null\n /** 在途流式/请求的 AbortController(cancel 时 abort) */\n const inFlight = new Map<string, AbortController>()\n\n const sendMessage = (msg: PluginMessage) => {\n if (typeof process !== 'undefined' && process.parentPort) {\n process.parentPort.postMessage(msg)\n }\n }\n\n // ---- directPort 直连消息处理(渲染层 preload 发出)----\n\n /**\n * 注意:utilityProcess 下发的 directPort 是 Electron MessagePortMain,\n * 其 postMessage 的 transfer 仅接受 MessagePortMain[](不支持 ArrayBuffer),\n * 且传 undefined 第二参也会抛 \"transferables must be an array of MessagePorts\"。\n * 因此 data 字段一律随消息结构化克隆(深拷贝),不启用 transfer。\n */\n const sendDirect = (msg: DirectPortMessage) => {\n if (!directPort) return\n directPort.postMessage(msg)\n }\n\n const handleDirectMessage = (event: { data: DirectPortMessage }) => {\n const msg = event.data\n switch (msg.type) {\n case 'request':\n runDirectHandler(msg, false)\n break\n case 'listen':\n runDirectHandler(msg, true)\n break\n case 'cancel':\n inFlight.get(msg.request_id)?.abort()\n inFlight.delete(msg.request_id)\n break\n }\n }\n\n /** 执行 directPort 请求并自动按 request_id 回传(并发:每条消息独立 Promise,不互相等待) */\n function runDirectHandler(msg: DirectRequestMessage | DirectListenMessage, isStream: boolean): void {\n const handler = handlers.get(msg.method)\n if (!handler) {\n sendDirect({ type: 'error', request_id: msg.request_id, error: `Method \"${msg.method}\" not found` })\n return\n }\n const ac = new AbortController()\n inFlight.set(msg.request_id, ac)\n\n const emit = (chunk?: unknown, opts?: { data?: ArrayBuffer }) => {\n sendDirect({ type: 'stream', request_id: msg.request_id, chunk, data: opts?.data })\n }\n\n const ctx: HandlerContext = {\n from_plugin_id: msg.from_plugin_id,\n request_id: msg.request_id,\n data: msg.data,\n signal: ac.signal,\n emit: isStream ? emit : undefined,\n }\n\n Promise.resolve()\n .then(() => handler(msg.args, ctx))\n .then((result) => {\n if (ac.signal.aborted) return\n const data = (result as { data?: ArrayBuffer } | null | undefined)?.data\n const value = data !== undefined ? (result as { result?: unknown }).result : result\n sendDirect(\n isStream\n ? { type: 'stream-done', request_id: msg.request_id, result: value }\n : { type: 'response', request_id: msg.request_id, result: value, data },\n )\n })\n .catch((err) => {\n if (ac.signal.aborted) return\n sendDirect({ type: 'error', request_id: msg.request_id, error: err instanceof Error ? err.message : String(err) })\n })\n .finally(() => {\n inFlight.delete(msg.request_id)\n })\n }\n\n if (typeof process !== 'undefined' && process.parentPort) {\n // 主进程最近一次有消息的时间(心跳 / rpc-call / host-api-response 等均代表主进程存活)\n let lastSeenAt = Date.now()\n\n process.parentPort.on('message', (event: unknown) => {\n const e = event as { data: PluginMessage; ports?: unknown[] }\n const msg = e.data\n lastSeenAt = Date.now()\n switch (msg.type) {\n case 'init': {\n sendMessage({ type: 'ready', pluginId, generation, capabilities: Array.from(handlers.keys()) })\n break\n }\n case 'port-setup': {\n // 主进程下发直连 port → 设置 directPort 监听 → 通知主进程(据此下发 port1 给渲染层)\n const [port] = (e.ports ?? []) as Array<{\n postMessage: (msg: unknown, transfer?: unknown[]) => void\n on: (event: string, cb: (...args: unknown[]) => void) => void\n start: () => void\n }>\n if (port) {\n directPort = port\n port.on('message', (payload: unknown) => handleDirectMessage(payload as { data: DirectPortMessage }))\n if (typeof port.start === 'function') port.start()\n sendMessage({ type: 'direct-port-ready', pluginId })\n }\n break\n }\n case 'rpc-call':\n handleRpcCall(msg as never)\n break\n case 'host-api-response':\n handleHostApiResponse(msg as HostApiResponseMessage)\n break\n case 'heartbeat':\n // 主进程存活信号:仅刷新 lastSeenAt(已在开头统一处理)\n break\n }\n })\n\n // worker → 主进程心跳:每 10s 上报一次,主进程据此判定 worker 是否失联\n setInterval(() => {\n sendMessage({ type: 'heartbeat', pluginId, ts: Date.now() })\n }, 10000)\n\n // worker 侧失联 watchdog:主进程心跳每 10s 一条,超过 30s 未收到任何消息\n setInterval(() => {\n if (Date.now() - lastSeenAt > 30000) {\n const error = new Error('Main process lost (no message for 30s)')\n for (const { reject } of pending.values()) reject(error)\n pending.clear()\n }\n }, 5000)\n }\n\n const handleRpcCall = (msg: { id: string; method: string; args: unknown[] }) => {\n const handler = handlers.get(msg.method)\n const response: RpcResponseMessage = { type: 'rpc-response', id: msg.id, pluginId }\n if (!handler) {\n response.error = `Method \"${msg.method}\" not found`\n } else {\n try {\n const result = handler(msg.args)\n if (result instanceof Promise) {\n result.then(\n (value) => { response.result = value; sendMessage(response) },\n (err) => { response.error = err.message; sendMessage(response) },\n )\n return\n }\n response.result = result\n } catch (err) {\n response.error = err instanceof Error ? err.message : String(err)\n }\n }\n sendMessage(response)\n }\n\n const handleHostApiResponse = (msg: HostApiResponseMessage) => {\n const pendingItem = pending.get(msg.id)\n if (pendingItem) {\n pending.delete(msg.id)\n if (msg.error) {\n pendingItem.reject(new Error(msg.error))\n } else {\n pendingItem.resolve(msg.result)\n }\n }\n }\n\n return {\n getPluginId: () => pluginId,\n getGeneration: () => generation,\n\n registerHandler: (method: string, handler: WorkerRpcHandler) => {\n handlers.set(method, handler)\n streamHandlers.delete(method)\n },\n\n registerStreamHandler: (method: string, handler: WorkerRpcHandler) => {\n handlers.set(method, handler)\n streamHandlers.add(method)\n },\n\n callHostApi: (method: string, args: unknown[] = []) => callHostApiImpl(method, args),\n\n /** rpc.api.{module}.{method}(...args) → callHostApi('{module}.{method}', args) */\n api: createApiProxy(),\n\n callPlugin: (targetPluginId: string, method: string, args: unknown[] = []) =>\n callHostApiImpl('plugin.invoke', [targetPluginId, method, args]),\n\n push: (event: string, data?: unknown) => {\n const msg: PushMessage = { type: 'push', pluginId, event, data }\n sendMessage(msg)\n },\n\n log: (level: 'info' | 'warn' | 'error' | 'debug', message: string, data?: unknown) => {\n sendMessage({ type: 'log', pluginId, level, message, data })\n },\n }\n\n function callHostApiImpl(method: string, args: unknown[]): Promise<unknown> {\n const id = `host-api-${pluginId}-${generation}-${++messageId}`\n const msg: HostApiCallMessage = { type: 'host-api-call', id, method, args, pluginId }\n return new Promise<unknown>((resolve, reject) => {\n pending.set(id, { resolve, reject })\n sendMessage(msg)\n })\n }\n\n /**\n * 分层 api 代理:访问 rpc.api.{module}.{method} 时动态生成调用函数。\n * 模块与方法名直接拼成 host-api 方法 id(\"{module}.{method}\"),与主进程方法表一致;\n * 参数原样透传,格式与 Electron 官方 API 一致。\n */\n function createApiProxy(): HostApi {\n const moduleProxies = new Map<string, Record<string, (...args: unknown[]) => Promise<unknown>>>()\n return new Proxy({} as HostApi, {\n get: (_target, module) => {\n const mod = String(module)\n let modProxy = moduleProxies.get(mod)\n if (!modProxy) {\n modProxy = new Proxy({} as Record<string, (...args: unknown[]) => Promise<unknown>>, {\n get: (_t, method) => (...args: unknown[]) => callHostApiImpl(`${mod}.${String(method)}`, args),\n })\n moduleProxies.set(mod, modProxy)\n }\n return modProxy\n },\n })\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/types.ts","../src/worker.ts"],"sourcesContent":["/**\n * 领域模型:PluginManifest / PluginPermission / PluginRuntime / PluginInstalled / PluginType。\n * manifest 对应插件 package.json 的 `dlient` 子对象。\n * 该文件是类型与工具函数的事实来源(自 core 迁入),core 反向引用本包。\n */\n\n/** 本地化文案:字符串(默认值)或多语言映射(键为 locale,如 'zh-CN'/'en-US';'default' 键兜底) */\nexport type LocalizedText = string | Record<string, string>\n\n/**\n * expose 方法调用权限(docs/v3/plugin-view.md §2.2):\n * - private 仅本插件自己;\n * - default(缺省) 宿主(主进程侧)+ 本插件自己;\n * - system system 插件可默认调用,非 system 插件不可调;\n * - public 所有插件,无需 dependencies 声明;\n * - install-confirm 安装时用户确认(永久有效),须在 dependencies 中声明;\n * - runtime-confirm 使用时用户确认(1 天内有效),须在 dependencies 中声明。\n * 支持组合表达式:'|' = 或(满足任一),'&' = 且(需全部满足)。\n * 例:`runtime-confirm | system` = system 直接调用,其它插件需运行时确认。\n */\nexport type ExposeAccess =\n | 'private'\n | 'default'\n | 'system'\n | 'public'\n | 'install-confirm'\n | 'runtime-confirm'\n\n/** expose 方法条目 */\nexport interface PluginExposeMethod {\n /** 方法说明(确认框 / 上传校验文案用) */\n description?: string\n /** 调用权限表达式(缺省 'default') */\n access?: string\n}\n\nexport interface PluginManifest {\n id: string // 插件唯一 ID\n version: string // semver\n /** 显示名:字符串或多语言映射(键为 locale,如 'zh-CN'/'en-US';'default' 兜底) */\n name: LocalizedText\n /** 描述:字符串或多语言映射(同上) */\n description?: LocalizedText\n /** dist 目录相对插件根目录的路径(默认 'dist';开发插件可为任意外部目录的构建输出) */\n dist?: string\n /**\n * 产物打包方式(方案 build.md 2.3):\n * - 'plain'(缺省):多文件 dist 目录,dlientV3:// 协议直接读磁盘文件;\n * - 'asar':发布时 dist 打成 <pluginDir>/plugin.asar,协议与 worker fork 从 asar 内读取。\n */\n packaging?: 'plain' | 'asar'\n /**\n * 插件类型:\n * - app:独立应用,可在基座中直接启动(启动后打开页面);\n * - full / worker / ui:提供能力给其它插件调用的常规插件。\n * 缺省 'ui'。\n */\n type?: PluginType\n /** 是否为系统级插件(不可卸载;基座首次启动时从服务器下载并安装) */\n system?: boolean\n /** 来源:market=市场;local=本地;dev=开发中(缺省由加载上下文决定) */\n source?: PluginSource\n /** 图标:相对插件根目录的路径(如 \"icon.svg\" / \"assets/icon.svg\") */\n icon?: string\n /** 插件标签(市场展示 / 检索;如 [\"ai\", \"chat\"]) */\n tags?: string[]\n /** 所属组织(组织标识,形如 \"@xxx\") */\n organization?: string\n requires?: string[] // 依赖的插件 ID\n platforms?: PluginPlatform[] // 支持平台(缺省=全平台)\n permissions?: PluginPermission[] // 申请的宿主能力\n /** 对外暴露给其它插件调用的方法(plugin.invoke 校验;access 见 ExposeAccess) */\n expose?: Record<string, PluginExposeMethod>\n /** 声明要调用的其它插件方法(plugin.invoke 校验) */\n dependencies?: Record<string, string[]>\n engines?: {\n dlient?: string\n electron?: string\n }\n author?: string\n homepage?: string\n repository?: string\n}\n\nexport type PluginPlatform = 'darwin' | 'win32' | 'linux'\n\nexport type PluginPermission =\n | 'webview.create'\n | 'webview.navigate'\n | 'fs.read'\n | 'fs.write'\n | 'fs.delete'\n | 'os.openExternal'\n | 'clipboard.read'\n | 'clipboard.write'\n | 'notifications'\n | 'system.getIdleState'\n // app.* 宿主能力\n | 'app.data' // 插件数据隔离存储(USER_DATA/plugin-data/<pluginId>/)\n | 'app.setAutoLaunch' // 开机自启动\n | 'app.shortcut.register' // 快捷键注册\n | 'app.crypt' // 每插件独立密钥加解密(主进程内部派生,插件间隔离)\n | 'app.window' // 窗口控制(无头模式自绘标题栏:关闭窗口等)\n | 'plugins.dev' // 开发插件管理(外部目录注册 / 移除 / 热重载)\n\nexport interface PluginRuntime {\n id: string\n version: string\n status: 'stopped' | 'starting' | 'running' | 'error' | 'stopping'\n generation: number\n enabled: boolean\n error?: string\n startTime?: number\n}\n\nexport interface PluginInstalled {\n id: string\n version: string\n installedAt: number\n enabled: boolean\n updateAvailable?: string\n}\n\nexport type PluginType = 'full' | 'worker' | 'ui' | 'app'\n\n/** 插件来源:market=市场安装;local=本地导入;dev=开发中 */\nexport type PluginSource = 'market' | 'local' | 'dev'\n\n/** 缺省推断:显式声明优先;未声明按 'ui'(无 worker 依赖,最安全) */\nexport function inferPluginType(manifest: PluginManifest): PluginType {\n return manifest.type ?? 'ui'\n}\n\n/** 解析本地化文案:locale 无匹配时回退 'default' 键 → 字符串本身 → fallback */\nexport function localizeText(text: LocalizedText | undefined, locale: string, fallback = ''): string {\n if (!text) return fallback\n if (typeof text === 'string') return text\n return text[locale] ?? text['default'] ?? fallback\n}\n","/**\n * createWorkerRpc - 插件 worker(utilityProcess)端辅助。\n * 双通道:\n * - parentPort 主通道:init / rpc-call(主进程 invokePlugin 转发)/ host-api / 心跳 / port-setup\n * - directPort 直连通道(MessageChannelMain):渲染层 preload 直连\n * request/listen → 执行 registerHandler/registerStreamHandler → 自动按 request_id 回传\n * 插件开发者不感知 port 与 request_id:handler(args, ctx) 即可。\n */\n\nimport type {\n PluginMessage,\n RpcResponseMessage,\n HostApiCallMessage,\n HostApiResponseMessage,\n PushMessage,\n DirectRequestMessage,\n DirectListenMessage,\n DirectPortMessage,\n SnapshotRequestMessage,\n SnapshotResponseMessage,\n SnapshotRestoreMessage,\n} from './protocol'\n\n/** Electron utilityProcess 的 parentPort(Node 全局 Process 类型无此成员,此处扩展) */\ninterface UtilityParentPort {\n postMessage(message: unknown, transfer?: unknown[]): void\n on(event: 'message', listener: (message: unknown) => void): void\n}\n\ndeclare global {\n namespace NodeJS {\n interface Process {\n parentPort?: UtilityParentPort\n }\n }\n}\n\n/**\n * 通道(控制面 / 直连):兼容 Electron MessagePortMain 与 utilityProcess parentPort。\n * 共享池模式下控制/直连通道由池运行时注入(见 pool-worker.ts),独立进程模式用 process.parentPort。\n */\ninterface RpcChannel {\n postMessage(message: unknown, transfer?: unknown[]): void\n on(event: 'message', listener: (event: { data: unknown; ports?: unknown[] }) => void): void\n start?(): void\n}\n\n/** 池上下文:pool-worker 加载插件前设置,createWorkerRpc 同步读取 */\ninterface PoolContext {\n pluginId?: string\n controlPort?: RpcChannel\n directPort?: RpcChannel\n}\n\n/** 池运行时读写全局键(与 pool-worker.ts 一致) */\nconst POOL_CTX_KEY = '__dlientPoolCtx'\nconst RPC_TABLE_KEY = '__dlientRpcTable'\n\ntype WorkerRpcHandler = (args: unknown[], ctx?: HandlerContext) => unknown | Promise<unknown>\n\n/** handler 执行上下文(directPort 直连调用时填充) */\nexport interface HandlerContext {\n from_plugin_id: string\n request_id: string\n /** 请求携带的大块数据(transfer 零拷贝) */\n data?: ArrayBuffer\n /** 流式发射(仅 registerStreamHandler / listen 模式可用) */\n emit?: (chunk?: unknown, opts?: { data?: ArrayBuffer }) => void\n /** 取消信号(UI 发 cancel 时 abort) */\n signal: AbortSignal\n}\n\n/** rpc.api:按模块分层的宿主能力调用入口(Proxy 动态生成,无需预先枚举) */\nexport type HostApi = Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>>\n\nexport interface WorkerRpc {\n registerHandler: (method: string, handler: WorkerRpcHandler) => void\n /** 流式 handler:ctx.emit(chunk) 多次,return 后自动发 stream-done */\n registerStreamHandler: (method: string, handler: WorkerRpcHandler) => void\n /** 跨插件调用:经主进程 plugin.invoke(expose + dependencies 校验) */\n callPlugin: (pluginId: string, method: string, args?: unknown[]) => Promise<unknown>\n /** 宿主能力调用(字符串方法名方式) */\n callHostApi: (method: string, args?: unknown[]) => Promise<unknown>\n /**\n * 宿主能力分层调用:rpc.api.{module}.{method}(...args),\n * 等价 rpc.callHostApi('{module}.{method}', args)。\n * 参数格式与 Electron 官方 API 一致(模块与接口见 docs/v3/worker_api.md)。\n */\n api: HostApi\n /** 推送事件给绑定本插件的渲染层 view(经主进程转发) */\n push: (event: string, data?: unknown) => void\n /** 业务状态快照:热重载/重启前主进程请求(1s 窗口),插件实现返回可序列化状态 */\n registerSnapshotHandler: (handler: () => unknown | Promise<unknown>) => void\n /** 业务状态恢复:重启后主进程回灌快照,插件据此恢复状态 */\n registerRestoreHandler: (handler: (snapshot: unknown) => void | Promise<void>) => void\n getPluginId: () => string\n getGeneration: () => number\n log: (level: 'info' | 'warn' | 'error' | 'debug', message: string, data?: unknown) => void\n}\n\nexport function createWorkerRpc(\n pluginId: string,\n generation: number,\n options?: { controlPort?: RpcChannel; directPort?: RpcChannel },\n): WorkerRpc {\n const handlers = new Map<string, WorkerRpcHandler>()\n const streamHandlers = new Set<string>()\n let messageId = 0\n const pending = new Map<string, { resolve: (value: unknown) => void; reject: (err: Error) => void }>()\n /** directPort(渲染层直连通道);池注入或 port-setup 消息时设置 */\n let directPort: RpcChannel | null = null\n /** 在途流式/请求的 AbortController(cancel 时 abort) */\n const inFlight = new Map<string, AbortController>()\n /** 业务状态快照/恢复 handler(热重载/重启用;未注册则跳过快照) */\n let snapshotHandler: (() => unknown | Promise<unknown>) | undefined\n let restoreHandler: ((snapshot: unknown) => void | Promise<void>) | undefined\n\n // 通道解析:池上下文(pool-worker 注入)优先,其次显式 options,最后 process.parentPort(独立进程)\n const poolCtx = (globalThis as unknown as Record<string, unknown>)[POOL_CTX_KEY] as PoolContext | undefined\n const ctl: RpcChannel | undefined =\n options?.controlPort ??\n poolCtx?.controlPort ??\n (typeof process !== 'undefined' ? (process.parentPort as RpcChannel | undefined) : undefined)\n\n const sendMessage = (msg: PluginMessage) => {\n ctl?.postMessage(msg)\n }\n\n // ---- directPort 直连消息处理(渲染层 preload 发出)----\n\n /**\n * 注意:utilityProcess 下发的 directPort 是 Electron MessagePortMain,\n * 其 postMessage 的 transfer 仅接受 MessagePortMain[](不支持 ArrayBuffer),\n * 且传 undefined 第二参也会抛 \"transferables must be an array of MessagePorts\"。\n * 因此 data 字段一律随消息结构化克隆(深拷贝),不启用 transfer。\n */\n const sendDirect = (msg: DirectPortMessage) => {\n if (!directPort) return\n directPort.postMessage(msg)\n }\n\n const handleDirectMessage = (event: { data: DirectPortMessage }) => {\n const msg = event.data\n switch (msg.type) {\n case 'request':\n runDirectHandler(msg, false)\n break\n case 'listen':\n runDirectHandler(msg, true)\n break\n case 'cancel':\n inFlight.get(msg.request_id)?.abort()\n inFlight.delete(msg.request_id)\n break\n }\n }\n\n /** 设置并监听直连通道(池注入 directPort / port-setup 消息 / reattach-direct 共用) */\n const setupDirectPort = (port: RpcChannel) => {\n directPort = port\n port.on('message', (payload: unknown) => handleDirectMessage(payload as { data: DirectPortMessage }))\n port.start?.()\n sendMessage({ type: 'direct-port-ready', pluginId })\n }\n\n // 池模式直连通道注入(pool-worker 随 load-plugin transfer 的 direct port;同步生效)\n const directInit = options?.directPort ?? poolCtx?.directPort\n if (directInit) setupDirectPort(directInit)\n\n // 注册到池 rpc 表:池运行时重建直连通道(reattach-direct 消息)时按 pluginId 调用\n // (独立进程模式无 pool-worker,表仅被池运行时消费;全局键与 pool-worker.ts 一致)\n const rpcTable = ((globalThis as unknown as Record<string, unknown>)[RPC_TABLE_KEY] ??= {}) as Record<\n string,\n { setDirectPort?: (port: RpcChannel) => void }\n >\n rpcTable[pluginId] = { setDirectPort: setupDirectPort }\n\n /** 执行 directPort 请求并自动按 request_id 回传(并发:每条消息独立 Promise,不互相等待) */\n function runDirectHandler(msg: DirectRequestMessage | DirectListenMessage, isStream: boolean): void {\n const handler = handlers.get(msg.method)\n if (!handler) {\n sendDirect({ type: 'error', request_id: msg.request_id, error: `Method \"${msg.method}\" not found` })\n return\n }\n const ac = new AbortController()\n inFlight.set(msg.request_id, ac)\n\n const emit = (chunk?: unknown, opts?: { data?: ArrayBuffer }) => {\n sendDirect({ type: 'stream', request_id: msg.request_id, chunk, data: opts?.data })\n }\n\n const ctx: HandlerContext = {\n from_plugin_id: msg.from_plugin_id,\n request_id: msg.request_id,\n data: msg.data,\n signal: ac.signal,\n emit: isStream ? emit : undefined,\n }\n\n Promise.resolve()\n .then(() => handler(msg.args, ctx))\n .then((result) => {\n if (ac.signal.aborted) return\n const data = (result as { data?: ArrayBuffer } | null | undefined)?.data\n const value = data !== undefined ? (result as { result?: unknown }).result : result\n sendDirect(\n isStream\n ? { type: 'stream-done', request_id: msg.request_id, result: value }\n : { type: 'response', request_id: msg.request_id, result: value, data },\n )\n })\n .catch((err) => {\n if (ac.signal.aborted) return\n sendDirect({ type: 'error', request_id: msg.request_id, error: err instanceof Error ? err.message : String(err) })\n })\n .finally(() => {\n inFlight.delete(msg.request_id)\n })\n }\n\n if (ctl) {\n // 主进程最近一次有消息的时间(心跳 / rpc-call / host-api-response 等均代表主进程存活)\n let lastSeenAt = Date.now()\n\n ctl.on('message', (event: unknown) => {\n const e = event as { data: PluginMessage; ports?: unknown[] }\n const msg = e.data\n lastSeenAt = Date.now()\n switch (msg.type) {\n case 'init': {\n sendMessage({ type: 'ready', pluginId, generation, capabilities: Array.from(handlers.keys()) })\n break\n }\n case 'port-setup': {\n // 独立进程模式:主进程下发直连 port → 复用 setupDirectPort(池模式经 load-plugin 直接注入)\n const [port] = (e.ports ?? []) as RpcChannel[]\n if (port) setupDirectPort(port)\n break\n }\n case 'rpc-call':\n handleRpcCall(msg as never)\n break\n case 'host-api-response':\n handleHostApiResponse(msg as HostApiResponseMessage)\n break\n case 'snapshot-request': {\n // 主进程重启前请求快照:1s 窗口内回 snapshot-response;未注册 handler 则静默不回\n const req = msg as SnapshotRequestMessage\n if (snapshotHandler) {\n Promise.resolve()\n .then(() => snapshotHandler!())\n .then((snapshot) => {\n const res: SnapshotResponseMessage = { type: 'snapshot-response', pluginId, request_id: req.request_id, snapshot }\n sendMessage(res)\n })\n .catch((err) => {\n const res: SnapshotResponseMessage = {\n type: 'snapshot-response',\n pluginId,\n request_id: req.request_id,\n error: err instanceof Error ? err.message : String(err),\n }\n sendMessage(res)\n })\n }\n break\n }\n case 'snapshot-restore': {\n // 重启后主进程回灌快照(消息保序:先于后续 rpc-call 到达)\n const res = msg as SnapshotRestoreMessage\n if (restoreHandler) {\n void Promise.resolve()\n .then(() => restoreHandler!(res.snapshot))\n .catch((err) => console.error(`[plugin:${pluginId}] snapshot restore failed:`, err))\n }\n break\n }\n case 'heartbeat':\n // 主进程存活信号:仅刷新 lastSeenAt(已在开头统一处理)\n break\n }\n })\n // 池模式:ctl 是注入的 MessagePortMain,必须显式 start() 才会开始接收消息\n //(独立进程模式为 process.parentPort,无 start 方法,可选链安全跳过)。\n // 缺少 start 时 worker 收不到主进程 heartbeat / host-api-response → watchdog 误报\n // 「Main process lost (no message for 30s)」,所有 host-api 调用与渲染层请求全部超时。\n ctl.start?.()\n\n // worker → 主进程心跳:每 10s 上报一次,主进程据此判定 worker 是否失联\n setInterval(() => {\n sendMessage({ type: 'heartbeat', pluginId, ts: Date.now() })\n }, 10000)\n\n // worker 侧失联 watchdog:主进程心跳每 10s 一条,超过 30s 未收到任何消息\n setInterval(() => {\n if (Date.now() - lastSeenAt > 30000) {\n const error = new Error('Main process lost (no message for 30s)')\n for (const { reject } of pending.values()) reject(error)\n pending.clear()\n }\n }, 5000)\n }\n\n const handleRpcCall = (msg: { id: string; method: string; args: unknown[] }) => {\n const handler = handlers.get(msg.method)\n const response: RpcResponseMessage = { type: 'rpc-response', id: msg.id, pluginId }\n if (!handler) {\n response.error = `Method \"${msg.method}\" not found`\n } else {\n try {\n const result = handler(msg.args)\n if (result instanceof Promise) {\n result.then(\n (value) => { response.result = value; sendMessage(response) },\n (err) => { response.error = err.message; sendMessage(response) },\n )\n return\n }\n response.result = result\n } catch (err) {\n response.error = err instanceof Error ? err.message : String(err)\n }\n }\n sendMessage(response)\n }\n\n const handleHostApiResponse = (msg: HostApiResponseMessage) => {\n const pendingItem = pending.get(msg.id)\n if (pendingItem) {\n pending.delete(msg.id)\n if (msg.error) {\n pendingItem.reject(new Error(msg.error))\n } else {\n pendingItem.resolve(msg.result)\n }\n }\n }\n\n // ---- 内建 webview 能力转发(Webview 组件经「承载插件」worker 调用 hostApi)----\n // 任何插件的 worker 都自动带这些 handler:前端 <Webview>(@dlient/ui)用 useDientApi 请求\n // 自己插件的 webview:* → 这里转发到主进程 webview-manager。hostApi 侧按 manifest.permissions\n //(webview.create / webview.navigate)鉴权,未声明权限的插件调用会被主进程拒绝。\n // 插件可用 registerHandler 显式注册同名方法覆盖内建实现(显式优先)。\n handlers.set('webview:create', ([opts]) => callHostApiImpl('webview.create', [opts]))\n handlers.set('webview:update', ([opts]) => callHostApiImpl('webview.update', [opts]))\n handlers.set('webview:destroy', ([viewId]) => callHostApiImpl('webview.destroy', [String(viewId ?? '')]))\n handlers.set('webview:webContents:call', ([opts]) => callHostApiImpl('webview.webContentsCall', [opts]))\n // 主进程 webview-manager 按 owner 插件转发 webContents 白名单事件 → 推送承载插件前端组件\n handlers.set('onWebContentsEvent', ([payload]) => {\n const { viewId, name, args } = (payload ?? {}) as { viewId?: string; name?: string; args?: unknown[] }\n if (!viewId || !name) return\n const msg: PushMessage = { type: 'push', pluginId, event: 'webview:event', data: { viewId, name, args } }\n sendMessage(msg)\n })\n\n return {\n getPluginId: () => pluginId,\n getGeneration: () => generation,\n\n registerHandler: (method: string, handler: WorkerRpcHandler) => {\n handlers.set(method, handler)\n streamHandlers.delete(method)\n },\n\n registerSnapshotHandler: (handler: () => unknown | Promise<unknown>) => {\n snapshotHandler = handler\n },\n\n registerRestoreHandler: (handler: (snapshot: unknown) => void | Promise<void>) => {\n restoreHandler = handler\n },\n\n registerStreamHandler: (method: string, handler: WorkerRpcHandler) => {\n handlers.set(method, handler)\n streamHandlers.add(method)\n },\n\n callHostApi: (method: string, args: unknown[] = []) => callHostApiImpl(method, args),\n\n /** rpc.api.{module}.{method}(...args) → callHostApi('{module}.{method}', args) */\n api: createApiProxy(),\n\n callPlugin: (targetPluginId: string, method: string, args: unknown[] = []) =>\n callHostApiImpl('plugin.invoke', [targetPluginId, method, args]),\n\n push: (event: string, data?: unknown) => {\n const msg: PushMessage = { type: 'push', pluginId, event, data }\n sendMessage(msg)\n },\n\n log: (level: 'info' | 'warn' | 'error' | 'debug', message: string, data?: unknown) => {\n sendMessage({ type: 'log', pluginId, level, message, data })\n },\n }\n\n function callHostApiImpl(method: string, args: unknown[]): Promise<unknown> {\n const id = `host-api-${pluginId}-${generation}-${++messageId}`\n const msg: HostApiCallMessage = { type: 'host-api-call', id, method, args, pluginId }\n return new Promise<unknown>((resolve, reject) => {\n pending.set(id, { resolve, reject })\n sendMessage(msg)\n })\n }\n\n /**\n * 分层 api 代理:访问 rpc.api.{module}.{method} 时动态生成调用函数。\n * 模块与方法名直接拼成 host-api 方法 id(\"{module}.{method}\"),与主进程方法表一致;\n * 参数原样透传,格式与 Electron 官方 API 一致。\n */\n function createApiProxy(): HostApi {\n const moduleProxies = new Map<string, Record<string, (...args: unknown[]) => Promise<unknown>>>()\n return new Proxy({} as HostApi, {\n get: (_target, module) => {\n const mod = String(module)\n let modProxy = moduleProxies.get(mod)\n if (!modProxy) {\n modProxy = new Proxy({} as Record<string, (...args: unknown[]) => Promise<unknown>>, {\n get: (_t, method) => (...args: unknown[]) => callHostApiImpl(`${mod}.${String(method)}`, args),\n })\n moduleProxies.set(mod, modProxy)\n }\n return modProxy\n },\n })\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -140,4 +140,24 @@ export interface DirectPortReadyMessage {
|
|
|
140
140
|
type: 'direct-port-ready';
|
|
141
141
|
pluginId: string;
|
|
142
142
|
}
|
|
143
|
-
|
|
143
|
+
/** 主进程 → worker:请求业务状态快照(热重载/重启前;1s 窗口内回 snapshot-response,超时按无快照处理) */
|
|
144
|
+
export interface SnapshotRequestMessage {
|
|
145
|
+
type: 'snapshot-request';
|
|
146
|
+
pluginId: string;
|
|
147
|
+
request_id: string;
|
|
148
|
+
}
|
|
149
|
+
/** worker → 主进程:快照响应(未注册快照 handler 或超时不回) */
|
|
150
|
+
export interface SnapshotResponseMessage {
|
|
151
|
+
type: 'snapshot-response';
|
|
152
|
+
pluginId: string;
|
|
153
|
+
request_id: string;
|
|
154
|
+
snapshot?: unknown;
|
|
155
|
+
error?: string;
|
|
156
|
+
}
|
|
157
|
+
/** 主进程 → worker:恢复业务状态快照(重启后新 worker 回灌,消息保序保证先于后续调用) */
|
|
158
|
+
export interface SnapshotRestoreMessage {
|
|
159
|
+
type: 'snapshot-restore';
|
|
160
|
+
pluginId: string;
|
|
161
|
+
snapshot: unknown;
|
|
162
|
+
}
|
|
163
|
+
export type PluginMessage = InitMessage | ReadyMessage | RpcCallMessage | RpcResponseMessage | HostApiCallMessage | HostApiResponseMessage | PushMessage | HeartbeatMessage | ErrorMessage | LogMessage | PortSetupMessage | DirectPortReadyMessage | SnapshotRequestMessage | SnapshotResponseMessage | SnapshotRestoreMessage;
|
package/dist/vite-config.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* createPluginViteConfig - 插件 UI 构建预设(System.register 格式,宿主用 SystemJS 加载)。
|
|
3
|
+
*
|
|
4
|
+
* dev 模式(HMR):设 DLIENT_DEV=1 且插件 package.json 声明 dlient.devPort(或传 devPort)时,
|
|
5
|
+
* 以 appType:'custom' 启动该插件的独立 vite dev server(react-refresh + 模块级 HMR),
|
|
6
|
+
* 宿主 PluginView 经 manifest dlient.devServerUrl 加载 dev 模块。未启用时行为与纯构建一致。
|
|
3
7
|
*/
|
|
4
8
|
import { type UserConfig } from 'vite';
|
|
5
9
|
export interface PluginViteConfigOptions {
|
|
@@ -8,5 +12,7 @@ export interface PluginViteConfigOptions {
|
|
|
8
12
|
hasWorker: boolean;
|
|
9
13
|
uiEntry?: string;
|
|
10
14
|
workerEntry?: string;
|
|
15
|
+
/** dev server 端口(优先于 package.json dlient.devPort) */
|
|
16
|
+
devPort?: number;
|
|
11
17
|
}
|
|
12
18
|
export declare function createPluginViteConfig(options: PluginViteConfigOptions): UserConfig;
|
package/dist/vite-config.mjs
CHANGED
|
@@ -1,11 +1,56 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { defineConfig } from 'vite';
|
|
2
4
|
import react from '@vitejs/plugin-react';
|
|
3
5
|
|
|
4
6
|
function createPluginViteConfig(options) {
|
|
5
7
|
const { pluginDir, pluginId, uiEntry = "src/renderer/App.tsx" } = options;
|
|
8
|
+
let devPort = options.devPort;
|
|
9
|
+
if (process.env.DLIENT_DEV === "1" && devPort === void 0) {
|
|
10
|
+
try {
|
|
11
|
+
const pkg = JSON.parse(readFileSync(join(pluginDir, "package.json"), "utf-8"));
|
|
12
|
+
if (typeof pkg?.dlient?.devPort === "number") devPort = pkg.dlient.devPort;
|
|
13
|
+
} catch {
|
|
14
|
+
}
|
|
15
|
+
} else if (process.env.DLIENT_DEV !== "1") {
|
|
16
|
+
devPort = void 0;
|
|
17
|
+
}
|
|
18
|
+
const devServer = devPort !== void 0 ? {
|
|
19
|
+
appType: "custom",
|
|
20
|
+
server: {
|
|
21
|
+
host: "127.0.0.1",
|
|
22
|
+
port: devPort,
|
|
23
|
+
strictPort: true,
|
|
24
|
+
cors: true
|
|
25
|
+
}
|
|
26
|
+
} : {};
|
|
27
|
+
const SHARED_EXTERNALS = [
|
|
28
|
+
"react",
|
|
29
|
+
"react/jsx-runtime",
|
|
30
|
+
"react/jsx-dev-runtime",
|
|
31
|
+
"react-dom",
|
|
32
|
+
"react-dom/client",
|
|
33
|
+
"@dlient/api-bridge",
|
|
34
|
+
"@dlient/i18n",
|
|
35
|
+
"@dlient/ui",
|
|
36
|
+
"tdesign-react",
|
|
37
|
+
"tdesign-icons-react"
|
|
38
|
+
];
|
|
39
|
+
const devPlugins = devPort !== void 0 ? [
|
|
40
|
+
{
|
|
41
|
+
name: "dlient:dev-shared-external",
|
|
42
|
+
enforce: "pre",
|
|
43
|
+
resolveId(source) {
|
|
44
|
+
if (SHARED_EXTERNALS.includes(source)) return { id: source, external: true };
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
] : [];
|
|
6
49
|
return defineConfig({
|
|
7
50
|
root: pluginDir,
|
|
51
|
+
...devServer,
|
|
8
52
|
plugins: [
|
|
53
|
+
...devPlugins,
|
|
9
54
|
react(),
|
|
10
55
|
// System.register 格式的 JS 不产出 CSS 引用(esm 会有 import './x.css'),而插件的
|
|
11
56
|
// remoteEntry.js 由宿主经 SystemJS 动态加载(无 HTML 流程)。这里把 CSS 保留为
|
|
@@ -49,6 +94,7 @@ function createPluginViteConfig(options) {
|
|
|
49
94
|
preserveEntrySignatures: "strict",
|
|
50
95
|
// react 系与 @dlient/* 全部 external:运行时从宿主 SystemJS registry 解析单实例
|
|
51
96
|
// (见 @dlient/ui PluginView 的 ensureSystemShared)。
|
|
97
|
+
// tdesign-react / tdesign-icons-react 同理由宿主 SystemJS registry 共享(tdesign 单实例)。
|
|
52
98
|
external: [
|
|
53
99
|
"react",
|
|
54
100
|
"react/jsx-runtime",
|
|
@@ -56,7 +102,9 @@ function createPluginViteConfig(options) {
|
|
|
56
102
|
"react-dom/client",
|
|
57
103
|
"@dlient/api-bridge",
|
|
58
104
|
"@dlient/i18n",
|
|
59
|
-
"@dlient/ui"
|
|
105
|
+
"@dlient/ui",
|
|
106
|
+
"tdesign-react",
|
|
107
|
+
"tdesign-icons-react"
|
|
60
108
|
],
|
|
61
109
|
output: {
|
|
62
110
|
format: "system",
|
package/dist/vite-config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-config.mjs","sources":["../src/vite-config.ts"],"sourcesContent":["/**\n * createPluginViteConfig - 插件 UI 构建预设(System.register 格式,宿主用 SystemJS 加载)。\n */\n\nimport { defineConfig, type UserConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport interface PluginViteConfigOptions {\n pluginId: string\n pluginDir: string\n hasWorker: boolean\n uiEntry?: string\n workerEntry?: string\n}\n\nexport function createPluginViteConfig(options: PluginViteConfigOptions): UserConfig {\n const { pluginDir, pluginId, uiEntry = 'src/renderer/App.tsx' } = options\n\n return defineConfig({\n root: pluginDir,\n plugins: [\n react(),\n // System.register 格式的 JS 不产出 CSS 引用(esm 会有 import './x.css'),而插件的\n // remoteEntry.js 由宿主经 SystemJS 动态加载(无 HTML 流程)。这里把 CSS 保留为\n // 独立文件,并在入口 chunk 注入 <link>:样式经 dlientV3:// 协议加载;CSS 内 url()\n // 相对路径由浏览器按 CSS 文件位置解析,可正确引用插件内资源(不内联、无需重写)。\n {\n name: 'dlient:css-link',\n enforce: 'post',\n generateBundle(_b, bundle) {\n const css = Object.values(bundle).find(\n (b) => b.type === 'asset' && typeof b.source === 'string' && b.fileName.endsWith('.css'),\n ) as unknown as { fileName: string; source: string } | undefined\n if (!css) return\n const entry = Object.values(bundle).find((b) => b.type === 'chunk' && b.isEntry) as unknown as\n | { code: string }\n | undefined\n if (!entry) return\n // 版本戳:dev 热重载 / 插件升级后 href 变化,避免浏览器缓存旧 style.css。\n entry.code += `\\n;(function(){var l=document.createElement('link');l.rel='stylesheet';l.href='dlientV3://plugin/${pluginId}/dist/${css.fileName}?v=${Date.now()}';document.head.appendChild(l);})();`\n },\n },\n ],\n build: {\n outDir: 'dist',\n // worker 产物(dist/worker.js)与 UI 共享同一 dist,且 worker 不经 vite 构建;\n // 关闭 emptyOutDir,避免 vite build 清空 dist 时误删 worker.js。\n emptyOutDir: false,\n target: 'esnext',\n // 方案 build.md 2.2:UI 端 JS 仅压缩(不混淆——System.register 结构 / import map 裸依赖\n // / 入口导出会被混淆器破坏)。产物始终不带 sourcemap,避免源码经 dlientV3:// 泄露。\n minify: 'esbuild',\n sourcemap: false,\n // CSS 压缩(esbuild):vite 5 的 cssMinify 不跟随 minify,需显式开启。\n cssMinify: 'esbuild',\n modulePreload: false,\n cssCodeSplit: false,\n rollupOptions: {\n input: {\n main: uiEntry,\n },\n // 入口导出由宿主(SystemJS import)在运行时消费:'exports-only' 会把默认导出\n // 当作未使用 tree-shake 掉,必须 'strict' 保留入口全部导出。\n preserveEntrySignatures: 'strict',\n // react 系与 @dlient/* 全部 external:运行时从宿主 SystemJS registry 解析单实例\n // (见 @dlient/ui PluginView 的 ensureSystemShared)。\n external: [\n 'react',\n 'react/jsx-runtime',\n 'react-dom',\n 'react-dom/client',\n '@dlient/api-bridge',\n '@dlient/i18n',\n '@dlient/ui',\n ],\n output: {\n format: 'system',\n entryFileNames: 'remoteEntry.js',\n chunkFileNames: 'assets/[name].js',\n assetFileNames: 'assets/[name].[ext]',\n },\n },\n },\n } as UserConfig)\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"vite-config.mjs","sources":["../src/vite-config.ts"],"sourcesContent":["/**\n * createPluginViteConfig - 插件 UI 构建预设(System.register 格式,宿主用 SystemJS 加载)。\n *\n * dev 模式(HMR):设 DLIENT_DEV=1 且插件 package.json 声明 dlient.devPort(或传 devPort)时,\n * 以 appType:'custom' 启动该插件的独立 vite dev server(react-refresh + 模块级 HMR),\n * 宿主 PluginView 经 manifest dlient.devServerUrl 加载 dev 模块。未启用时行为与纯构建一致。\n */\n\nimport { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { defineConfig, type UserConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport interface PluginViteConfigOptions {\n pluginId: string\n pluginDir: string\n hasWorker: boolean\n uiEntry?: string\n workerEntry?: string\n /** dev server 端口(优先于 package.json dlient.devPort) */\n devPort?: number\n}\n\nexport function createPluginViteConfig(options: PluginViteConfigOptions): UserConfig {\n const { pluginDir, pluginId, uiEntry = 'src/renderer/App.tsx' } = options\n\n // ---- dev 模式(DLIENT_DEV=1):启动独立 vite dev server ----\n // 端口来源:options.devPort > package.json dlient.devPort;未声明则退回纯构建模式。\n // hmr 保持默认开启:/@react-refresh 与模块级 refresh 变换依赖它(宿主 PluginView 侧\n // 会以 SystemJS stub 屏蔽 /@vite/client 的 WS 客户端,由宿主自己驱动刷新,见 plugin-view)。\n let devPort: number | undefined = options.devPort\n if (process.env.DLIENT_DEV === '1' && devPort === undefined) {\n try {\n const pkg = JSON.parse(readFileSync(join(pluginDir, 'package.json'), 'utf-8')) as {\n dlient?: { devPort?: unknown }\n }\n if (typeof pkg?.dlient?.devPort === 'number') devPort = pkg.dlient.devPort\n } catch {\n /* 读取失败视为未声明 devPort */\n }\n } else if (process.env.DLIENT_DEV !== '1') {\n devPort = undefined\n }\n const devServer = devPort !== undefined\n ? {\n appType: 'custom' as const,\n server: {\n host: '127.0.0.1',\n port: devPort,\n strictPort: true,\n cors: true,\n },\n }\n : {}\n\n // dev 模式:宿主共享依赖(react 系 / @dlient/* / tdesign)标记为 external —— vite dev 的\n // importAnalysis 会把裸导入重写为 /node_modules/.vite/deps/*.js(插件自己的副本),破坏\n // SystemJS 共享单实例(双 React / 双 i18n / 双 tdesign 上下文)。external 后裸导入原样保留,\n // 由宿主 SystemJS import map 解析到注册表里的宿主实例,语义与生产构建 external 完全一致。\n const SHARED_EXTERNALS = [\n 'react',\n 'react/jsx-runtime',\n 'react/jsx-dev-runtime',\n 'react-dom',\n 'react-dom/client',\n '@dlient/api-bridge',\n '@dlient/i18n',\n '@dlient/ui',\n 'tdesign-react',\n 'tdesign-icons-react',\n ]\n const devPlugins = devPort !== undefined\n ? [\n {\n name: 'dlient:dev-shared-external',\n enforce: 'pre' as const,\n resolveId(source: string) {\n if (SHARED_EXTERNALS.includes(source)) return { id: source, external: true }\n return null\n },\n },\n ]\n : []\n\n return defineConfig({\n root: pluginDir,\n ...devServer,\n plugins: [\n ...devPlugins,\n react(),\n // System.register 格式的 JS 不产出 CSS 引用(esm 会有 import './x.css'),而插件的\n // remoteEntry.js 由宿主经 SystemJS 动态加载(无 HTML 流程)。这里把 CSS 保留为\n // 独立文件,并在入口 chunk 注入 <link>:样式经 dlientV3:// 协议加载;CSS 内 url()\n // 相对路径由浏览器按 CSS 文件位置解析,可正确引用插件内资源(不内联、无需重写)。\n {\n name: 'dlient:css-link',\n enforce: 'post',\n generateBundle(_b, bundle) {\n const css = Object.values(bundle).find(\n (b) => b.type === 'asset' && typeof b.source === 'string' && b.fileName.endsWith('.css'),\n ) as unknown as { fileName: string; source: string } | undefined\n if (!css) return\n const entry = Object.values(bundle).find((b) => b.type === 'chunk' && b.isEntry) as unknown as\n | { code: string }\n | undefined\n if (!entry) return\n // 版本戳:dev 热重载 / 插件升级后 href 变化,避免浏览器缓存旧 style.css。\n entry.code += `\\n;(function(){var l=document.createElement('link');l.rel='stylesheet';l.href='dlientV3://plugin/${pluginId}/dist/${css.fileName}?v=${Date.now()}';document.head.appendChild(l);})();`\n },\n },\n ],\n build: {\n outDir: 'dist',\n // worker 产物(dist/worker.js)与 UI 共享同一 dist,且 worker 不经 vite 构建;\n // 关闭 emptyOutDir,避免 vite build 清空 dist 时误删 worker.js。\n emptyOutDir: false,\n target: 'esnext',\n // 方案 build.md 2.2:UI 端 JS 仅压缩(不混淆——System.register 结构 / import map 裸依赖\n // / 入口导出会被混淆器破坏)。产物始终不带 sourcemap,避免源码经 dlientV3:// 泄露。\n minify: 'esbuild',\n sourcemap: false,\n // CSS 压缩(esbuild):vite 5 的 cssMinify 不跟随 minify,需显式开启。\n cssMinify: 'esbuild',\n modulePreload: false,\n cssCodeSplit: false,\n rollupOptions: {\n input: {\n main: uiEntry,\n },\n // 入口导出由宿主(SystemJS import)在运行时消费:'exports-only' 会把默认导出\n // 当作未使用 tree-shake 掉,必须 'strict' 保留入口全部导出。\n preserveEntrySignatures: 'strict',\n // react 系与 @dlient/* 全部 external:运行时从宿主 SystemJS registry 解析单实例\n // (见 @dlient/ui PluginView 的 ensureSystemShared)。\n // tdesign-react / tdesign-icons-react 同理由宿主 SystemJS registry 共享(tdesign 单实例)。\n external: [\n 'react',\n 'react/jsx-runtime',\n 'react-dom',\n 'react-dom/client',\n '@dlient/api-bridge',\n '@dlient/i18n',\n '@dlient/ui',\n 'tdesign-react',\n 'tdesign-icons-react',\n ],\n output: {\n format: 'system',\n entryFileNames: 'remoteEntry.js',\n chunkFileNames: 'assets/[name].js',\n assetFileNames: 'assets/[name].[ext]',\n },\n },\n },\n } as UserConfig)\n}\n"],"names":[],"mappings":"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"}
|
package/dist/worker.d.ts
CHANGED
|
@@ -18,6 +18,18 @@ declare global {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* 通道(控制面 / 直连):兼容 Electron MessagePortMain 与 utilityProcess parentPort。
|
|
23
|
+
* 共享池模式下控制/直连通道由池运行时注入(见 pool-worker.ts),独立进程模式用 process.parentPort。
|
|
24
|
+
*/
|
|
25
|
+
interface RpcChannel {
|
|
26
|
+
postMessage(message: unknown, transfer?: unknown[]): void;
|
|
27
|
+
on(event: 'message', listener: (event: {
|
|
28
|
+
data: unknown;
|
|
29
|
+
ports?: unknown[];
|
|
30
|
+
}) => void): void;
|
|
31
|
+
start?(): void;
|
|
32
|
+
}
|
|
21
33
|
type WorkerRpcHandler = (args: unknown[], ctx?: HandlerContext) => unknown | Promise<unknown>;
|
|
22
34
|
/** handler 执行上下文(directPort 直连调用时填充) */
|
|
23
35
|
export interface HandlerContext {
|
|
@@ -50,9 +62,16 @@ export interface WorkerRpc {
|
|
|
50
62
|
api: HostApi;
|
|
51
63
|
/** 推送事件给绑定本插件的渲染层 view(经主进程转发) */
|
|
52
64
|
push: (event: string, data?: unknown) => void;
|
|
65
|
+
/** 业务状态快照:热重载/重启前主进程请求(1s 窗口),插件实现返回可序列化状态 */
|
|
66
|
+
registerSnapshotHandler: (handler: () => unknown | Promise<unknown>) => void;
|
|
67
|
+
/** 业务状态恢复:重启后主进程回灌快照,插件据此恢复状态 */
|
|
68
|
+
registerRestoreHandler: (handler: (snapshot: unknown) => void | Promise<void>) => void;
|
|
53
69
|
getPluginId: () => string;
|
|
54
70
|
getGeneration: () => number;
|
|
55
71
|
log: (level: 'info' | 'warn' | 'error' | 'debug', message: string, data?: unknown) => void;
|
|
56
72
|
}
|
|
57
|
-
export declare function createWorkerRpc(pluginId: string, generation: number
|
|
73
|
+
export declare function createWorkerRpc(pluginId: string, generation: number, options?: {
|
|
74
|
+
controlPort?: RpcChannel;
|
|
75
|
+
directPort?: RpcChannel;
|
|
76
|
+
}): WorkerRpc;
|
|
58
77
|
export {};
|