ruvector 0.2.23 → 0.2.25

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 (111) hide show
  1. package/bin/cli.js +211 -63
  2. package/dist/analysis/complexity.d.ts +52 -0
  3. package/dist/analysis/complexity.d.ts.map +1 -0
  4. package/dist/analysis/complexity.js +146 -0
  5. package/dist/analysis/index.d.ts +15 -0
  6. package/dist/analysis/index.d.ts.map +1 -0
  7. package/dist/analysis/index.js +38 -0
  8. package/dist/analysis/patterns.d.ts +71 -0
  9. package/dist/analysis/patterns.d.ts.map +1 -0
  10. package/dist/analysis/patterns.js +243 -0
  11. package/dist/analysis/security.d.ts +51 -0
  12. package/dist/analysis/security.d.ts.map +1 -0
  13. package/dist/analysis/security.js +139 -0
  14. package/dist/core/adaptive-embedder.d.ts +156 -0
  15. package/dist/core/adaptive-embedder.d.ts.map +1 -0
  16. package/dist/core/adaptive-embedder.js +838 -0
  17. package/dist/core/agentdb-fast.d.ts +149 -0
  18. package/dist/core/agentdb-fast.d.ts.map +1 -0
  19. package/dist/core/agentdb-fast.js +301 -0
  20. package/dist/core/ast-parser.d.ts +108 -0
  21. package/dist/core/ast-parser.d.ts.map +1 -0
  22. package/dist/core/ast-parser.js +602 -0
  23. package/dist/core/attention-fallbacks.d.ts +321 -0
  24. package/dist/core/attention-fallbacks.d.ts.map +1 -0
  25. package/dist/core/attention-fallbacks.js +552 -0
  26. package/dist/core/cluster-wrapper.d.ts +148 -0
  27. package/dist/core/cluster-wrapper.d.ts.map +1 -0
  28. package/dist/core/cluster-wrapper.js +271 -0
  29. package/dist/core/coverage-router.d.ts +88 -0
  30. package/dist/core/coverage-router.d.ts.map +1 -0
  31. package/dist/core/coverage-router.js +315 -0
  32. package/dist/core/diff-embeddings.d.ts +93 -0
  33. package/dist/core/diff-embeddings.d.ts.map +1 -0
  34. package/dist/core/diff-embeddings.js +334 -0
  35. package/dist/core/diskann-wrapper.d.ts +53 -0
  36. package/dist/core/diskann-wrapper.d.ts.map +1 -0
  37. package/dist/core/diskann-wrapper.js +105 -0
  38. package/dist/core/gnn-wrapper.d.ts +143 -0
  39. package/dist/core/gnn-wrapper.d.ts.map +1 -0
  40. package/dist/core/gnn-wrapper.js +213 -0
  41. package/dist/core/graph-algorithms.d.ts +83 -0
  42. package/dist/core/graph-algorithms.d.ts.map +1 -0
  43. package/dist/core/graph-algorithms.js +514 -0
  44. package/dist/core/graph-wrapper.d.ts +147 -0
  45. package/dist/core/graph-wrapper.d.ts.map +1 -0
  46. package/dist/core/graph-wrapper.js +299 -0
  47. package/dist/core/index.d.ts +50 -0
  48. package/dist/core/index.d.ts.map +1 -0
  49. package/dist/core/index.js +92 -0
  50. package/dist/core/intelligence-engine.d.ts +258 -0
  51. package/dist/core/intelligence-engine.d.ts.map +1 -0
  52. package/dist/core/intelligence-engine.js +1030 -0
  53. package/dist/core/learning-engine.d.ts +160 -0
  54. package/dist/core/learning-engine.d.ts.map +1 -0
  55. package/dist/core/learning-engine.js +589 -0
  56. package/dist/core/neural-embeddings.d.ts +393 -0
  57. package/dist/core/neural-embeddings.d.ts.map +1 -0
  58. package/dist/core/neural-embeddings.js +1091 -0
  59. package/dist/core/neural-perf.d.ts +331 -0
  60. package/dist/core/neural-perf.d.ts.map +1 -0
  61. package/dist/core/neural-perf.js +704 -0
  62. package/dist/core/onnx/pkg/package.json +3 -0
  63. package/dist/core/onnx-embedder.d.ts +105 -0
  64. package/dist/core/onnx-embedder.d.ts.map +1 -0
  65. package/dist/core/onnx-embedder.js +410 -0
  66. package/dist/core/onnx-optimized.d.ts +109 -0
  67. package/dist/core/onnx-optimized.d.ts.map +1 -0
  68. package/dist/core/onnx-optimized.js +419 -0
  69. package/dist/core/parallel-intelligence.d.ts +109 -0
  70. package/dist/core/parallel-intelligence.d.ts.map +1 -0
  71. package/dist/core/parallel-intelligence.js +340 -0
  72. package/dist/core/parallel-workers.d.ts +177 -0
  73. package/dist/core/parallel-workers.d.ts.map +1 -0
  74. package/dist/core/parallel-workers.js +783 -0
  75. package/dist/core/router-wrapper.d.ts +75 -0
  76. package/dist/core/router-wrapper.d.ts.map +1 -0
  77. package/dist/core/router-wrapper.js +243 -0
  78. package/dist/core/rvf-wrapper.d.ts +86 -0
  79. package/dist/core/rvf-wrapper.d.ts.map +1 -0
  80. package/dist/core/rvf-wrapper.js +102 -0
  81. package/dist/core/sona-wrapper.d.ts +226 -0
  82. package/dist/core/sona-wrapper.d.ts.map +1 -0
  83. package/dist/core/sona-wrapper.js +282 -0
  84. package/dist/core/tensor-compress.d.ts +134 -0
  85. package/dist/core/tensor-compress.d.ts.map +1 -0
  86. package/dist/core/tensor-compress.js +432 -0
  87. package/dist/index.d.ts +106 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +258 -0
  90. package/dist/services/embedding-service.d.ts +136 -0
  91. package/dist/services/embedding-service.d.ts.map +1 -0
  92. package/dist/services/embedding-service.js +294 -0
  93. package/dist/services/index.d.ts +6 -0
  94. package/dist/services/index.d.ts.map +1 -0
  95. package/dist/services/index.js +26 -0
  96. package/dist/types.d.ts +145 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +2 -0
  99. package/dist/workers/benchmark.d.ts +44 -0
  100. package/dist/workers/benchmark.d.ts.map +1 -0
  101. package/dist/workers/benchmark.js +230 -0
  102. package/dist/workers/index.d.ts +10 -0
  103. package/dist/workers/index.d.ts.map +1 -0
  104. package/dist/workers/index.js +25 -0
  105. package/dist/workers/native-worker.d.ts +76 -0
  106. package/dist/workers/native-worker.d.ts.map +1 -0
  107. package/dist/workers/native-worker.js +490 -0
  108. package/dist/workers/types.d.ts +69 -0
  109. package/dist/workers/types.d.ts.map +1 -0
  110. package/dist/workers/types.js +7 -0
  111. package/package.json +8 -7
package/bin/cli.js CHANGED
@@ -125,6 +125,21 @@ const program = new Command();
125
125
  // Get package version from package.json
126
126
  const packageJson = require('../package.json');
127
127
 
128
+ // `@ruvector/gnn@0.1.25` has a native-binding regression where every method
129
+ // throws `Given napi value is not an array` regardless of input shape (verified
130
+ // with both Array<Float32Array> and number[][]). Use this helper to print a
131
+ // pointer to the upstream issue when the CLI-side typed-array conversion is
132
+ // already correct.
133
+ function reportGnnBindingError(error) {
134
+ const msg = error && error.message ? error.message : String(error);
135
+ console.error(chalk.red(msg));
136
+ if (msg.includes('Given napi value is not an array') || msg.includes('TypedArray info failed')) {
137
+ console.error(chalk.yellow(' Note: this is a known regression in the @ruvector/gnn native binding,'));
138
+ console.error(chalk.yellow(' not in the CLI. Track at:'));
139
+ console.error(chalk.white(' https://github.com/ruvnet/ruvector/issues/402'));
140
+ }
141
+ }
142
+
128
143
  // Version and description (lazy load implementation info)
129
144
  program
130
145
  .name('ruvector')
@@ -758,13 +773,16 @@ gnnCmd
758
773
  if (options.test) {
759
774
  spinner.start('Running test forward pass...');
760
775
 
761
- // Create test data
762
- const nodeEmbedding = Array.from({ length: inputDim }, () => Math.random());
763
- const neighborEmbeddings = [
764
- Array.from({ length: inputDim }, () => Math.random()),
765
- Array.from({ length: inputDim }, () => Math.random())
766
- ];
767
- const edgeWeights = [0.6, 0.4];
776
+ // The @ruvector/gnn binding requires Float32Array — plain number[] surfaces
777
+ // as `Get TypedArray info failed` from napi-rs.
778
+ const randVec = (n) => {
779
+ const v = new Float32Array(n);
780
+ for (let i = 0; i < n; i++) v[i] = Math.random();
781
+ return v;
782
+ };
783
+ const nodeEmbedding = randVec(inputDim);
784
+ const neighborEmbeddings = [randVec(inputDim), randVec(inputDim)];
785
+ const edgeWeights = new Float32Array([0.6, 0.4]);
768
786
 
769
787
  const output = layer.forward(nodeEmbedding, neighborEmbeddings, edgeWeights);
770
788
  spinner.succeed(chalk.green('Forward pass completed'));
@@ -782,7 +800,7 @@ gnnCmd
782
800
  }
783
801
  } catch (error) {
784
802
  spinner.fail(chalk.red('Failed to create GNN layer'));
785
- console.error(chalk.red(error.message));
803
+ reportGnnBindingError(error);
786
804
  process.exit(1);
787
805
  }
788
806
  });
@@ -812,7 +830,9 @@ gnnCmd
812
830
  let totalCompressedSize = 0;
813
831
 
814
832
  for (const embedding of embeddings) {
815
- const vec = embedding.vector || embedding;
833
+ const rawVec = embedding.vector || embedding;
834
+ // TensorCompress requires Float32Array.
835
+ const vec = rawVec instanceof Float32Array ? rawVec : new Float32Array(rawVec);
816
836
  totalOriginalSize += vec.length * 4; // float32 = 4 bytes
817
837
 
818
838
  let compressed;
@@ -855,7 +875,7 @@ gnnCmd
855
875
  }
856
876
  } catch (error) {
857
877
  spinner.fail(chalk.red('Failed to compress embeddings'));
858
- console.error(chalk.red(error.message));
878
+ reportGnnBindingError(error);
859
879
  process.exit(1);
860
880
  }
861
881
  });
@@ -875,12 +895,18 @@ gnnCmd
875
895
  try {
876
896
  const query = JSON.parse(options.query);
877
897
  const candidatesData = JSON.parse(fs.readFileSync(options.candidates, 'utf8'));
878
- const candidates = candidatesData.map(c => c.vector || c);
898
+ // @ruvector/gnn's differentiableSearch needs Float32Array everywhere; plain
899
+ // number[] surfaces as napi-rs `Get TypedArray info failed`.
900
+ const queryVec = query instanceof Float32Array ? query : new Float32Array(query);
901
+ const candidates = candidatesData.map((c) => {
902
+ const v = c.vector || c;
903
+ return v instanceof Float32Array ? v : new Float32Array(v);
904
+ });
879
905
  const k = parseInt(options.topK);
880
906
  const temperature = parseFloat(options.temperature);
881
907
 
882
908
  spinner.text = 'Running differentiable search...';
883
- const result = differentiableSearch(query, candidates, k, temperature);
909
+ const result = differentiableSearch(queryVec, candidates, k, temperature);
884
910
 
885
911
  spinner.succeed(chalk.green(`Found top-${k} results`));
886
912
 
@@ -889,17 +915,19 @@ gnnCmd
889
915
  console.log(chalk.white(` Candidates: ${chalk.yellow(candidates.length)}`));
890
916
  console.log(chalk.white(` Temperature: ${chalk.yellow(temperature)}`));
891
917
 
918
+ // The wrapper exposes `weights`; older native shape used `attention_weights`.
919
+ const weights = result.weights || result.attention_weights || [];
892
920
  console.log(chalk.cyan('\nTop-K Results:'));
893
921
  for (let i = 0; i < result.indices.length; i++) {
894
922
  const idx = result.indices[i];
895
- const weight = result.weights[i];
923
+ const weight = weights[i];
896
924
  const id = candidatesData[idx]?.id || `candidate_${idx}`;
897
925
  console.log(chalk.white(` ${i + 1}. ${chalk.yellow(id)} (index: ${idx})`));
898
- console.log(chalk.gray(` Weight: ${weight.toFixed(6)}`));
926
+ console.log(chalk.gray(` Weight: ${weight != null ? weight.toFixed(6) : 'n/a'}`));
899
927
  }
900
928
  } catch (error) {
901
929
  spinner.fail(chalk.red('Failed to run search'));
902
- console.error(chalk.red(error.message));
930
+ reportGnnBindingError(error);
903
931
  process.exit(1);
904
932
  }
905
933
  });
@@ -971,59 +999,60 @@ attentionCmd
971
999
  const spinner = ora('Loading keys...').start();
972
1000
 
973
1001
  try {
974
- const query = JSON.parse(options.query);
1002
+ const queryRaw = JSON.parse(options.query);
975
1003
  const keysData = JSON.parse(fs.readFileSync(options.keys, 'utf8'));
976
- const keys = keysData.map(k => k.vector || k);
1004
+ // The native @ruvector/attention bindings require Float32Array; passing
1005
+ // plain number[] surfaces as napi-rs `Get TypedArray info failed` or
1006
+ // (when dim is read off a missing arg) `... Undefined into rust type u32`.
1007
+ const toF32 = (v) => (v instanceof Float32Array ? v : new Float32Array(v));
1008
+ const query = toF32(queryRaw);
1009
+ const keys = keysData.map((k) => toF32(k.vector || k));
977
1010
 
978
1011
  let values = keys;
979
1012
  if (options.values) {
980
1013
  const valuesData = JSON.parse(fs.readFileSync(options.values, 'utf8'));
981
- values = valuesData.map(v => v.vector || v);
1014
+ values = valuesData.map((v) => toF32(v.vector || v));
982
1015
  }
983
1016
 
1017
+ const dim = query.length;
1018
+
984
1019
  spinner.text = `Computing ${options.type} attention...`;
985
1020
 
986
1021
  let result;
987
1022
  let attentionWeights;
988
1023
 
1024
+ // The native @ruvector/attention bindings expose `compute(query, keys, values)`
1025
+ // — a flat Float32Array query plus Float32Array[] keys/values, returning a
1026
+ // flat Float32Array. The older CLI invoked `forward([query], keys, values)`,
1027
+ // which doesn't exist on the current binding (issue #402 §B).
989
1028
  switch (options.type) {
990
1029
  case 'dot': {
991
- const attn = new DotProductAttention();
992
- const queryMat = [query];
993
- const output = attn.forward(queryMat, keys, values);
994
- result = output[0];
995
- attentionWeights = attn.getLastWeights ? attn.getLastWeights()[0] : null;
1030
+ const attn = new DotProductAttention(dim);
1031
+ result = attn.compute(query, keys, values);
1032
+ attentionWeights = attn.getLastWeights ? attn.getLastWeights() : null;
996
1033
  break;
997
1034
  }
998
1035
  case 'multi-head': {
999
1036
  const numHeads = parseInt(options.heads);
1000
1037
  const headDim = parseInt(options.headDim);
1001
- const attn = new MultiHeadAttention(query.length, numHeads, headDim);
1002
- const queryMat = [query];
1003
- const output = attn.forward(queryMat, keys, values);
1004
- result = output[0];
1038
+ const attn = new MultiHeadAttention(dim, numHeads, headDim);
1039
+ result = attn.compute(query, keys, values);
1005
1040
  break;
1006
1041
  }
1007
1042
  case 'flash': {
1008
- const attn = new FlashAttention(query.length);
1009
- const queryMat = [query];
1010
- const output = attn.forward(queryMat, keys, values);
1011
- result = output[0];
1043
+ const attn = new FlashAttention(dim);
1044
+ result = attn.compute(query, keys, values);
1012
1045
  break;
1013
1046
  }
1014
1047
  case 'hyperbolic': {
1015
1048
  const curvature = parseFloat(options.curvature);
1016
- const attn = new HyperbolicAttention(query.length, curvature);
1017
- const queryMat = [query];
1018
- const output = attn.forward(queryMat, keys, values);
1019
- result = output[0];
1049
+ const attn = new HyperbolicAttention(dim, curvature);
1050
+ result = attn.compute(query, keys, values);
1020
1051
  break;
1021
1052
  }
1022
1053
  case 'linear': {
1023
- const attn = new LinearAttention(query.length);
1024
- const queryMat = [query];
1025
- const output = attn.forward(queryMat, keys, values);
1026
- result = output[0];
1054
+ const attn = new LinearAttention(dim);
1055
+ result = attn.compute(query, keys, values);
1027
1056
  break;
1028
1057
  }
1029
1058
  default:
@@ -1619,7 +1648,8 @@ program
1619
1648
  console.log(chalk.white(' cargo add ruvector-raft # Raft consensus'));
1620
1649
  console.log(chalk.white(' cargo add ruvector-replication # Data replication'));
1621
1650
  console.log(chalk.white(' cargo add ruvector-tiny-dancer-core # AI routing'));
1622
- console.log(chalk.white(' cargo add ruvector-router-core # Semantic routing'));
1651
+ console.log(chalk.white(' cargo add ruvector-router-core # Semantic routing (Rust crate)'));
1652
+ console.log(chalk.white(' npm install @ruvector/router # Semantic routing (npm)'));
1623
1653
  console.log('');
1624
1654
 
1625
1655
  console.log(chalk.yellow('Platform-specific notes:'));
@@ -1737,7 +1767,7 @@ program
1737
1767
 
1738
1768
  program
1739
1769
  .command('router')
1740
- .description('AI semantic router operations (requires ruvector-router-core)')
1770
+ .description('AI semantic router operations (requires @ruvector/router)')
1741
1771
  .option('--route <text>', 'Route text to best matching intent')
1742
1772
  .option('--intents <file>', 'Load intents from JSON file')
1743
1773
  .option('--add-intent <name>', 'Add new intent')
@@ -1758,8 +1788,9 @@ program
1758
1788
  console.log(chalk.gray(' - Vector-based similarity matching'));
1759
1789
  console.log('');
1760
1790
  console.log(chalk.cyan(' Status:'), chalk.yellow('Coming Soon'));
1761
- console.log(chalk.gray(' The npm package for router is in development.'));
1762
- console.log(chalk.gray(' Rust crate available: cargo add ruvector-router-core'));
1791
+ console.log(chalk.gray(' The router subcommand integration is still in development.'));
1792
+ console.log(chalk.gray(' npm package: npm install @ruvector/router'));
1793
+ console.log(chalk.gray(' Rust crate: cargo add ruvector-router-core'));
1763
1794
  console.log('');
1764
1795
  console.log(chalk.cyan(' Usage (when available):'));
1765
1796
  console.log(chalk.white(' npx ruvector router --route "What is the weather?"'));
@@ -2534,13 +2565,15 @@ program
2534
2565
  const spinner = ora('Creating demo database...').start();
2535
2566
 
2536
2567
  try {
2537
- const db = new VectorDB({ dimensions: 4, metric: 'cosine' });
2568
+ const db = new VectorDB({ dimensions: 4, distanceMetric: 'cosine' });
2538
2569
 
2539
2570
  spinner.text = 'Inserting vectors...';
2540
- db.insert('vec1', [1.0, 0.0, 0.0, 0.0], { label: 'x-axis' });
2541
- db.insert('vec2', [0.0, 1.0, 0.0, 0.0], { label: 'y-axis' });
2542
- db.insert('vec3', [0.0, 0.0, 1.0, 0.0], { label: 'z-axis' });
2543
- db.insert('vec4', [0.7, 0.7, 0.0, 0.0], { label: 'xy-diagonal' });
2571
+ // VectorDBWrapper.insert takes a single object: { id?, vector, metadata? }.
2572
+ // Wrap to Float32Array so the native binding sees the right typed array.
2573
+ await db.insert({ id: 'vec1', vector: new Float32Array([1.0, 0.0, 0.0, 0.0]), metadata: { label: 'x-axis' } });
2574
+ await db.insert({ id: 'vec2', vector: new Float32Array([0.0, 1.0, 0.0, 0.0]), metadata: { label: 'y-axis' } });
2575
+ await db.insert({ id: 'vec3', vector: new Float32Array([0.0, 0.0, 1.0, 0.0]), metadata: { label: 'z-axis' } });
2576
+ await db.insert({ id: 'vec4', vector: new Float32Array([0.7, 0.7, 0.0, 0.0]), metadata: { label: 'xy-diagonal' } });
2544
2577
 
2545
2578
  spinner.succeed('Demo database created with 4 vectors');
2546
2579
 
@@ -2551,7 +2584,7 @@ program
2551
2584
  console.log(chalk.gray(' vec4: [0.7,0.7,0,0] - xy-diagonal'));
2552
2585
 
2553
2586
  console.log(chalk.cyan('\n Searching for nearest to [0.8, 0.6, 0, 0]:'));
2554
- const results = db.search([0.8, 0.6, 0.0, 0.0], 3);
2587
+ const results = await db.search({ vector: new Float32Array([0.8, 0.6, 0.0, 0.0]), k: 3 });
2555
2588
  results.forEach((r, i) => {
2556
2589
  console.log(chalk.gray(` ${i + 1}. ${r.id} (score: ${r.score.toFixed(4)})`));
2557
2590
  });
@@ -2577,20 +2610,26 @@ program
2577
2610
  try {
2578
2611
  console.log(chalk.cyan(' Running differentiable search with gradients...\n'));
2579
2612
 
2580
- const queryVec = [1.0, 0.5, 0.3, 0.1];
2613
+ // The native @ruvector/gnn binding expects Float32Array typed arrays.
2614
+ const queryVec = new Float32Array([1.0, 0.5, 0.3, 0.1]);
2581
2615
  const dbVectors = [
2582
- [1.0, 0.0, 0.0, 0.0],
2583
- [0.0, 1.0, 0.0, 0.0],
2584
- [0.5, 0.5, 0.5, 0.5],
2585
- [0.9, 0.4, 0.2, 0.1]
2616
+ new Float32Array([1.0, 0.0, 0.0, 0.0]),
2617
+ new Float32Array([0.0, 1.0, 0.0, 0.0]),
2618
+ new Float32Array([0.5, 0.5, 0.5, 0.5]),
2619
+ new Float32Array([0.9, 0.4, 0.2, 0.1]),
2586
2620
  ];
2587
2621
 
2588
2622
  const result = differentiableSearch(queryVec, dbVectors, 3, 10.0);
2589
2623
 
2590
- console.log(chalk.cyan(' Query:'), JSON.stringify(queryVec));
2624
+ // The wrapper returns `{ indices, weights }`; older binding versions
2625
+ // exposed `attention_weights` instead.
2626
+ const weights = result.weights || result.attention_weights || [];
2627
+
2628
+ console.log(chalk.cyan(' Query:'), JSON.stringify(Array.from(queryVec)));
2591
2629
  console.log(chalk.cyan(' Top 3 results:'));
2592
2630
  result.indices.forEach((idx, i) => {
2593
- console.log(chalk.gray(` ${i + 1}. Index ${idx} (attention: ${result.attention_weights[i].toFixed(4)})`));
2631
+ const w = weights[i] != null ? weights[i].toFixed(4) : 'n/a';
2632
+ console.log(chalk.gray(` ${i + 1}. Index ${idx} (attention: ${w})`));
2594
2633
  });
2595
2634
 
2596
2635
  console.log(chalk.cyan('\n Gradient flow enabled:'), chalk.green('Yes'));
@@ -2598,7 +2637,19 @@ program
2598
2637
 
2599
2638
  console.log(chalk.green('\n GNN demo complete!'));
2600
2639
  } catch (error) {
2601
- console.error(chalk.red('GNN demo failed:', error.message));
2640
+ // `@ruvector/gnn@0.1.25`'s native binding has a regression where every
2641
+ // method throws `Given napi value is not an array`, regardless of the
2642
+ // input shape (verified with both Array<Float32Array> and number[][]).
2643
+ // Surface that explicitly so users don't think it's their CLI install.
2644
+ const msg = error && error.message ? error.message : String(error);
2645
+ if (msg.includes('not an array') || msg.includes('TypedArray')) {
2646
+ console.error(chalk.red(` GNN demo failed: ${msg}`));
2647
+ console.error(chalk.yellow('\n This looks like a regression in the @ruvector/gnn native binding,'));
2648
+ console.error(chalk.yellow(' not in the CLI. Tracking at:'));
2649
+ console.error(chalk.white(' https://github.com/ruvnet/ruvector/issues/402'));
2650
+ } else {
2651
+ console.error(chalk.red('GNN demo failed:', msg));
2652
+ }
2602
2653
  }
2603
2654
  }
2604
2655
 
@@ -2608,18 +2659,111 @@ program
2608
2659
  let graphNode;
2609
2660
  try {
2610
2661
  graphNode = require('@ruvector/graph-node');
2611
- console.log(chalk.green(' @ruvector/graph-node is available!'));
2612
- console.log(chalk.gray(' Full graph demo coming soon.'));
2613
2662
  } catch (e) {
2614
2663
  console.log(chalk.yellow(' @ruvector/graph-node not installed.'));
2615
2664
  console.log(chalk.white(' Install with: npm install @ruvector/graph-node'));
2665
+ console.log('');
2666
+ return;
2667
+ }
2668
+
2669
+ try {
2670
+ // The current binding exposes a `GraphDatabase` class (not Graph /
2671
+ // HyperGraph / RuVectorGraph) with createNode / createEdge / query.
2672
+ const GraphDatabase = graphNode.GraphDatabase;
2673
+ if (typeof GraphDatabase !== 'function') {
2674
+ console.log(chalk.yellow(' @ruvector/graph-node has no GraphDatabase constructor.'));
2675
+ console.log(chalk.gray(` Available exports: ${Object.keys(graphNode).join(', ')}`));
2676
+ return;
2677
+ }
2678
+
2679
+ const g = new GraphDatabase();
2680
+ console.log(chalk.green(' ✓ GraphDatabase instance created'));
2681
+
2682
+ // createNode / createEdge take a JsNode / JsEdge object (not positional
2683
+ // args) and are async — see @ruvector/graph-node index.d.ts.
2684
+ const aId = await g.createNode({
2685
+ id: 'alice',
2686
+ embedding: new Float32Array([1, 0, 0, 0]),
2687
+ properties: { name: 'Alice', label: 'Person' },
2688
+ });
2689
+ const bId = await g.createNode({
2690
+ id: 'bob',
2691
+ embedding: new Float32Array([0, 1, 0, 0]),
2692
+ properties: { name: 'Bob', label: 'Person' },
2693
+ });
2694
+ console.log(chalk.green(` ✓ Created nodes: Alice (${aId}), Bob (${bId})`));
2695
+
2696
+ const edgeId = await g.createEdge({
2697
+ from: 'alice',
2698
+ to: 'bob',
2699
+ description: 'KNOWS',
2700
+ embedding: new Float32Array([0.5, 0.5, 0, 0]),
2701
+ confidence: 0.95,
2702
+ });
2703
+ console.log(chalk.green(` ✓ Created edge Alice -[:KNOWS]-> Bob (${edgeId})`));
2704
+
2705
+ const stats = g.stats();
2706
+ console.log(chalk.gray(` Stats: ${typeof stats === 'string' ? stats : JSON.stringify(stats)}`));
2707
+
2708
+ console.log(chalk.green('\n Graph demo complete!'));
2709
+ } catch (error) {
2710
+ // The createNode/createEdge signatures vary across binding versions
2711
+ // (some take (label, propsJson), some take (label, propsObject)).
2712
+ // Print enough context that the user can adapt without guessing.
2713
+ console.error(chalk.red(` Graph demo failed: ${error.message}`));
2714
+ const G = graphNode && graphNode.GraphDatabase;
2715
+ if (G) {
2716
+ const methods = Object.getOwnPropertyNames(G.prototype || {}).filter((m) => m !== 'constructor');
2717
+ console.error(chalk.gray(` GraphDatabase prototype: ${methods.join(', ')}`));
2718
+ }
2616
2719
  }
2617
2720
  console.log('');
2618
2721
  }
2619
2722
 
2620
2723
  if (options.benchmark) {
2621
- console.log(chalk.yellow(' Redirecting to benchmark command...\n'));
2622
- console.log(chalk.white(' Run: npx ruvector benchmark'));
2724
+ requireRuvector();
2725
+ console.log(chalk.yellow(' Mini Benchmark Demo\n'));
2726
+
2727
+ try {
2728
+ // Note: ruvector-core-linux-x64-gnu@0.1.29 (and current sister binaries)
2729
+ // has a regression where the `dimensions` constructor arg is ignored
2730
+ // and inserts are pinned to dim=4. Tracking at issue #402. Keeping the
2731
+ // demo at dim=4 so it completes; once the binding is rebuilt from
2732
+ // current source, this can scale up.
2733
+ const dim = 4;
2734
+ const n = 1000;
2735
+ const k = 10;
2736
+ const db = new VectorDB({ dimensions: dim, distanceMetric: 'cosine' });
2737
+
2738
+ console.log(chalk.cyan(` Generating ${n} random ${dim}-dim vectors...`));
2739
+ const t0 = Date.now();
2740
+ const entries = [];
2741
+ for (let i = 0; i < n; i++) {
2742
+ const v = new Float32Array(dim);
2743
+ for (let j = 0; j < dim; j++) v[j] = Math.random();
2744
+ entries.push({ id: `v${i}`, vector: v });
2745
+ }
2746
+ const insertStart = Date.now();
2747
+ for (const entry of entries) await db.insert(entry);
2748
+ const insertMs = Date.now() - insertStart;
2749
+
2750
+ const queryVec = new Float32Array(dim);
2751
+ for (let j = 0; j < dim; j++) queryVec[j] = Math.random();
2752
+
2753
+ const searchStart = Date.now();
2754
+ const iters = 100;
2755
+ for (let i = 0; i < iters; i++) {
2756
+ await db.search({ vector: queryVec, k });
2757
+ }
2758
+ const searchMs = Date.now() - searchStart;
2759
+
2760
+ console.log(chalk.green(`\n ✓ Inserted ${n} vectors in ${insertMs}ms (${(insertMs / n).toFixed(2)}ms/vec)`));
2761
+ console.log(chalk.green(` ✓ ${iters}× top-${k} search in ${searchMs}ms (${(searchMs / iters).toFixed(2)}ms/query)`));
2762
+ console.log(chalk.gray(` Wall time: ${Date.now() - t0}ms`));
2763
+ console.log(chalk.gray(' For deeper benchmarks: npx ruvector benchmark'));
2764
+ } catch (error) {
2765
+ console.error(chalk.red(` Benchmark demo failed: ${error.message}`));
2766
+ }
2623
2767
  console.log('');
2624
2768
  }
2625
2769
  });
@@ -9119,10 +9263,14 @@ const optimizeCmd = program.command('optimize')
9119
9263
  .action(async (opts) => {
9120
9264
  let optimizerMod;
9121
9265
  try {
9266
+ // Resolve via package.json so it works whether the optimizer ships under
9267
+ // src/optimizer/ or dist/optimizer/.
9122
9268
  optimizerMod = require('../src/optimizer/index.js');
9123
9269
  } catch (e) {
9124
- console.error(chalk.red('Error: Failed to load optimizer module.'));
9125
- console.error(chalk.dim(` ${e.message}`));
9270
+ console.error(chalk.yellow('\n ruvector optimize: not yet shipped in this release.\n'));
9271
+ console.error(chalk.gray(' The optimizer module (profiles, settings generation) is in development'));
9272
+ console.error(chalk.gray(' and will land in a future release. Track progress at:'));
9273
+ console.error(chalk.white(' https://github.com/ruvnet/ruvector/issues/401\n'));
9126
9274
  process.exit(1);
9127
9275
  }
9128
9276
 
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Complexity Analysis Module - Consolidated code complexity metrics
3
+ *
4
+ * Single source of truth for cyclomatic complexity and code metrics.
5
+ * Used by native-worker.ts and parallel-workers.ts
6
+ */
7
+ export interface ComplexityResult {
8
+ file: string;
9
+ lines: number;
10
+ nonEmptyLines: number;
11
+ cyclomaticComplexity: number;
12
+ functions: number;
13
+ avgFunctionSize: number;
14
+ maxFunctionComplexity?: number;
15
+ }
16
+ export interface ComplexityThresholds {
17
+ complexity: number;
18
+ functions: number;
19
+ lines: number;
20
+ avgSize: number;
21
+ }
22
+ export declare const DEFAULT_THRESHOLDS: ComplexityThresholds;
23
+ /**
24
+ * Analyze complexity of a single file
25
+ */
26
+ export declare function analyzeFile(filePath: string, content?: string): ComplexityResult;
27
+ /**
28
+ * Analyze complexity of multiple files
29
+ */
30
+ export declare function analyzeFiles(files: string[], maxFiles?: number): ComplexityResult[];
31
+ /**
32
+ * Check if complexity exceeds thresholds
33
+ */
34
+ export declare function exceedsThresholds(result: ComplexityResult, thresholds?: ComplexityThresholds): boolean;
35
+ /**
36
+ * Get complexity rating
37
+ */
38
+ export declare function getComplexityRating(complexity: number): 'low' | 'medium' | 'high' | 'critical';
39
+ /**
40
+ * Filter files exceeding thresholds
41
+ */
42
+ export declare function filterComplex(results: ComplexityResult[], thresholds?: ComplexityThresholds): ComplexityResult[];
43
+ declare const _default: {
44
+ DEFAULT_THRESHOLDS: ComplexityThresholds;
45
+ analyzeFile: typeof analyzeFile;
46
+ analyzeFiles: typeof analyzeFiles;
47
+ exceedsThresholds: typeof exceedsThresholds;
48
+ getComplexityRating: typeof getComplexityRating;
49
+ filterComplex: typeof filterComplex;
50
+ };
51
+ export default _default;
52
+ //# sourceMappingURL=complexity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complexity.d.ts","sourceRoot":"","sources":["../../src/analysis/complexity.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,kBAAkB,EAAE,oBAKhC,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAsDhF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,GAAE,MAAY,GAAG,gBAAgB,EAAE,CAExF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,UAAU,GAAE,oBAAyC,GACpD,OAAO,CAOT;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAK9F;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,UAAU,GAAE,oBAAyC,GACpD,gBAAgB,EAAE,CAEpB;;;;;;;;;AAED,wBAOE"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Complexity Analysis Module - Consolidated code complexity metrics
4
+ *
5
+ * Single source of truth for cyclomatic complexity and code metrics.
6
+ * Used by native-worker.ts and parallel-workers.ts
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.DEFAULT_THRESHOLDS = void 0;
43
+ exports.analyzeFile = analyzeFile;
44
+ exports.analyzeFiles = analyzeFiles;
45
+ exports.exceedsThresholds = exceedsThresholds;
46
+ exports.getComplexityRating = getComplexityRating;
47
+ exports.filterComplex = filterComplex;
48
+ const fs = __importStar(require("fs"));
49
+ exports.DEFAULT_THRESHOLDS = {
50
+ complexity: 10,
51
+ functions: 30,
52
+ lines: 500,
53
+ avgSize: 50,
54
+ };
55
+ /**
56
+ * Analyze complexity of a single file
57
+ */
58
+ function analyzeFile(filePath, content) {
59
+ try {
60
+ const fileContent = content ?? (fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : '');
61
+ if (!fileContent) {
62
+ return { file: filePath, lines: 0, nonEmptyLines: 0, cyclomaticComplexity: 1, functions: 0, avgFunctionSize: 0 };
63
+ }
64
+ const lines = fileContent.split('\n');
65
+ const nonEmptyLines = lines.filter(l => l.trim().length > 0).length;
66
+ // Count branching statements for cyclomatic complexity
67
+ const branches = (fileContent.match(/\bif\b/g)?.length || 0) +
68
+ (fileContent.match(/\belse\b/g)?.length || 0) +
69
+ (fileContent.match(/\bfor\b/g)?.length || 0) +
70
+ (fileContent.match(/\bwhile\b/g)?.length || 0) +
71
+ (fileContent.match(/\bswitch\b/g)?.length || 0) +
72
+ (fileContent.match(/\bcase\b/g)?.length || 0) +
73
+ (fileContent.match(/\bcatch\b/g)?.length || 0) +
74
+ (fileContent.match(/\?\?/g)?.length || 0) +
75
+ (fileContent.match(/&&/g)?.length || 0) +
76
+ (fileContent.match(/\|\|/g)?.length || 0) +
77
+ (fileContent.match(/\?[^:]/g)?.length || 0); // Ternary
78
+ const cyclomaticComplexity = branches + 1;
79
+ // Count functions
80
+ const functionPatterns = [
81
+ /function\s+\w+/g,
82
+ /\w+\s*=\s*(?:async\s*)?\(/g,
83
+ /\w+\s*:\s*(?:async\s*)?\(/g,
84
+ /(?:async\s+)?(?:public|private|protected)?\s+\w+\s*\([^)]*\)\s*[:{]/g,
85
+ ];
86
+ let functions = 0;
87
+ for (const pattern of functionPatterns) {
88
+ functions += (fileContent.match(pattern) || []).length;
89
+ }
90
+ // Deduplicate by rough estimate
91
+ functions = Math.ceil(functions / 2);
92
+ const avgFunctionSize = functions > 0 ? Math.round(nonEmptyLines / functions) : nonEmptyLines;
93
+ return {
94
+ file: filePath,
95
+ lines: lines.length,
96
+ nonEmptyLines,
97
+ cyclomaticComplexity,
98
+ functions,
99
+ avgFunctionSize,
100
+ };
101
+ }
102
+ catch {
103
+ return { file: filePath, lines: 0, nonEmptyLines: 0, cyclomaticComplexity: 1, functions: 0, avgFunctionSize: 0 };
104
+ }
105
+ }
106
+ /**
107
+ * Analyze complexity of multiple files
108
+ */
109
+ function analyzeFiles(files, maxFiles = 100) {
110
+ return files.slice(0, maxFiles).map(f => analyzeFile(f));
111
+ }
112
+ /**
113
+ * Check if complexity exceeds thresholds
114
+ */
115
+ function exceedsThresholds(result, thresholds = exports.DEFAULT_THRESHOLDS) {
116
+ return (result.cyclomaticComplexity > thresholds.complexity ||
117
+ result.functions > thresholds.functions ||
118
+ result.lines > thresholds.lines ||
119
+ result.avgFunctionSize > thresholds.avgSize);
120
+ }
121
+ /**
122
+ * Get complexity rating
123
+ */
124
+ function getComplexityRating(complexity) {
125
+ if (complexity <= 5)
126
+ return 'low';
127
+ if (complexity <= 10)
128
+ return 'medium';
129
+ if (complexity <= 20)
130
+ return 'high';
131
+ return 'critical';
132
+ }
133
+ /**
134
+ * Filter files exceeding thresholds
135
+ */
136
+ function filterComplex(results, thresholds = exports.DEFAULT_THRESHOLDS) {
137
+ return results.filter(r => exceedsThresholds(r, thresholds));
138
+ }
139
+ exports.default = {
140
+ DEFAULT_THRESHOLDS: exports.DEFAULT_THRESHOLDS,
141
+ analyzeFile,
142
+ analyzeFiles,
143
+ exceedsThresholds,
144
+ getComplexityRating,
145
+ filterComplex,
146
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Analysis Module - Consolidated code analysis utilities
3
+ *
4
+ * Single source of truth for:
5
+ * - Security scanning
6
+ * - Complexity analysis
7
+ * - Pattern extraction
8
+ */
9
+ export * from './security';
10
+ export * from './complexity';
11
+ export * from './patterns';
12
+ export { default as security } from './security';
13
+ export { default as complexity } from './complexity';
14
+ export { default as patterns } from './patterns';
15
+ //# sourceMappingURL=index.d.ts.map