@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,434 @@
1
+ /**
2
+ * Dependency Graph Tests
3
+ *
4
+ * Comprehensive tests for version constraints, dependency resolution,
5
+ * and safe unload functionality.
6
+ */
7
+
8
+ import { describe, it, expect, beforeEach } from 'vitest';
9
+ import {
10
+ DependencyGraph,
11
+ parseVersion,
12
+ compareVersions,
13
+ satisfiesVersion,
14
+ type PluginDependency,
15
+ } from '../src/registry/dependency-graph.js';
16
+
17
+ // ============================================================================
18
+ // Version Parsing Tests
19
+ // ============================================================================
20
+
21
+ describe('parseVersion', () => {
22
+ it('should parse valid semver versions', () => {
23
+ expect(parseVersion('1.0.0')).toEqual({ major: 1, minor: 0, patch: 0 });
24
+ expect(parseVersion('3.2.1')).toEqual({ major: 3, minor: 2, patch: 1 });
25
+ expect(parseVersion('10.20.30')).toEqual({ major: 10, minor: 20, patch: 30 });
26
+ });
27
+
28
+ it('should parse versions with prerelease suffixes', () => {
29
+ expect(parseVersion('1.0.0-alpha')).toEqual({ major: 1, minor: 0, patch: 0 });
30
+ expect(parseVersion('2.0.0-beta.1')).toEqual({ major: 2, minor: 0, patch: 0 });
31
+ });
32
+
33
+ it('should return null for invalid versions', () => {
34
+ expect(parseVersion('invalid')).toBeNull();
35
+ expect(parseVersion('1.0')).toBeNull();
36
+ expect(parseVersion('1')).toBeNull();
37
+ expect(parseVersion('')).toBeNull();
38
+ });
39
+ });
40
+
41
+ // ============================================================================
42
+ // Version Comparison Tests
43
+ // ============================================================================
44
+
45
+ describe('compareVersions', () => {
46
+ it('should compare major versions', () => {
47
+ expect(compareVersions('2.0.0', '1.0.0')).toBe(1);
48
+ expect(compareVersions('1.0.0', '2.0.0')).toBe(-1);
49
+ expect(compareVersions('1.0.0', '1.0.0')).toBe(0);
50
+ });
51
+
52
+ it('should compare minor versions', () => {
53
+ expect(compareVersions('1.2.0', '1.1.0')).toBe(1);
54
+ expect(compareVersions('1.1.0', '1.2.0')).toBe(-1);
55
+ expect(compareVersions('1.1.0', '1.1.0')).toBe(0);
56
+ });
57
+
58
+ it('should compare patch versions', () => {
59
+ expect(compareVersions('1.0.2', '1.0.1')).toBe(1);
60
+ expect(compareVersions('1.0.1', '1.0.2')).toBe(-1);
61
+ expect(compareVersions('1.0.1', '1.0.1')).toBe(0);
62
+ });
63
+
64
+ it('should handle complex comparisons', () => {
65
+ expect(compareVersions('2.1.0', '1.9.9')).toBe(1);
66
+ expect(compareVersions('1.9.9', '2.0.0')).toBe(-1);
67
+ });
68
+ });
69
+
70
+ // ============================================================================
71
+ // Version Satisfies Tests
72
+ // ============================================================================
73
+
74
+ describe('satisfiesVersion', () => {
75
+ describe('wildcard', () => {
76
+ it('should match any version with *', () => {
77
+ expect(satisfiesVersion('*', '1.0.0')).toBe(true);
78
+ expect(satisfiesVersion('*', '99.99.99')).toBe(true);
79
+ });
80
+
81
+ it('should match any version with empty string', () => {
82
+ expect(satisfiesVersion('', '1.0.0')).toBe(true);
83
+ });
84
+ });
85
+
86
+ describe('exact match', () => {
87
+ it('should match exact versions', () => {
88
+ expect(satisfiesVersion('1.0.0', '1.0.0')).toBe(true);
89
+ expect(satisfiesVersion('2.3.4', '2.3.4')).toBe(true);
90
+ });
91
+
92
+ it('should reject non-matching versions', () => {
93
+ expect(satisfiesVersion('1.0.0', '1.0.1')).toBe(false);
94
+ expect(satisfiesVersion('1.0.0', '2.0.0')).toBe(false);
95
+ });
96
+ });
97
+
98
+ describe('caret range (^)', () => {
99
+ it('should match compatible versions', () => {
100
+ expect(satisfiesVersion('^1.0.0', '1.0.0')).toBe(true);
101
+ expect(satisfiesVersion('^1.0.0', '1.0.1')).toBe(true);
102
+ expect(satisfiesVersion('^1.0.0', '1.5.0')).toBe(true);
103
+ expect(satisfiesVersion('^1.0.0', '1.99.99')).toBe(true);
104
+ });
105
+
106
+ it('should reject incompatible major versions', () => {
107
+ expect(satisfiesVersion('^1.0.0', '2.0.0')).toBe(false);
108
+ expect(satisfiesVersion('^1.0.0', '0.9.9')).toBe(false);
109
+ });
110
+
111
+ it('should reject lower minor/patch than minimum', () => {
112
+ expect(satisfiesVersion('^1.2.3', '1.2.2')).toBe(false);
113
+ expect(satisfiesVersion('^1.2.3', '1.1.0')).toBe(false);
114
+ });
115
+ });
116
+
117
+ describe('tilde range (~)', () => {
118
+ it('should match patch versions', () => {
119
+ expect(satisfiesVersion('~1.2.0', '1.2.0')).toBe(true);
120
+ expect(satisfiesVersion('~1.2.0', '1.2.5')).toBe(true);
121
+ expect(satisfiesVersion('~1.2.0', '1.2.99')).toBe(true);
122
+ });
123
+
124
+ it('should reject different minor versions', () => {
125
+ expect(satisfiesVersion('~1.2.0', '1.3.0')).toBe(false);
126
+ expect(satisfiesVersion('~1.2.0', '1.1.0')).toBe(false);
127
+ });
128
+
129
+ it('should reject different major versions', () => {
130
+ expect(satisfiesVersion('~1.2.0', '2.2.0')).toBe(false);
131
+ });
132
+ });
133
+
134
+ describe('comparison operators', () => {
135
+ it('should handle >=', () => {
136
+ expect(satisfiesVersion('>=1.0.0', '1.0.0')).toBe(true);
137
+ expect(satisfiesVersion('>=1.0.0', '2.0.0')).toBe(true);
138
+ expect(satisfiesVersion('>=1.0.0', '0.9.0')).toBe(false);
139
+ });
140
+
141
+ it('should handle <=', () => {
142
+ expect(satisfiesVersion('<=2.0.0', '2.0.0')).toBe(true);
143
+ expect(satisfiesVersion('<=2.0.0', '1.0.0')).toBe(true);
144
+ expect(satisfiesVersion('<=2.0.0', '3.0.0')).toBe(false);
145
+ });
146
+
147
+ it('should handle >', () => {
148
+ expect(satisfiesVersion('>1.0.0', '1.0.1')).toBe(true);
149
+ expect(satisfiesVersion('>1.0.0', '2.0.0')).toBe(true);
150
+ expect(satisfiesVersion('>1.0.0', '1.0.0')).toBe(false);
151
+ });
152
+
153
+ it('should handle <', () => {
154
+ expect(satisfiesVersion('<2.0.0', '1.9.9')).toBe(true);
155
+ expect(satisfiesVersion('<2.0.0', '1.0.0')).toBe(true);
156
+ expect(satisfiesVersion('<2.0.0', '2.0.0')).toBe(false);
157
+ });
158
+ });
159
+
160
+ describe('range expressions', () => {
161
+ it('should handle AND ranges', () => {
162
+ expect(satisfiesVersion('>=1.0.0 <2.0.0', '1.5.0')).toBe(true);
163
+ expect(satisfiesVersion('>=1.0.0 <2.0.0', '1.0.0')).toBe(true);
164
+ expect(satisfiesVersion('>=1.0.0 <2.0.0', '1.9.9')).toBe(true);
165
+ expect(satisfiesVersion('>=1.0.0 <2.0.0', '2.0.0')).toBe(false);
166
+ expect(satisfiesVersion('>=1.0.0 <2.0.0', '0.9.9')).toBe(false);
167
+ });
168
+ });
169
+ });
170
+
171
+ // ============================================================================
172
+ // Dependency Graph Tests
173
+ // ============================================================================
174
+
175
+ describe('DependencyGraph', () => {
176
+ let graph: DependencyGraph;
177
+
178
+ beforeEach(() => {
179
+ graph = new DependencyGraph();
180
+ });
181
+
182
+ describe('addPlugin', () => {
183
+ it('should add a plugin without dependencies', () => {
184
+ graph.addPlugin('plugin-a', '1.0.0');
185
+ expect(graph.hasPlugin('plugin-a')).toBe(true);
186
+ expect(graph.getVersion('plugin-a')).toBe('1.0.0');
187
+ });
188
+
189
+ it('should add a plugin with dependencies', () => {
190
+ graph.addPlugin('plugin-a', '1.0.0');
191
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
192
+
193
+ expect(graph.getDependencies('plugin-b')).toEqual(['plugin-a']);
194
+ expect(graph.getDependents('plugin-a')).toEqual(['plugin-b']);
195
+ });
196
+ });
197
+
198
+ describe('removePlugin', () => {
199
+ it('should remove a plugin and its edges', () => {
200
+ graph.addPlugin('plugin-a', '1.0.0');
201
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
202
+
203
+ graph.removePlugin('plugin-b');
204
+
205
+ expect(graph.hasPlugin('plugin-b')).toBe(false);
206
+ expect(graph.getDependents('plugin-a')).toEqual([]);
207
+ });
208
+
209
+ it('should handle removing a plugin with dependents', () => {
210
+ graph.addPlugin('plugin-a', '1.0.0');
211
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
212
+
213
+ graph.removePlugin('plugin-a');
214
+
215
+ expect(graph.hasPlugin('plugin-a')).toBe(false);
216
+ expect(graph.getDependencies('plugin-b')).toEqual([]);
217
+ });
218
+ });
219
+
220
+ describe('getDependencies', () => {
221
+ it('should return direct dependencies', () => {
222
+ graph.addPlugin('plugin-a', '1.0.0');
223
+ graph.addPlugin('plugin-b', '1.0.0');
224
+ graph.addPlugin('plugin-c', '1.0.0', [
225
+ { name: 'plugin-a', version: '^1.0.0' },
226
+ { name: 'plugin-b', version: '^1.0.0' },
227
+ ]);
228
+
229
+ expect(graph.getDependencies('plugin-c')).toEqual(['plugin-a', 'plugin-b']);
230
+ });
231
+
232
+ it('should return empty array for plugin with no dependencies', () => {
233
+ graph.addPlugin('plugin-a', '1.0.0');
234
+ expect(graph.getDependencies('plugin-a')).toEqual([]);
235
+ });
236
+ });
237
+
238
+ describe('getDependents', () => {
239
+ it('should return plugins that depend on this one', () => {
240
+ graph.addPlugin('plugin-a', '1.0.0');
241
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
242
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
243
+
244
+ const dependents = graph.getDependents('plugin-a');
245
+ expect(dependents).toContain('plugin-b');
246
+ expect(dependents).toContain('plugin-c');
247
+ expect(dependents).toHaveLength(2);
248
+ });
249
+ });
250
+
251
+ describe('getAllDependencies', () => {
252
+ it('should return transitive dependencies', () => {
253
+ graph.addPlugin('plugin-a', '1.0.0');
254
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
255
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
256
+
257
+ const allDeps = graph.getAllDependencies('plugin-c');
258
+ expect(allDeps).toContain('plugin-a');
259
+ expect(allDeps).toContain('plugin-b');
260
+ });
261
+ });
262
+
263
+ describe('getAllDependents', () => {
264
+ it('should return transitive dependents', () => {
265
+ graph.addPlugin('plugin-a', '1.0.0');
266
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
267
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
268
+
269
+ const allDependents = graph.getAllDependents('plugin-a');
270
+ expect(allDependents).toContain('plugin-b');
271
+ expect(allDependents).toContain('plugin-c');
272
+ });
273
+ });
274
+
275
+ describe('canSafelyRemove', () => {
276
+ it('should return true for plugins with no dependents', () => {
277
+ graph.addPlugin('plugin-a', '1.0.0');
278
+ expect(graph.canSafelyRemove('plugin-a')).toBe(true);
279
+ });
280
+
281
+ it('should return false for plugins with dependents', () => {
282
+ graph.addPlugin('plugin-a', '1.0.0');
283
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
284
+
285
+ expect(graph.canSafelyRemove('plugin-a')).toBe(false);
286
+ });
287
+ });
288
+
289
+ describe('getRemovalOrder', () => {
290
+ it('should return correct removal order (dependents first)', () => {
291
+ graph.addPlugin('plugin-a', '1.0.0');
292
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
293
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
294
+
295
+ const order = graph.getRemovalOrder('plugin-a');
296
+ expect(order.indexOf('plugin-c')).toBeLessThan(order.indexOf('plugin-b'));
297
+ expect(order.indexOf('plugin-b')).toBeLessThan(order.indexOf('plugin-a'));
298
+ });
299
+ });
300
+
301
+ describe('validate', () => {
302
+ it('should detect missing dependencies', () => {
303
+ graph.addPlugin('plugin-a', '1.0.0', [{ name: 'missing-plugin', version: '^1.0.0' }]);
304
+
305
+ const errors = graph.validate();
306
+ expect(errors).toHaveLength(1);
307
+ expect(errors[0].type).toBe('missing');
308
+ expect(errors[0].dependency).toBe('missing-plugin');
309
+ });
310
+
311
+ it('should detect version mismatches', () => {
312
+ graph.addPlugin('plugin-a', '1.0.0');
313
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^2.0.0' }]);
314
+
315
+ const errors = graph.validate();
316
+ expect(errors.some(e => e.type === 'version_mismatch')).toBe(true);
317
+ });
318
+
319
+ it('should detect circular dependencies', () => {
320
+ graph.addPlugin('plugin-a', '1.0.0', [{ name: 'plugin-c', version: '^1.0.0' }]);
321
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
322
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
323
+
324
+ const errors = graph.validate();
325
+ expect(errors.some(e => e.type === 'circular')).toBe(true);
326
+ });
327
+
328
+ it('should pass validation for valid dependencies', () => {
329
+ graph.addPlugin('plugin-a', '1.0.0');
330
+ graph.addPlugin('plugin-b', '1.5.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
331
+
332
+ const errors = graph.validate();
333
+ expect(errors).toHaveLength(0);
334
+ });
335
+ });
336
+
337
+ describe('detectCircular', () => {
338
+ it('should detect simple cycles', () => {
339
+ graph.addPlugin('plugin-a', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
340
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
341
+
342
+ const cycles = graph.detectCircular();
343
+ expect(cycles.length).toBeGreaterThan(0);
344
+ });
345
+
346
+ it('should detect longer cycles', () => {
347
+ graph.addPlugin('plugin-a', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
348
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-c', version: '^1.0.0' }]);
349
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
350
+
351
+ const cycles = graph.detectCircular();
352
+ expect(cycles.length).toBeGreaterThan(0);
353
+ });
354
+
355
+ it('should return empty array for acyclic graphs', () => {
356
+ graph.addPlugin('plugin-a', '1.0.0');
357
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
358
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
359
+
360
+ const cycles = graph.detectCircular();
361
+ expect(cycles).toHaveLength(0);
362
+ });
363
+ });
364
+
365
+ describe('getLoadOrder', () => {
366
+ it('should return topological order', () => {
367
+ graph.addPlugin('plugin-a', '1.0.0');
368
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
369
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-b', version: '^1.0.0' }]);
370
+
371
+ const order = graph.getLoadOrder();
372
+ expect(order.indexOf('plugin-a')).toBeLessThan(order.indexOf('plugin-b'));
373
+ expect(order.indexOf('plugin-b')).toBeLessThan(order.indexOf('plugin-c'));
374
+ });
375
+
376
+ it('should handle multiple roots', () => {
377
+ graph.addPlugin('plugin-a', '1.0.0');
378
+ graph.addPlugin('plugin-b', '1.0.0');
379
+ graph.addPlugin('plugin-c', '1.0.0', [
380
+ { name: 'plugin-a', version: '^1.0.0' },
381
+ { name: 'plugin-b', version: '^1.0.0' },
382
+ ]);
383
+
384
+ const order = graph.getLoadOrder();
385
+ expect(order.indexOf('plugin-a')).toBeLessThan(order.indexOf('plugin-c'));
386
+ expect(order.indexOf('plugin-b')).toBeLessThan(order.indexOf('plugin-c'));
387
+ });
388
+ });
389
+
390
+ describe('getDepthLevels', () => {
391
+ it('should group plugins by dependency depth', () => {
392
+ graph.addPlugin('plugin-a', '1.0.0');
393
+ graph.addPlugin('plugin-b', '1.0.0');
394
+ graph.addPlugin('plugin-c', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
395
+ graph.addPlugin('plugin-d', '1.0.0', [{ name: 'plugin-c', version: '^1.0.0' }]);
396
+
397
+ const levels = graph.getDepthLevels();
398
+
399
+ expect(levels[0]).toContain('plugin-a');
400
+ expect(levels[0]).toContain('plugin-b');
401
+ expect(levels[1]).toContain('plugin-c');
402
+ expect(levels[2]).toContain('plugin-d');
403
+ });
404
+
405
+ it('should handle complex dependency trees', () => {
406
+ graph.addPlugin('core', '1.0.0');
407
+ graph.addPlugin('utils', '1.0.0', [{ name: 'core', version: '^1.0.0' }]);
408
+ graph.addPlugin('auth', '1.0.0', [{ name: 'core', version: '^1.0.0' }]);
409
+ graph.addPlugin('api', '1.0.0', [
410
+ { name: 'utils', version: '^1.0.0' },
411
+ { name: 'auth', version: '^1.0.0' },
412
+ ]);
413
+
414
+ const levels = graph.getDepthLevels();
415
+
416
+ expect(levels[0]).toContain('core');
417
+ expect(levels[1]).toContain('utils');
418
+ expect(levels[1]).toContain('auth');
419
+ expect(levels[2]).toContain('api');
420
+ });
421
+ });
422
+
423
+ describe('toJSON', () => {
424
+ it('should export graph structure', () => {
425
+ graph.addPlugin('plugin-a', '1.0.0');
426
+ graph.addPlugin('plugin-b', '1.0.0', [{ name: 'plugin-a', version: '^1.0.0' }]);
427
+
428
+ const json = graph.toJSON() as any;
429
+
430
+ expect(json.nodes).toHaveLength(2);
431
+ expect(json.edges).toHaveLength(2);
432
+ });
433
+ });
434
+ });