@sparkleideas/plugins 3.0.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +401 -0
  2. package/__tests__/collection-manager.test.ts +332 -0
  3. package/__tests__/dependency-graph.test.ts +434 -0
  4. package/__tests__/enhanced-plugin-registry.test.ts +488 -0
  5. package/__tests__/plugin-registry.test.ts +368 -0
  6. package/__tests__/ruvector-bridge.test.ts +2429 -0
  7. package/__tests__/ruvector-integration.test.ts +1602 -0
  8. package/__tests__/ruvector-migrations.test.ts +1099 -0
  9. package/__tests__/ruvector-quantization.test.ts +846 -0
  10. package/__tests__/ruvector-streaming.test.ts +1088 -0
  11. package/__tests__/sdk.test.ts +325 -0
  12. package/__tests__/security.test.ts +348 -0
  13. package/__tests__/utils/ruvector-test-utils.ts +860 -0
  14. package/examples/plugin-creator/index.ts +636 -0
  15. package/examples/plugin-creator/plugin-creator.test.ts +312 -0
  16. package/examples/ruvector/README.md +288 -0
  17. package/examples/ruvector/attention-patterns.ts +394 -0
  18. package/examples/ruvector/basic-usage.ts +288 -0
  19. package/examples/ruvector/docker-compose.yml +75 -0
  20. package/examples/ruvector/gnn-analysis.ts +501 -0
  21. package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
  22. package/examples/ruvector/init-db.sql +119 -0
  23. package/examples/ruvector/quantization.ts +680 -0
  24. package/examples/ruvector/self-learning.ts +447 -0
  25. package/examples/ruvector/semantic-search.ts +576 -0
  26. package/examples/ruvector/streaming-large-data.ts +507 -0
  27. package/examples/ruvector/transactions.ts +594 -0
  28. package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
  29. package/examples/ruvector-plugins/index.ts +79 -0
  30. package/examples/ruvector-plugins/intent-router.ts +354 -0
  31. package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
  32. package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
  33. package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
  34. package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
  35. package/examples/ruvector-plugins/shared/index.ts +20 -0
  36. package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
  37. package/examples/ruvector-plugins/sona-learning.ts +445 -0
  38. package/package.json +97 -0
  39. package/src/collections/collection-manager.ts +661 -0
  40. package/src/collections/index.ts +56 -0
  41. package/src/collections/official/index.ts +1040 -0
  42. package/src/core/base-plugin.ts +416 -0
  43. package/src/core/plugin-interface.ts +215 -0
  44. package/src/hooks/index.ts +685 -0
  45. package/src/index.ts +378 -0
  46. package/src/integrations/agentic-flow.ts +743 -0
  47. package/src/integrations/index.ts +88 -0
  48. package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
  49. package/src/integrations/ruvector/attention-advanced.ts +1040 -0
  50. package/src/integrations/ruvector/attention-executor.ts +782 -0
  51. package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
  52. package/src/integrations/ruvector/attention.ts +1063 -0
  53. package/src/integrations/ruvector/gnn.ts +3050 -0
  54. package/src/integrations/ruvector/hyperbolic.ts +1948 -0
  55. package/src/integrations/ruvector/index.ts +394 -0
  56. package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
  57. package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
  58. package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
  59. package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
  60. package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
  61. package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
  62. package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
  63. package/src/integrations/ruvector/migrations/index.ts +35 -0
  64. package/src/integrations/ruvector/migrations/migrations.ts +647 -0
  65. package/src/integrations/ruvector/quantization.ts +2036 -0
  66. package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
  67. package/src/integrations/ruvector/self-learning.ts +2376 -0
  68. package/src/integrations/ruvector/streaming.ts +1737 -0
  69. package/src/integrations/ruvector/types.ts +1945 -0
  70. package/src/providers/index.ts +643 -0
  71. package/src/registry/dependency-graph.ts +568 -0
  72. package/src/registry/enhanced-plugin-registry.ts +994 -0
  73. package/src/registry/plugin-registry.ts +604 -0
  74. package/src/sdk/index.ts +563 -0
  75. package/src/security/index.ts +594 -0
  76. package/src/types/index.ts +446 -0
  77. package/src/workers/index.ts +700 -0
  78. package/tmp.json +0 -0
  79. package/tsconfig.json +25 -0
  80. package/vitest.config.ts +23 -0
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Plugin Registry Tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
6
+ import { PluginRegistry, getDefaultRegistry, setDefaultRegistry } from '../src/registry/plugin-registry.js';
7
+ import { BasePlugin } from '../src/core/base-plugin.js';
8
+ import type { PluginMetadata, PluginContext, MCPToolDefinition } from '../src/types/index.js';
9
+
10
+ // ============================================================================
11
+ // Test Plugin Implementation
12
+ // ============================================================================
13
+
14
+ class TestPlugin extends BasePlugin {
15
+ private tools: MCPToolDefinition[] = [];
16
+ initializeCalled = false;
17
+ shutdownCalled = false;
18
+
19
+ constructor(metadata: PluginMetadata, tools?: MCPToolDefinition[]) {
20
+ super(metadata);
21
+ this.tools = tools ?? [];
22
+ }
23
+
24
+ protected async onInitialize(): Promise<void> {
25
+ this.initializeCalled = true;
26
+ }
27
+
28
+ protected async onShutdown(): Promise<void> {
29
+ this.shutdownCalled = true;
30
+ }
31
+
32
+ registerMCPTools(): MCPToolDefinition[] {
33
+ return this.tools;
34
+ }
35
+ }
36
+
37
+ // ============================================================================
38
+ // Tests
39
+ // ============================================================================
40
+
41
+ describe('PluginRegistry', () => {
42
+ let registry: PluginRegistry;
43
+
44
+ beforeEach(() => {
45
+ registry = new PluginRegistry({
46
+ coreVersion: '3.0.0',
47
+ dataDir: '/tmp/test',
48
+ });
49
+ });
50
+
51
+ afterEach(async () => {
52
+ await registry.shutdown();
53
+ });
54
+
55
+ describe('register', () => {
56
+ it('should register a plugin', async () => {
57
+ const plugin = new TestPlugin({
58
+ name: 'test-plugin',
59
+ version: '1.0.0',
60
+ });
61
+
62
+ await registry.register(plugin);
63
+
64
+ expect(registry.getPlugin('test-plugin')).toBe(plugin);
65
+ });
66
+
67
+ it('should reject duplicate plugin names', async () => {
68
+ const plugin1 = new TestPlugin({
69
+ name: 'test-plugin',
70
+ version: '1.0.0',
71
+ });
72
+ const plugin2 = new TestPlugin({
73
+ name: 'test-plugin',
74
+ version: '2.0.0',
75
+ });
76
+
77
+ await registry.register(plugin1);
78
+
79
+ await expect(registry.register(plugin2)).rejects.toThrow('already registered');
80
+ });
81
+
82
+ it('should reject invalid plugins', async () => {
83
+ const invalidPlugin = { name: 'invalid' } as any;
84
+
85
+ await expect(registry.register(invalidPlugin)).rejects.toThrow('Invalid plugin');
86
+ });
87
+
88
+ it('should respect max plugins limit', async () => {
89
+ const limitedRegistry = new PluginRegistry({
90
+ coreVersion: '3.0.0',
91
+ dataDir: '/tmp/test',
92
+ maxPlugins: 2,
93
+ });
94
+
95
+ await limitedRegistry.register(new TestPlugin({ name: 'p1', version: '1.0.0' }));
96
+ await limitedRegistry.register(new TestPlugin({ name: 'p2', version: '1.0.0' }));
97
+
98
+ await expect(
99
+ limitedRegistry.register(new TestPlugin({ name: 'p3', version: '1.0.0' }))
100
+ ).rejects.toThrow('Maximum plugin limit');
101
+ });
102
+
103
+ it('should accept plugin factory functions', async () => {
104
+ const factory = async () => new TestPlugin({
105
+ name: 'factory-plugin',
106
+ version: '1.0.0',
107
+ });
108
+
109
+ await registry.register(factory);
110
+
111
+ expect(registry.getPlugin('factory-plugin')).toBeDefined();
112
+ });
113
+ });
114
+
115
+ describe('unregister', () => {
116
+ it('should unregister a plugin', async () => {
117
+ const plugin = new TestPlugin({
118
+ name: 'test-plugin',
119
+ version: '1.0.0',
120
+ });
121
+
122
+ await registry.register(plugin);
123
+ await registry.initialize();
124
+ await registry.unregister('test-plugin');
125
+
126
+ expect(registry.getPlugin('test-plugin')).toBeUndefined();
127
+ expect(plugin.shutdownCalled).toBe(true);
128
+ });
129
+
130
+ it('should throw for non-existent plugin', async () => {
131
+ await expect(registry.unregister('nonexistent')).rejects.toThrow('not found');
132
+ });
133
+ });
134
+
135
+ describe('initialize', () => {
136
+ it('should initialize all plugins', async () => {
137
+ const plugin1 = new TestPlugin({ name: 'p1', version: '1.0.0' });
138
+ const plugin2 = new TestPlugin({ name: 'p2', version: '1.0.0' });
139
+
140
+ await registry.register(plugin1);
141
+ await registry.register(plugin2);
142
+ await registry.initialize();
143
+
144
+ expect(plugin1.initializeCalled).toBe(true);
145
+ expect(plugin2.initializeCalled).toBe(true);
146
+ });
147
+
148
+ it('should throw if already initialized', async () => {
149
+ await registry.initialize();
150
+
151
+ await expect(registry.initialize()).rejects.toThrow('already initialized');
152
+ });
153
+
154
+ it('should skip disabled plugins', async () => {
155
+ const plugin = new TestPlugin({ name: 'disabled', version: '1.0.0' });
156
+
157
+ await registry.register(plugin, { enabled: false, priority: 50, settings: {} });
158
+ await registry.initialize();
159
+
160
+ expect(plugin.initializeCalled).toBe(false);
161
+ });
162
+
163
+ it('should respect dependency order', async () => {
164
+ const initOrder: string[] = [];
165
+
166
+ class OrderedPlugin extends TestPlugin {
167
+ protected async onInitialize(): Promise<void> {
168
+ initOrder.push(this.metadata.name);
169
+ }
170
+ }
171
+
172
+ const child = new OrderedPlugin({
173
+ name: 'child',
174
+ version: '1.0.0',
175
+ dependencies: ['parent'],
176
+ });
177
+ const parent = new OrderedPlugin({
178
+ name: 'parent',
179
+ version: '1.0.0',
180
+ });
181
+
182
+ // Register in reverse order
183
+ await registry.register(child);
184
+ await registry.register(parent);
185
+ await registry.initialize();
186
+
187
+ expect(initOrder).toEqual(['parent', 'child']);
188
+ });
189
+
190
+ it('should detect circular dependencies', async () => {
191
+ const pluginA = new TestPlugin({
192
+ name: 'a',
193
+ version: '1.0.0',
194
+ dependencies: ['b'],
195
+ });
196
+ const pluginB = new TestPlugin({
197
+ name: 'b',
198
+ version: '1.0.0',
199
+ dependencies: ['a'],
200
+ });
201
+
202
+ await registry.register(pluginA);
203
+ await registry.register(pluginB);
204
+
205
+ await expect(registry.initialize()).rejects.toThrow('Circular dependency');
206
+ });
207
+ });
208
+
209
+ describe('shutdown', () => {
210
+ it('should shutdown all plugins in reverse order', async () => {
211
+ const shutdownOrder: string[] = [];
212
+
213
+ class OrderedPlugin extends TestPlugin {
214
+ protected async onShutdown(): Promise<void> {
215
+ shutdownOrder.push(this.metadata.name);
216
+ }
217
+ }
218
+
219
+ const plugin1 = new OrderedPlugin({ name: 'first', version: '1.0.0' });
220
+ const plugin2 = new OrderedPlugin({ name: 'second', version: '1.0.0' });
221
+
222
+ await registry.register(plugin1);
223
+ await registry.register(plugin2);
224
+ await registry.initialize();
225
+ await registry.shutdown();
226
+
227
+ expect(shutdownOrder).toEqual(['second', 'first']);
228
+ });
229
+ });
230
+
231
+ describe('extension points', () => {
232
+ it('should collect MCP tools from plugins', async () => {
233
+ const tools: MCPToolDefinition[] = [
234
+ {
235
+ name: 'test-tool',
236
+ description: 'A test tool',
237
+ inputSchema: { type: 'object', properties: {} },
238
+ handler: async () => ({ content: [{ type: 'text', text: 'done' }] }),
239
+ },
240
+ ];
241
+
242
+ const plugin = new TestPlugin({ name: 'tool-plugin', version: '1.0.0' }, tools);
243
+
244
+ await registry.register(plugin);
245
+ await registry.initialize();
246
+
247
+ const registeredTools = registry.getMCPTools();
248
+ expect(registeredTools).toHaveLength(1);
249
+ expect(registeredTools[0].name).toBe('test-tool');
250
+ });
251
+ });
252
+
253
+ describe('health check', () => {
254
+ it('should return health status for all plugins', async () => {
255
+ const plugin = new TestPlugin({ name: 'test', version: '1.0.0' });
256
+
257
+ await registry.register(plugin);
258
+ await registry.initialize();
259
+
260
+ const results = await registry.healthCheck();
261
+
262
+ expect(results.has('test')).toBe(true);
263
+ expect(results.get('test')?.healthy).toBe(true);
264
+ });
265
+
266
+ it('should report unhealthy for uninitialized plugins', async () => {
267
+ const plugin = new TestPlugin({ name: 'test', version: '1.0.0' });
268
+
269
+ await registry.register(plugin, { enabled: false, priority: 50, settings: {} });
270
+ await registry.initialize();
271
+
272
+ const results = await registry.healthCheck();
273
+
274
+ expect(results.get('test')?.healthy).toBe(false);
275
+ });
276
+ });
277
+
278
+ describe('stats', () => {
279
+ it('should return accurate statistics', async () => {
280
+ const tools: MCPToolDefinition[] = [
281
+ {
282
+ name: 'tool1',
283
+ description: 'Tool 1',
284
+ inputSchema: { type: 'object', properties: {} },
285
+ handler: async () => ({ content: [{ type: 'text', text: '' }] }),
286
+ },
287
+ ];
288
+
289
+ const plugin1 = new TestPlugin({ name: 'p1', version: '1.0.0' }, tools);
290
+ const plugin2 = new TestPlugin({ name: 'p2', version: '1.0.0' });
291
+
292
+ await registry.register(plugin1);
293
+ await registry.register(plugin2);
294
+ await registry.initialize();
295
+
296
+ const stats = registry.getStats();
297
+
298
+ expect(stats.total).toBe(2);
299
+ expect(stats.initialized).toBe(2);
300
+ expect(stats.failed).toBe(0);
301
+ expect(stats.mcpTools).toBe(1);
302
+ });
303
+ });
304
+
305
+ describe('default registry', () => {
306
+ it('should provide a default registry', () => {
307
+ const defaultReg = getDefaultRegistry();
308
+ expect(defaultReg).toBeInstanceOf(PluginRegistry);
309
+ });
310
+
311
+ it('should allow setting custom default registry', () => {
312
+ const customRegistry = new PluginRegistry({
313
+ coreVersion: '3.0.0',
314
+ dataDir: '/custom',
315
+ });
316
+
317
+ setDefaultRegistry(customRegistry);
318
+
319
+ expect(getDefaultRegistry()).toBe(customRegistry);
320
+ });
321
+ });
322
+ });
323
+
324
+ describe('BasePlugin', () => {
325
+ it('should properly manage lifecycle state', async () => {
326
+ const plugin = new TestPlugin({ name: 'lifecycle-test', version: '1.0.0' });
327
+
328
+ expect(plugin.state).toBe('uninitialized');
329
+
330
+ const registry = new PluginRegistry({
331
+ coreVersion: '3.0.0',
332
+ dataDir: '/tmp/test',
333
+ });
334
+
335
+ await registry.register(plugin);
336
+ await registry.initialize();
337
+
338
+ expect(plugin.state).toBe('initialized');
339
+
340
+ await registry.shutdown();
341
+
342
+ expect(plugin.state).toBe('shutdown');
343
+ });
344
+
345
+ it('should provide accurate health checks', async () => {
346
+ const plugin = new TestPlugin({ name: 'health-test', version: '1.0.0' });
347
+
348
+ const registry = new PluginRegistry({
349
+ coreVersion: '3.0.0',
350
+ dataDir: '/tmp/test',
351
+ });
352
+
353
+ await registry.register(plugin);
354
+ await registry.initialize();
355
+
356
+ const health = await plugin.healthCheck();
357
+
358
+ expect(health.healthy).toBe(true);
359
+ expect(health.status).toBe('healthy');
360
+ expect(health.checks.state.healthy).toBe(true);
361
+ });
362
+
363
+ it('should freeze metadata', () => {
364
+ const plugin = new TestPlugin({ name: 'freeze-test', version: '1.0.0' });
365
+
366
+ expect(Object.isFrozen(plugin.metadata)).toBe(true);
367
+ });
368
+ });