@dlient/plugin-sdk 3.0.2 → 3.0.4
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.map +1 -1
- package/dist/types.d.ts +31 -4
- package/dist/vite-config.d.ts +1 -1
- package/dist/vite-config.mjs +41 -71
- package/dist/vite-config.mjs.map +1 -1
- package/package.json +2 -3
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\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 * 插件类型:\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 requires?: string[] // 依赖的插件 ID\n platforms?: PluginPlatform[] // 支持平台(缺省=全平台)\n permissions?: PluginPermission[] // 申请的宿主能力\n /** 对外暴露给其它插件调用的方法(plugin.invoke 校验) */\n expose?: Record<string, { description?: string }>\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\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 /** 推送事件给绑定本插件的渲染层 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 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"],"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} 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\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 /** 推送事件给绑定本插件的渲染层 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 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"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,25 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/** 本地化文案:字符串(默认值)或多语言映射(键为 locale,如 'zh-CN'/'en-US';'default' 键兜底) */
|
|
7
7
|
export type LocalizedText = string | Record<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* expose 方法调用权限(docs/v3/plugin-view.md §2.2):
|
|
10
|
+
* - private 仅本插件自己;
|
|
11
|
+
* - default(缺省) 宿主(主进程侧)+ 本插件自己;
|
|
12
|
+
* - system system 插件可默认调用,非 system 插件不可调;
|
|
13
|
+
* - public 所有插件,无需 dependencies 声明;
|
|
14
|
+
* - install-confirm 安装时用户确认(永久有效),须在 dependencies 中声明;
|
|
15
|
+
* - runtime-confirm 使用时用户确认(1 天内有效),须在 dependencies 中声明。
|
|
16
|
+
* 支持组合表达式:'|' = 或(满足任一),'&' = 且(需全部满足)。
|
|
17
|
+
* 例:`runtime-confirm | system` = system 直接调用,其它插件需运行时确认。
|
|
18
|
+
*/
|
|
19
|
+
export type ExposeAccess = 'private' | 'default' | 'system' | 'public' | 'install-confirm' | 'runtime-confirm';
|
|
20
|
+
/** expose 方法条目 */
|
|
21
|
+
export interface PluginExposeMethod {
|
|
22
|
+
/** 方法说明(确认框 / 上传校验文案用) */
|
|
23
|
+
description?: string;
|
|
24
|
+
/** 调用权限表达式(缺省 'default') */
|
|
25
|
+
access?: string;
|
|
26
|
+
}
|
|
8
27
|
export interface PluginManifest {
|
|
9
28
|
id: string;
|
|
10
29
|
version: string;
|
|
@@ -14,6 +33,12 @@ export interface PluginManifest {
|
|
|
14
33
|
description?: LocalizedText;
|
|
15
34
|
/** dist 目录相对插件根目录的路径(默认 'dist';开发插件可为任意外部目录的构建输出) */
|
|
16
35
|
dist?: string;
|
|
36
|
+
/**
|
|
37
|
+
* 产物打包方式(方案 build.md 2.3):
|
|
38
|
+
* - 'plain'(缺省):多文件 dist 目录,dlientV3:// 协议直接读磁盘文件;
|
|
39
|
+
* - 'asar':发布时 dist 打成 <pluginDir>/plugin.asar,协议与 worker fork 从 asar 内读取。
|
|
40
|
+
*/
|
|
41
|
+
packaging?: 'plain' | 'asar';
|
|
17
42
|
/**
|
|
18
43
|
* 插件类型:
|
|
19
44
|
* - app:独立应用,可在基座中直接启动(启动后打开页面);
|
|
@@ -27,13 +52,15 @@ export interface PluginManifest {
|
|
|
27
52
|
source?: PluginSource;
|
|
28
53
|
/** 图标:相对插件根目录的路径(如 "icon.svg" / "assets/icon.svg") */
|
|
29
54
|
icon?: string;
|
|
55
|
+
/** 插件标签(市场展示 / 检索;如 ["ai", "chat"]) */
|
|
56
|
+
tags?: string[];
|
|
57
|
+
/** 所属组织(组织标识,形如 "@xxx") */
|
|
58
|
+
organization?: string;
|
|
30
59
|
requires?: string[];
|
|
31
60
|
platforms?: PluginPlatform[];
|
|
32
61
|
permissions?: PluginPermission[];
|
|
33
|
-
/** 对外暴露给其它插件调用的方法(plugin.invoke
|
|
34
|
-
expose?: Record<string,
|
|
35
|
-
description?: string;
|
|
36
|
-
}>;
|
|
62
|
+
/** 对外暴露给其它插件调用的方法(plugin.invoke 校验;access 见 ExposeAccess) */
|
|
63
|
+
expose?: Record<string, PluginExposeMethod>;
|
|
37
64
|
/** 声明要调用的其它插件方法(plugin.invoke 校验) */
|
|
38
65
|
dependencies?: Record<string, string[]>;
|
|
39
66
|
engines?: {
|
package/dist/vite-config.d.ts
CHANGED
package/dist/vite-config.mjs
CHANGED
|
@@ -1,96 +1,66 @@
|
|
|
1
1
|
import { defineConfig } from 'vite';
|
|
2
2
|
import react from '@vitejs/plugin-react';
|
|
3
|
-
import federation from 'vite-plugin-federation';
|
|
4
3
|
|
|
5
4
|
function createPluginViteConfig(options) {
|
|
6
|
-
const {
|
|
5
|
+
const { pluginDir, pluginId, uiEntry = "src/renderer/App.tsx" } = options;
|
|
7
6
|
return defineConfig({
|
|
8
7
|
root: pluginDir,
|
|
9
8
|
plugins: [
|
|
10
9
|
react(),
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
10
|
+
// System.register 格式的 JS 不产出 CSS 引用(esm 会有 import './x.css'),而插件的
|
|
11
|
+
// remoteEntry.js 由宿主经 SystemJS 动态加载(无 HTML 流程)。这里把 CSS 保留为
|
|
12
|
+
// 独立文件,并在入口 chunk 注入 <link>:样式经 dlientV3:// 协议加载;CSS 内 url()
|
|
13
|
+
// 相对路径由浏览器按 CSS 文件位置解析,可正确引用插件内资源(不内联、无需重写)。
|
|
14
14
|
{
|
|
15
|
-
name: "dlient:
|
|
15
|
+
name: "dlient:css-link",
|
|
16
16
|
enforce: "post",
|
|
17
|
-
generateBundle(
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const full = m[0];
|
|
27
|
-
for (const spec of m[1].split(",")) {
|
|
28
|
-
const p = spec.trim().split(/\s+as\s+/);
|
|
29
|
-
decls.push({ imported: p[0].trim(), local: (p[1] || p[0]).trim(), full });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const bound = new Set(decls.map((d) => d.local));
|
|
33
|
-
let changed = false;
|
|
34
|
-
const original = code;
|
|
35
|
-
const refRe = /\b([A-Za-z_$][\w$]*)\$(\d+)\b/g;
|
|
36
|
-
while ((m = refRe.exec(original)) !== null) {
|
|
37
|
-
const unbound = m[0];
|
|
38
|
-
const base = m[1];
|
|
39
|
-
const idx = Number(m[2]);
|
|
40
|
-
if (bound.has(unbound)) continue;
|
|
41
|
-
const target = decls.filter((d) => d.local === base)[idx];
|
|
42
|
-
if (!target) continue;
|
|
43
|
-
const specRe = new RegExp(`(^|[,\\s])(${target.imported}(?:\\s+as\\s+${target.local})?)(?=[,}])`, "g");
|
|
44
|
-
const newFull = target.full.replace(specRe, `$1${target.imported} as ${unbound}`);
|
|
45
|
-
code = code.replace(target.full, newFull);
|
|
46
|
-
changed = true;
|
|
47
|
-
}
|
|
48
|
-
if (!changed && /\bReact\$1\b/.test(code) && !/[{(,]\s*R\s+as\s+React\$1\s*[,}]/.test(code) && !/export\s*\{[^}]*React\$1/.test(code)) {
|
|
49
|
-
const pm = reactProxyRe.exec(code);
|
|
50
|
-
if (pm) code = `import{R as React$1}from"./${pm[1]}";
|
|
51
|
-
${code}`;
|
|
52
|
-
}
|
|
53
|
-
if (changed || code !== chunk.code) chunk.code = code;
|
|
54
|
-
}
|
|
17
|
+
generateBundle(_b, bundle) {
|
|
18
|
+
const css = Object.values(bundle).find(
|
|
19
|
+
(b) => b.type === "asset" && typeof b.source === "string" && b.fileName.endsWith(".css")
|
|
20
|
+
);
|
|
21
|
+
if (!css) return;
|
|
22
|
+
const entry = Object.values(bundle).find((b) => b.type === "chunk" && b.isEntry);
|
|
23
|
+
if (!entry) return;
|
|
24
|
+
entry.code += `
|
|
25
|
+
;(function(){var l=document.createElement('link');l.rel='stylesheet';l.href='dlientV3://plugin/${pluginId}/dist/${css.fileName}?v=${Date.now()}';document.head.appendChild(l);})();`;
|
|
55
26
|
}
|
|
56
|
-
}
|
|
57
|
-
federation({
|
|
58
|
-
name: pluginId,
|
|
59
|
-
filename: "remoteEntry.js",
|
|
60
|
-
// @dlient/ui 等打包进插件后依赖图较大,默认 10s 模块解析超时会使 require('react')
|
|
61
|
-
// 的 CJS 交织别名丢失(React$1 is not defined),按需调大。
|
|
62
|
-
moduleParseIdleTimeout: 60,
|
|
63
|
-
exposes: {
|
|
64
|
-
"./Plugin": uiEntry
|
|
65
|
-
},
|
|
66
|
-
shared: {
|
|
67
|
-
react: { singleton: true, version: "18.2.0", requiredVersion: "^18.2.0" },
|
|
68
|
-
"react/jsx-runtime": { singleton: true, version: "18.2.0", requiredVersion: "^18.2.0" },
|
|
69
|
-
"react-dom": { singleton: true, version: "18.2.0", requiredVersion: "^18.2.0" },
|
|
70
|
-
"react-dom/client": { singleton: true, version: "18.2.0", requiredVersion: "^18.2.0" },
|
|
71
|
-
// 必须与宿主同一实例的模块(api-bridge / i18n / ui):
|
|
72
|
-
// 两包(i18n、ui)均已 lib 构建为 ESM + react external,本地 file: 依赖走
|
|
73
|
-
// workspace 分支纯运行时 loadShare,不触发预构建改写 → 规避 require$$0。
|
|
74
|
-
// import:false:不预构建/静态改写,纯共享缓存读取,避免插件构建把绑定导入合并丢失。
|
|
75
|
-
"@dlient/api-bridge": { singleton: true, version: "3.0.0", requiredVersion: "^3.0.0", import: false },
|
|
76
|
-
"@dlient/i18n": { singleton: true, version: "3.0.0", requiredVersion: "^3.0.0" },
|
|
77
|
-
"@dlient/ui": { singleton: true, version: "3.0.0", requiredVersion: "^3.0.0" }
|
|
78
|
-
}
|
|
79
|
-
})
|
|
27
|
+
}
|
|
80
28
|
],
|
|
81
29
|
build: {
|
|
82
30
|
outDir: "dist",
|
|
31
|
+
// worker 产物(dist/worker.js)与 UI 共享同一 dist,且 worker 不经 vite 构建;
|
|
32
|
+
// 关闭 emptyOutDir,避免 vite build 清空 dist 时误删 worker.js。
|
|
33
|
+
emptyOutDir: false,
|
|
83
34
|
target: "esnext",
|
|
84
|
-
|
|
35
|
+
// 方案 build.md 2.2:UI 端 JS 仅压缩(不混淆——System.register 结构 / import map 裸依赖
|
|
36
|
+
// / 入口导出会被混淆器破坏)。产物始终不带 sourcemap,避免源码经 dlientV3:// 泄露。
|
|
37
|
+
minify: "esbuild",
|
|
38
|
+
sourcemap: false,
|
|
39
|
+
// CSS 压缩(esbuild):vite 5 的 cssMinify 不跟随 minify,需显式开启。
|
|
40
|
+
cssMinify: "esbuild",
|
|
85
41
|
modulePreload: false,
|
|
86
42
|
cssCodeSplit: false,
|
|
87
43
|
rollupOptions: {
|
|
88
44
|
input: {
|
|
89
45
|
main: uiEntry
|
|
90
46
|
},
|
|
47
|
+
// 入口导出由宿主(SystemJS import)在运行时消费:'exports-only' 会把默认导出
|
|
48
|
+
// 当作未使用 tree-shake 掉,必须 'strict' 保留入口全部导出。
|
|
49
|
+
preserveEntrySignatures: "strict",
|
|
50
|
+
// react 系与 @dlient/* 全部 external:运行时从宿主 SystemJS registry 解析单实例
|
|
51
|
+
// (见 @dlient/ui PluginView 的 ensureSystemShared)。
|
|
52
|
+
external: [
|
|
53
|
+
"react",
|
|
54
|
+
"react/jsx-runtime",
|
|
55
|
+
"react-dom",
|
|
56
|
+
"react-dom/client",
|
|
57
|
+
"@dlient/api-bridge",
|
|
58
|
+
"@dlient/i18n",
|
|
59
|
+
"@dlient/ui"
|
|
60
|
+
],
|
|
91
61
|
output: {
|
|
92
|
-
format: "
|
|
93
|
-
entryFileNames: "
|
|
62
|
+
format: "system",
|
|
63
|
+
entryFileNames: "remoteEntry.js",
|
|
94
64
|
chunkFileNames: "assets/[name].js",
|
|
95
65
|
assetFileNames: "assets/[name].[ext]"
|
|
96
66
|
}
|
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 构建预设(
|
|
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":";;;AAeO,SAAS,uBAAuB,OAAA,EAA8C;AACnF,EAAA,MAAM,EAAE,SAAA,EAAW,QAAA,EAAU,OAAA,GAAU,wBAAuB,GAAI,OAAA;AAElE,EAAA,OAAO,YAAA,CAAa;AAAA,IAClB,IAAA,EAAM,SAAA;AAAA,IACN,OAAA,EAAS;AAAA,MACP,KAAA,EAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAKN;AAAA,QACE,IAAA,EAAM,iBAAA;AAAA,QACN,OAAA,EAAS,MAAA;AAAA,QACT,cAAA,CAAe,IAAI,MAAA,EAAQ;AACzB,UAAA,MAAM,GAAA,GAAM,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA,CAAE,IAAA;AAAA,YAChC,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,OAAA,IAAW,OAAO,CAAA,CAAE,MAAA,KAAW,QAAA,IAAY,CAAA,CAAE,QAAA,CAAS,QAAA,CAAS,MAAM;AAAA,WACzF;AACA,UAAA,IAAI,CAAC,GAAA,EAAK;AACV,UAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,OAAA,IAAW,CAAA,CAAE,OAAO,CAAA;AAG/E,UAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,UAAA,KAAA,CAAM,IAAA,IAAQ;AAAA,+FAAA,EAAoG,QAAQ,CAAA,MAAA,EAAS,GAAA,CAAI,QAAQ,CAAA,GAAA,EAAM,IAAA,CAAK,KAAK,CAAA,oCAAA,CAAA;AAAA,QACjK;AAAA;AACF,KACF;AAAA,IACA,KAAA,EAAO;AAAA,MACL,MAAA,EAAQ,MAAA;AAAA;AAAA;AAAA,MAGR,WAAA,EAAa,KAAA;AAAA,MACb,MAAA,EAAQ,QAAA;AAAA;AAAA;AAAA,MAGR,MAAA,EAAQ,SAAA;AAAA,MACR,SAAA,EAAW,KAAA;AAAA;AAAA,MAEX,SAAA,EAAW,SAAA;AAAA,MACX,aAAA,EAAe,KAAA;AAAA,MACf,YAAA,EAAc,KAAA;AAAA,MACd,aAAA,EAAe;AAAA,QACb,KAAA,EAAO;AAAA,UACL,IAAA,EAAM;AAAA,SACR;AAAA;AAAA;AAAA,QAGA,uBAAA,EAAyB,QAAA;AAAA;AAAA;AAAA,QAGzB,QAAA,EAAU;AAAA,UACR,OAAA;AAAA,UACA,mBAAA;AAAA,UACA,WAAA;AAAA,UACA,kBAAA;AAAA,UACA,oBAAA;AAAA,UACA,cAAA;AAAA,UACA;AAAA,SACF;AAAA,QACA,MAAA,EAAQ;AAAA,UACN,MAAA,EAAQ,QAAA;AAAA,UACR,cAAA,EAAgB,gBAAA;AAAA,UAChB,cAAA,EAAgB,kBAAA;AAAA,UAChB,cAAA,EAAgB;AAAA;AAClB;AACF;AACF,GACa,CAAA;AACjB;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dlient/plugin-sdk",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
"@types/node": "^20.0.0",
|
|
25
25
|
"@vitejs/plugin-react": "^4.2.1",
|
|
26
26
|
"typescript": "^5.4.0",
|
|
27
|
-
"vite": "^5.1.6"
|
|
28
|
-
"vite-plugin-federation": "^1.0.2"
|
|
27
|
+
"vite": "^5.1.6"
|
|
29
28
|
}
|
|
30
29
|
}
|