@chatbi-v/core 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,35 +2,60 @@
2
2
 
3
3
  ChatBI 核心模块,提供插件管理、基础设施接口及通用 API 引擎。
4
4
 
5
- ## 架构原则 (Architecture Principles)
5
+ ## 模块功能说明
6
6
 
7
- - **业务无关性 (Business Agnostic)**: `core` 包禁止包含任何具体的业务模型、业务逻辑或业务常量。
8
- - **共享契约 (Shared Contract)**: `core` 只定义通用的技术契约(如 `Plugin`, `StoragePort`, `ApiAdapter`)。
9
- - **插件驱动 (Plugin Driven)**: 所有的业务逻辑应由插件实现。
7
+ - **微内核架构**: 实现插件的注册、生命周期管理与隔离。
8
+ - **领域驱动设计**: 严格区分领域逻辑 (`domain`)、外部接口 (`ports`) 和具体实现 (`adapters`)。
9
+ - **API 引擎**: 统一的 API 请求管理,支持同步、流式请求及拦截器。
10
+ - **存储管理**: 提供隔离的存储适配器,确保插件数据互不干扰。
10
11
 
11
- ## 目录结构
12
+ ## 安装和使用方法
12
13
 
13
- - `adapters/`: 基础设施的具体实现(如 LocalStorage, Axios)。
14
- - `api/`: 通用 API 请求引擎。
15
- - `domain/`: 核心领域逻辑(插件管理器、运行时、沙箱)。
16
- - `ports/`: 核心接口定义(端口)。
17
-
18
- ## 功能
14
+ ### 安装
19
15
 
20
- - **PluginManager**: 管理插件的注册和生命周期。
21
- - **ApiEngine**: 统一的 API 请求管理。支持请求拦截器 (`ApiInterceptor`),且拦截器能力已覆盖普通请求 (`call`) 和流式请求 (`stream`)。
22
- - **Sandbox**: 提供插件运行时的样式和全局变量隔离。
16
+ ```bash
17
+ pnpm add @chatbi-v/core
18
+ ```
23
19
 
24
- ## 使用方法
20
+ ### 使用示例
25
21
 
26
22
  ```typescript
27
- import { pluginManager } from '@chatbi/core';
23
+ import { PluginManager, LocalStorageAdapter } from '@chatbi-v/core';
24
+
25
+ // 初始化插件管理器
26
+ const storage = new LocalStorageAdapter();
27
+ const pluginManager = new PluginManager(storage);
28
28
 
29
- // 注册插件
29
+ // 注册并初始化插件
30
30
  pluginManager.register({
31
31
  id: 'my-plugin',
32
- init: (context) => {
33
- console.log('Plugin initialized');
34
- },
32
+ metadata: { name: 'My Plugin', type: 'business' },
33
+ onLoad: async (context) => {
34
+ console.log('Plugin loaded', context.pluginId);
35
+ }
35
36
  });
36
37
  ```
38
+
39
+ ## API 文档链接
40
+
41
+ 详细 API 文档请参考 [docs/index.html](https://github.com/your-repo/docs/index.html) 或运行 `npm run docs:dev` 本地查看。
42
+
43
+ ## 开发注意事项
44
+
45
+ - **禁止引入 UI 框架**: 本模块为纯逻辑层,不得包含 React/AntD 等 UI 依赖。
46
+ - **禁止反向依赖**: `core` 不得依赖 `plugins` 或 `apps` 中的任何代码。
47
+ - **类型安全**: 优先使用 `ports` 中定义的接口。
48
+
49
+ ## 架构原则 (Architecture Principles)
50
+
51
+ - **业务无关性 (Business Agnostic)**: `core` 包禁止包含任何具体的业务模型、业务逻辑或业务常量。
52
+ - **共享契约 (Shared Contract)**: `core` 只定义通用的技术契约(如 `Plugin`, `StoragePort`, `ApiAdapter`)。
53
+ - **插件驱动 (Plugin Driven)**: 所有的业务逻辑应由插件实现。
54
+
55
+ ## 目录结构
56
+
57
+ - `adapters/`: 基础设施的具体实现(如 LocalStorage, Axios)。
58
+ - `api/`: 通用 API 请求引擎。
59
+ - `domain/`: 核心领域逻辑(插件管理器、运行时、沙箱)。
60
+ - `ports/`: 核心接口定义(端口)。
61
+ - `utils/`: 通用工具函数。
@@ -3,21 +3,13 @@ export interface PluginRegistry {
3
3
  }
4
4
  export interface DiscoveryRule {
5
5
  /**
6
- * 插件发现的 Glob 模式 (例如: '@chatbi-plugins/*\/src/index.{ts,tsx}')
7
- */
8
- pattern: string;
9
- /**
10
- * 路径标识部分(用于从绝对路径中提取 ID,例如 'packages/plugins')
6
+ * 路径匹配标识(如 'plugins' '@chatbi-plugins'
11
7
  */
12
8
  pathSegment: string;
13
9
  /**
14
- * 生成插件 ID 时的前缀
10
+ * 生成插件 ID 时的前缀(如 '@chatbi-v/plugin')
15
11
  */
16
12
  idPrefix: string;
17
- /**
18
- * 可选的别名映射
19
- */
20
- alias?: string;
21
13
  }
22
14
  /**
23
15
  * 自动发现并解析插件
@@ -25,13 +17,5 @@ export interface DiscoveryRule {
25
17
  */
26
18
  export declare const resolvePluginRegistry: (options: {
27
19
  modules: Record<string, () => Promise<any>>;
28
- baseUrl: string;
29
- rules: DiscoveryRule[];
20
+ rules?: DiscoveryRule[];
30
21
  }) => PluginRegistry;
31
- /**
32
- * 核心插件发现方法
33
- * @description 该方法应在应用层被调用,但逻辑集成在 core 中。
34
- * 注意:由于 Vite import.meta.glob 必须使用字面量的限制,
35
- * 这里使用一个覆盖范围合理的模式,并通过 rules 进行精确匹配。
36
- */
37
- export declare const discoverPlugins: (rules: DiscoveryRule[], baseUrl: string) => PluginRegistry;
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @file plugin-manager.ts
3
+ * @description 插件管理器,负责插件的注册、生命周期管理、状态持久化及扩展点收集
4
+ * @author ChatBI Team
5
+ */
1
6
  import { DefaultEventBus } from '../event-bus';
2
7
  import { Plugin, PluginExtension, RouteConfig, SlotPosition } from '../ports/plugin-port';
3
8
  import { StoragePort } from '../ports/storage-port';
@@ -104,9 +109,6 @@ export declare class PluginManager {
104
109
  * @param slot 插槽位置
105
110
  */
106
111
  getExtensions(slot: SlotPosition | string): PluginExtension[];
107
- /**
108
- * 获取所有收集到的路由
109
- */
110
112
  getRoutes(): RouteConfig[];
111
113
  /**
112
114
  * 注册插件
@@ -136,6 +138,10 @@ export declare class PluginManager {
136
138
  * @param config 插件配置
137
139
  */
138
140
  loadRemotePlugin(pluginId: string, url: string, config: any): Promise<Plugin | null>;
141
+ /**
142
+ * IIFE 模式加载插件
143
+ */
144
+ private loadIIFEPlugin;
139
145
  /**
140
146
  * 实例化插件
141
147
  */
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @file plugin-runtime.ts
3
+ * @description 插件运行时,封装单个插件的生命周期(load/mount/unmount)、沙箱环境和隔离上下文
4
+ * @author ChatBI Team
5
+ */
1
6
  import { Plugin, PluginContext } from '../ports/plugin-port';
2
7
  import { StorageManager } from './storage-manager';
3
8
  /**
@@ -1,6 +1,8 @@
1
1
  export interface PluginLoaderOptions {
2
2
  /** 插件发现规则 */
3
3
  discoveryRules?: any[];
4
+ /** 插件模块映射 (由 Vite import.meta.glob 生成) */
5
+ modules?: Record<string, () => Promise<any>>;
4
6
  /** 预注册的插件注册表 (可选,用于离线或手动加载) */
5
7
  registry?: Record<string, () => Promise<any>>;
6
8
  /** 插件配置映射 */
package/dist/index.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @file index.ts
3
+ * @description Core 模块入口文件,导出所有公共 API、领域逻辑、组件和 Hooks
4
+ * @author ChatBI Team
5
+ */
1
6
  export * from './ports/api-port';
2
7
  export * from './ports/event-bus-port';
3
8
  export * from './ports/plugin-port';
@@ -19,6 +24,7 @@ export * from './adapters/scoped-storage-adapter';
19
24
  export * from './event-bus';
20
25
  export * from './plugin-context';
21
26
  export * from './api';
27
+ export * from './sandbox/proxy-sandbox';
22
28
  export * from './utils';
23
29
  export * from './hooks/use-storage-state';
24
30
  export * from './hooks/use-plugin-loader';
package/dist/index.mjs CHANGED
@@ -18,6 +18,23 @@ var Slot = {
18
18
  RootLayout: "root-layout",
19
19
  Custom: "custom"
20
20
  };
21
+ var BasePlugin = class {
22
+ /**
23
+ * 插件 ID
24
+ * @description 自动从 metadata.id 获取
25
+ */
26
+ get id() {
27
+ return this.metadata.id;
28
+ }
29
+ };
30
+ function definePlugin(plugin) {
31
+ return {
32
+ ...plugin,
33
+ get id() {
34
+ return this.metadata.id;
35
+ }
36
+ };
37
+ }
21
38
 
22
39
  // src/utils/logger.ts
23
40
  var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
@@ -227,7 +244,7 @@ var PluginErrorBoundary = class extends Component {
227
244
  };
228
245
 
229
246
  // src/components/PluginSlot.tsx
230
- import { useMemo } from "react";
247
+ import { useMemo, useState, useEffect } from "react";
231
248
 
232
249
  // src/adapters/local-storage-adapter.ts
233
250
  var LocalStorageAdapter = class {
@@ -764,18 +781,24 @@ var StorageManager = class {
764
781
  this.validateKey(pluginId, key, scope);
765
782
  try {
766
783
  const val = adapter.getItem(key);
767
- if (val === null) {
768
- const schema = this.schemas.get(pluginId)?.find((s) => s.key === key);
769
- if (schema && schema.default !== void 0) {
770
- return schema.default;
784
+ if (val !== null) {
785
+ try {
786
+ return JSON.parse(val);
787
+ } catch {
788
+ return val;
771
789
  }
772
- return null;
773
790
  }
774
- try {
775
- return JSON.parse(val);
776
- } catch {
777
- return val;
791
+ if (scope === "plugin") {
792
+ const externalConfig = configManager.get(pluginId);
793
+ if (externalConfig && externalConfig[key] !== void 0) {
794
+ return externalConfig[key];
795
+ }
778
796
  }
797
+ const schema = this.schemas.get(pluginId)?.find((s) => s.key === key);
798
+ if (schema && schema.default !== void 0) {
799
+ return schema.default;
800
+ }
801
+ return null;
779
802
  } catch (e) {
780
803
  console.warn(`[Storage] Failed to read key "${key}"`, e);
781
804
  return null;
@@ -1012,14 +1035,21 @@ var PluginManager = class {
1012
1035
  });
1013
1036
  return extensions.sort((a, b) => (a.order || 0) - (b.order || 0));
1014
1037
  }
1015
- /**
1016
- * 获取所有收集到的路由
1017
- */
1018
1038
  getRoutes() {
1019
1039
  const activeRoutes = [];
1020
1040
  this.getPlugins().forEach((plugin) => {
1021
1041
  if (this.isPluginEnabled(plugin.id) && plugin.metadata.routes) {
1022
- activeRoutes.push(...plugin.metadata.routes);
1042
+ const config = configManager.get(plugin.id) || {};
1043
+ plugin.metadata.routes.forEach((route) => {
1044
+ activeRoutes.push({
1045
+ ...route,
1046
+ meta: {
1047
+ ...route.meta,
1048
+ pluginId: plugin.id,
1049
+ config
1050
+ }
1051
+ });
1052
+ });
1023
1053
  }
1024
1054
  });
1025
1055
  return activeRoutes;
@@ -1037,42 +1067,48 @@ var PluginManager = class {
1037
1067
  if (this.plugins.has(plugin.id)) {
1038
1068
  return;
1039
1069
  }
1040
- if (plugin.metadata.storage) {
1041
- this.storageManager.registerSchema(plugin.id, plugin.metadata.storage);
1070
+ const storageSchema = [
1071
+ ...plugin.metadata.storage || [],
1072
+ ...plugin.metadata.configuration?.map((c) => ({
1073
+ key: c.key,
1074
+ // 将 configuration 的 type 映射为 storage 支持的类型
1075
+ type: c.type === "select" ? "string" : c.type,
1076
+ label: c.label,
1077
+ description: c.description,
1078
+ default: c.default,
1079
+ scope: "plugin"
1080
+ })) || []
1081
+ ];
1082
+ if (storageSchema.length > 0) {
1083
+ this.storageManager.registerSchema(plugin.id, storageSchema);
1042
1084
  }
1043
1085
  if (!this.pluginStates[plugin.id]) {
1044
1086
  this.pluginStates[plugin.id] = { enabled: true, order: 0 };
1045
1087
  }
1046
- const configFromStorage = {};
1088
+ const metadataDefaults = {};
1089
+ const userOverrides = {};
1047
1090
  const pluginStorage = this.storageManager.getPluginStorage(plugin.id);
1048
1091
  if (plugin.metadata.configuration) {
1049
1092
  plugin.metadata.configuration.forEach((item) => {
1050
1093
  if (item.default !== void 0) {
1051
- configFromStorage[item.key] = item.default;
1094
+ metadataDefaults[item.key] = item.default;
1052
1095
  }
1053
1096
  try {
1054
1097
  const saved = pluginStorage.getItem(item.key);
1055
1098
  if (saved !== null) {
1056
- configFromStorage[item.key] = JSON.parse(saved);
1099
+ userOverrides[item.key] = JSON.parse(saved);
1057
1100
  }
1058
1101
  } catch (e) {
1059
1102
  }
1060
1103
  });
1061
1104
  }
1062
1105
  const mergedConfig = {
1106
+ ...metadataDefaults,
1063
1107
  ...plugin.defaultConfig,
1064
- ...configFromStorage,
1065
- ...configManager.get(plugin.id) || {}
1108
+ ...configManager.get(plugin.id) || {},
1109
+ ...userOverrides
1066
1110
  };
1067
1111
  configManager.set(plugin.id, mergedConfig);
1068
- if (mergedConfig) {
1069
- Object.entries(mergedConfig).forEach(([key, value]) => {
1070
- try {
1071
- pluginStorage.setItem(key, JSON.stringify(value));
1072
- } catch (e) {
1073
- }
1074
- });
1075
- }
1076
1112
  switch (plugin.metadata.type) {
1077
1113
  case "business":
1078
1114
  this.handleBusinessPlugin(plugin);
@@ -1133,7 +1169,9 @@ var PluginManager = class {
1133
1169
  const runtime = this.runtimes.get(id);
1134
1170
  if (runtime) {
1135
1171
  try {
1172
+ console.log(`[PluginManager] invoking onLoad for ${id}`);
1136
1173
  await runtime.load();
1174
+ console.log(`[PluginManager] onLoad completed for ${id}`);
1137
1175
  } catch (e) {
1138
1176
  logger7.error(`\u63D2\u4EF6 ${id} \u52A0\u8F7D\u5931\u8D25:`, e);
1139
1177
  }
@@ -1240,31 +1278,38 @@ var PluginManager = class {
1240
1278
  */
1241
1279
  async loadRemotePlugin(pluginId, url, config) {
1242
1280
  logger7.info(`\u6B63\u5728\u4ECE ${url} \u52A0\u8F7D\u8FDC\u7A0B\u63D2\u4EF6 ${pluginId}...`);
1281
+ if (config?.format === "iife") {
1282
+ return this.loadIIFEPlugin(pluginId, url, config);
1283
+ }
1243
1284
  try {
1244
- const module = await import(
1245
- /* @vite-ignore */
1246
- url
1247
- );
1285
+ const dynamicImport = new Function("specifier", "return import(specifier)");
1286
+ const module = await dynamicImport(url);
1248
1287
  return this.instantiatePlugin(pluginId, module, config);
1249
1288
  } catch (e) {
1250
1289
  logger7.warn(`ESM \u52A0\u8F7D\u5931\u8D25\uFF0C\u5C1D\u8BD5 IIFE \u52A0\u8F7D: ${pluginId}`);
1251
- return new Promise((resolve, reject) => {
1252
- const script = document.createElement("script");
1253
- script.src = url;
1254
- script.onload = () => {
1255
- const globalName = pluginId.replace(/[^a-zA-Z0-9]/g, "_");
1256
- const pluginModule = window[globalName];
1257
- if (pluginModule) {
1258
- resolve(this.instantiatePlugin(pluginId, pluginModule, config));
1259
- } else {
1260
- reject(new Error(`\u8FDC\u7A0B\u63D2\u4EF6 ${pluginId} \u52A0\u8F7D\u540E\u672A\u627E\u5230\u5168\u5C40\u53D8\u91CF ${globalName}`));
1261
- }
1262
- };
1263
- script.onerror = () => reject(new Error(`\u8FDC\u7A0B\u63D2\u4EF6 ${pluginId} \u52A0\u8F7D\u5931\u8D25: ${url}`));
1264
- document.head.appendChild(script);
1265
- });
1290
+ return this.loadIIFEPlugin(pluginId, url, config);
1266
1291
  }
1267
1292
  }
1293
+ /**
1294
+ * IIFE 模式加载插件
1295
+ */
1296
+ loadIIFEPlugin(pluginId, url, config) {
1297
+ return new Promise((resolve, reject) => {
1298
+ const script = document.createElement("script");
1299
+ script.src = url;
1300
+ script.onload = () => {
1301
+ const globalName = pluginId.replace(/[^a-zA-Z0-9]/g, "_");
1302
+ const pluginModule = window[globalName];
1303
+ if (pluginModule) {
1304
+ resolve(this.instantiatePlugin(pluginId, pluginModule, config));
1305
+ } else {
1306
+ reject(new Error(`\u8FDC\u7A0B\u63D2\u4EF6 ${pluginId} \u52A0\u8F7D\u540E\u672A\u627E\u5230\u5168\u5C40\u53D8\u91CF ${globalName}`));
1307
+ }
1308
+ };
1309
+ script.onerror = () => reject(new Error(`\u8FDC\u7A0B\u63D2\u4EF6 ${pluginId} \u52A0\u8F7D\u5931\u8D25: ${url}`));
1310
+ document.head.appendChild(script);
1311
+ });
1312
+ }
1268
1313
  /**
1269
1314
  * 实例化插件
1270
1315
  */
@@ -1280,11 +1325,22 @@ var PluginManager = class {
1280
1325
  }
1281
1326
  }
1282
1327
  if (PluginClass) {
1328
+ const isClass = typeof PluginClass === "function" && PluginClass.prototype;
1329
+ if (isClass) {
1330
+ logger7.warn(`\u63D2\u4EF6 ${pluginId} \u4F7F\u7528\u4E86\u7C7B\u5B9A\u4E49\u6A21\u5F0F\u3002\u5EFA\u8BAE\u7EDF\u4E00\u4F7F\u7528 definePlugin() \u5DE5\u5382\u6A21\u5F0F\u4EE5\u6D88\u9664\u6B67\u4E49\u5E76\u7B80\u5316\u4EE3\u7801\u3002`);
1331
+ }
1283
1332
  const pluginInstance = typeof PluginClass === "function" ? new PluginClass() : PluginClass;
1333
+ const isFilePath = pluginId.includes("/") && (pluginId.includes(".ts") || pluginId.includes(".tsx"));
1334
+ if (!isFilePath && pluginId && pluginInstance.metadata) {
1335
+ if (pluginInstance.metadata.id !== pluginId) {
1336
+ pluginInstance.metadata.id = pluginId;
1337
+ }
1338
+ }
1284
1339
  if (!pluginInstance.id && pluginInstance.metadata?.id) {
1285
- pluginInstance.id = pluginInstance.metadata.id;
1286
- } else if (!pluginInstance.id) {
1287
- pluginInstance.id = pluginId;
1340
+ try {
1341
+ pluginInstance.id = pluginInstance.metadata.id;
1342
+ } catch (e) {
1343
+ }
1288
1344
  }
1289
1345
  if (config) {
1290
1346
  pluginInstance.defaultConfig = { ...pluginInstance.defaultConfig, ...config };
@@ -1324,6 +1380,13 @@ var PluginSlot = ({
1324
1380
  skeleton,
1325
1381
  fallback
1326
1382
  }) => {
1383
+ const [, forceUpdate] = useState({});
1384
+ useEffect(() => {
1385
+ const unsubscribe = pluginManager.subscribe(() => {
1386
+ forceUpdate({});
1387
+ });
1388
+ return unsubscribe;
1389
+ }, []);
1327
1390
  const extensions = pluginManager.getExtensions(slot);
1328
1391
  const systemConfig = pluginManager.getSystemConfig("title") ? {
1329
1392
  title: pluginManager.getSystemConfig("title"),
@@ -1373,8 +1436,14 @@ var BlockSkeleton = ({ className }) => /* @__PURE__ */ jsx4("div", { className:
1373
1436
 
1374
1437
  // src/domain/auto-loader.ts
1375
1438
  var logger8 = createLogger("AutoLoader");
1439
+ var DEFAULT_RULES = [
1440
+ { pathSegment: "@chatbi-plugins", idPrefix: "@chatbi-v/plugin" },
1441
+ { pathSegment: "@chatbi-apps", idPrefix: "@chatbi-v/app" },
1442
+ { pathSegment: "packages/plugins", idPrefix: "@chatbi-v/plugin" },
1443
+ { pathSegment: "packages/apps", idPrefix: "@chatbi-v/app" }
1444
+ ];
1376
1445
  var resolvePluginRegistry = (options) => {
1377
- const { modules, baseUrl, rules } = options;
1446
+ const { modules, rules = DEFAULT_RULES } = options;
1378
1447
  const registry = {};
1379
1448
  const compiledRules = rules.map((rule) => {
1380
1449
  const escapedSegment = rule.pathSegment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1383,36 +1452,28 @@ var resolvePluginRegistry = (options) => {
1383
1452
  regex: new RegExp(`${escapedSegment}/([^/]+)/src/index`)
1384
1453
  };
1385
1454
  });
1386
- for (const relativePath in modules) {
1455
+ for (const path in modules) {
1387
1456
  try {
1388
- const path = new URL(relativePath, baseUrl).pathname;
1457
+ let pluginId = null;
1389
1458
  for (const rule of compiledRules) {
1390
1459
  const match = path.match(rule.regex);
1391
1460
  if (match && match[1]) {
1392
- const dirName = match[1];
1393
- const pluginId = `${rule.idPrefix}-${dirName}`;
1394
- registry[pluginId] = modules[relativePath];
1395
- logger8.info(`Found plugin: ${pluginId} at ${path}`);
1461
+ pluginId = `${rule.idPrefix}-${match[1]}`;
1462
+ logger8.info(`\u89E3\u6790\u8DEF\u5F84\u6210\u529F: ${path} -> ${pluginId}`);
1396
1463
  break;
1397
1464
  }
1398
1465
  }
1466
+ if (pluginId) {
1467
+ registry[pluginId] = modules[path];
1468
+ } else {
1469
+ logger8.warn(`\u65E0\u6CD5\u4ECE\u8DEF\u5F84\u89E3\u6790\u63D2\u4EF6 ID: ${path}\uFF0C\u8BF7\u68C0\u67E5\u662F\u5426\u7B26\u5408\u547D\u540D\u7EA6\u5B9A\u3002`);
1470
+ }
1399
1471
  } catch (e) {
1400
- logger8.error(`Failed to resolve plugin at ${relativePath}:`, e);
1472
+ logger8.error(`\u89E3\u6790\u63D2\u4EF6\u8DEF\u5F84\u5931\u8D25: ${path}`, e);
1401
1473
  }
1402
1474
  }
1403
1475
  return registry;
1404
1476
  };
1405
- var discoverPlugins = (rules, baseUrl) => {
1406
- const modules = import.meta.glob([
1407
- "../../../../packages/plugins/*/src/index.{ts,tsx}",
1408
- "../../../../apps/*/src/index.{ts,tsx}"
1409
- ]);
1410
- return resolvePluginRegistry({
1411
- modules,
1412
- baseUrl,
1413
- rules
1414
- });
1415
- };
1416
1477
 
1417
1478
  // src/domain/models.ts
1418
1479
  var SUCCESS_CODE = "000000";
@@ -1853,7 +1914,7 @@ var dateUtils = {
1853
1914
  var version = "1.0.0";
1854
1915
 
1855
1916
  // src/hooks/use-storage-state.ts
1856
- import { useCallback, useState } from "react";
1917
+ import { useCallback, useState as useState2 } from "react";
1857
1918
  function useStorageState(pluginId, key, options = {}) {
1858
1919
  const { defaultValue, scope = "plugin" } = options;
1859
1920
  const storageManager = pluginManager.getStorageManager();
@@ -1861,7 +1922,7 @@ function useStorageState(pluginId, key, options = {}) {
1861
1922
  const contextStorage = storageManager.getContextStorage(pluginId);
1862
1923
  return scope === "shared" ? contextStorage.shared : contextStorage;
1863
1924
  }, [pluginId, scope, storageManager]);
1864
- const [state, setState] = useState(() => {
1925
+ const [state, setState] = useState2(() => {
1865
1926
  try {
1866
1927
  if (typeof window === "undefined") return defaultValue;
1867
1928
  const storage = getStorage();
@@ -1886,13 +1947,13 @@ function useStorageState(pluginId, key, options = {}) {
1886
1947
  }
1887
1948
 
1888
1949
  // src/hooks/use-plugin-loader.ts
1889
- import { useEffect, useRef, useState as useState2 } from "react";
1950
+ import { useEffect as useEffect2, useRef, useState as useState3 } from "react";
1890
1951
  var logger10 = createLogger("PluginLoader");
1891
1952
  var usePluginLoader = (options) => {
1892
- const [pluginsLoaded, setPluginsLoaded] = useState2(false);
1893
- const [pluginVersion, setPluginVersion] = useState2(0);
1953
+ const [pluginsLoaded, setPluginsLoaded] = useState3(false);
1954
+ const [pluginVersion, setPluginVersion] = useState3(0);
1894
1955
  const loadingRef = useRef(false);
1895
- useEffect(() => {
1956
+ useEffect2(() => {
1896
1957
  const unsubscribe = pluginManager.subscribe(() => {
1897
1958
  logger10.debug("Plugin state changed, refreshing UI...");
1898
1959
  setPluginVersion((v) => v + 1);
@@ -1903,13 +1964,17 @@ var usePluginLoader = (options) => {
1903
1964
  try {
1904
1965
  const {
1905
1966
  discoveryRules = [],
1967
+ modules = {},
1906
1968
  registry: manualRegistry = {},
1907
1969
  pluginConfigs,
1908
1970
  sharedContext = {},
1909
1971
  baseUrl = window.location.origin
1910
1972
  } = options;
1911
1973
  logger10.info("Starting to load plugins...");
1912
- const discoveredRegistry = discoveryRules.length > 0 ? discoverPlugins(discoveryRules, baseUrl) : {};
1974
+ const discoveredRegistry = Object.keys(modules).length > 0 ? resolvePluginRegistry({
1975
+ modules,
1976
+ rules: discoveryRules.length > 0 ? discoveryRules : void 0
1977
+ }) : {};
1913
1978
  const finalRegistry = { ...discoveredRegistry, ...manualRegistry };
1914
1979
  if (options.systemConfig) {
1915
1980
  const { configManager: configManager2 } = await import("./config-manager-ZARQENTB.mjs");
@@ -1940,6 +2005,7 @@ export {
1940
2005
  ApiProvider,
1941
2006
  AvatarSkeleton,
1942
2007
  AxiosAdapter,
2008
+ BasePlugin,
1943
2009
  BlockSkeleton,
1944
2010
  ConfigManager,
1945
2011
  DefaultEventBus,
@@ -1953,6 +2019,7 @@ export {
1953
2019
  PluginRuntime,
1954
2020
  PluginSandbox,
1955
2021
  PluginSlot,
2022
+ ProxySandbox,
1956
2023
  SUCCESS_CODE,
1957
2024
  ScopedStorageAdapter,
1958
2025
  ServiceRegistry,
@@ -1965,7 +2032,7 @@ export {
1965
2032
  configManager,
1966
2033
  createLogger,
1967
2034
  dateUtils,
1968
- discoverPlugins,
2035
+ definePlugin,
1969
2036
  isMockMode,
1970
2037
  logger,
1971
2038
  normalizeParams,