@soulcraft/brainy 2.1.0 โ†’ 3.0.0

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 (55) hide show
  1. package/dist/augmentations/AugmentationMetadataContract.d.ts +94 -0
  2. package/dist/augmentations/AugmentationMetadataContract.js +306 -0
  3. package/dist/augmentations/apiServerAugmentation.d.ts +1 -0
  4. package/dist/augmentations/apiServerAugmentation.js +1 -0
  5. package/dist/augmentations/batchProcessingAugmentation.d.ts +1 -0
  6. package/dist/augmentations/batchProcessingAugmentation.js +1 -0
  7. package/dist/augmentations/brainyAugmentation.d.ts +16 -0
  8. package/dist/augmentations/cacheAugmentation.d.ts +1 -0
  9. package/dist/augmentations/cacheAugmentation.js +1 -0
  10. package/dist/augmentations/conduitAugmentations.d.ts +1 -0
  11. package/dist/augmentations/conduitAugmentations.js +1 -0
  12. package/dist/augmentations/connectionPoolAugmentation.d.ts +1 -0
  13. package/dist/augmentations/connectionPoolAugmentation.js +1 -0
  14. package/dist/augmentations/entityRegistryAugmentation.d.ts +2 -0
  15. package/dist/augmentations/entityRegistryAugmentation.js +2 -0
  16. package/dist/augmentations/indexAugmentation.d.ts +1 -0
  17. package/dist/augmentations/indexAugmentation.js +1 -0
  18. package/dist/augmentations/intelligentVerbScoringAugmentation.d.ts +4 -0
  19. package/dist/augmentations/intelligentVerbScoringAugmentation.js +4 -0
  20. package/dist/augmentations/metadataEnforcer.d.ts +20 -0
  21. package/dist/augmentations/metadataEnforcer.js +171 -0
  22. package/dist/augmentations/metricsAugmentation.d.ts +2 -7
  23. package/dist/augmentations/metricsAugmentation.js +1 -0
  24. package/dist/augmentations/monitoringAugmentation.d.ts +1 -0
  25. package/dist/augmentations/monitoringAugmentation.js +1 -0
  26. package/dist/augmentations/neuralImport.d.ts +4 -0
  27. package/dist/augmentations/neuralImport.js +4 -0
  28. package/dist/augmentations/requestDeduplicatorAugmentation.d.ts +1 -0
  29. package/dist/augmentations/requestDeduplicatorAugmentation.js +1 -0
  30. package/dist/augmentations/serverSearchAugmentations.d.ts +2 -0
  31. package/dist/augmentations/serverSearchAugmentations.js +2 -0
  32. package/dist/augmentations/storageAugmentation.d.ts +1 -0
  33. package/dist/augmentations/storageAugmentation.js +1 -0
  34. package/dist/augmentations/synapseAugmentation.d.ts +4 -0
  35. package/dist/augmentations/synapseAugmentation.js +4 -0
  36. package/dist/augmentations/walAugmentation.d.ts +1 -0
  37. package/dist/augmentations/walAugmentation.js +1 -0
  38. package/dist/brainyData.d.ts +28 -1
  39. package/dist/brainyData.js +229 -83
  40. package/dist/embeddings/model-manager.d.ts +9 -8
  41. package/dist/embeddings/model-manager.js +105 -85
  42. package/dist/triple/TripleIntelligence.d.ts +4 -0
  43. package/dist/triple/TripleIntelligence.js +39 -9
  44. package/dist/utils/deletedItemsIndex.d.ts +59 -0
  45. package/dist/utils/deletedItemsIndex.js +98 -0
  46. package/dist/utils/ensureDeleted.d.ts +38 -0
  47. package/dist/utils/ensureDeleted.js +79 -0
  48. package/dist/utils/metadataFilter.js +5 -0
  49. package/dist/utils/metadataIndex.d.ts +4 -0
  50. package/dist/utils/metadataIndex.js +45 -0
  51. package/dist/utils/metadataNamespace.d.ts +113 -0
  52. package/dist/utils/metadataNamespace.js +162 -0
  53. package/dist/utils/periodicCleanup.d.ts +87 -0
  54. package/dist/utils/periodicCleanup.js +219 -0
  55. package/package.json +9 -3
@@ -8,6 +8,8 @@ import { HNSWIndexOptimized } from './hnsw/hnswIndexOptimized.js';
8
8
  import { cosineDistance, defaultEmbeddingFunction, cleanupWorkerPools, batchEmbed } from './utils/index.js';
9
9
  import { getAugmentationVersion } from './utils/version.js';
10
10
  import { matchesMetadataFilter } from './utils/metadataFilter.js';
11
+ import { createNamespacedMetadata, updateNamespacedMetadata, markDeleted, markRestored, isDeleted, getUserMetadata } from './utils/metadataNamespace.js';
12
+ import { PeriodicCleanup } from './utils/periodicCleanup.js';
11
13
  import { NounType, VerbType } from './types/graphTypes.js';
12
14
  import { createServerSearchAugmentations } from './augmentations/serverSearchAugmentations.js';
13
15
  import { augmentationPipeline } from './augmentationPipeline.js';
@@ -94,6 +96,8 @@ export class BrainyData {
94
96
  * Handles WAL, connection pooling, batching, streaming, and intelligent scoring
95
97
  */
96
98
  this.augmentations = new AugmentationRegistry();
99
+ // Periodic cleanup for soft-deleted items
100
+ this.periodicCleanup = null;
97
101
  // Timeout and retry configuration
98
102
  this.timeoutConfig = {};
99
103
  this.retryConfig = {};
@@ -397,6 +401,41 @@ export class BrainyData {
397
401
  if (this.loggingConfig?.verbose) {
398
402
  console.log('๐Ÿš€ New augmentation system initialized successfully');
399
403
  }
404
+ // Initialize periodic cleanup system
405
+ await this.initializePeriodicCleanup();
406
+ }
407
+ /**
408
+ * Initialize periodic cleanup system for old soft-deleted items
409
+ * SAFETY-CRITICAL: Coordinates with both HNSW and metadata indexes
410
+ */
411
+ async initializePeriodicCleanup() {
412
+ if (!this.storage) {
413
+ throw new Error('Cannot initialize periodic cleanup: storage not available');
414
+ }
415
+ // Skip cleanup if in read-only or frozen mode
416
+ if (this.readOnly || this.frozen) {
417
+ if (this.loggingConfig?.verbose) {
418
+ console.log('๐Ÿงน Periodic cleanup disabled: database is read-only or frozen');
419
+ }
420
+ return;
421
+ }
422
+ // Get cleanup config with safe defaults
423
+ const cleanupConfig = this.config.cleanup || {};
424
+ // Create cleanup system with all required dependencies
425
+ this.periodicCleanup = new PeriodicCleanup(this.storage, this.hnswIndex, this.metadataIndex, // Can be null, cleanup will handle gracefully
426
+ {
427
+ enabled: cleanupConfig.enabled !== false, // Enabled by default
428
+ maxAge: cleanupConfig.maxAge || 60 * 60 * 1000, // 1 hour default
429
+ batchSize: cleanupConfig.batchSize || 100, // 100 items per batch
430
+ cleanupInterval: cleanupConfig.cleanupInterval || 15 * 60 * 1000 // 15 minutes
431
+ });
432
+ // Start cleanup if enabled
433
+ if (this.periodicCleanup && cleanupConfig.enabled !== false) {
434
+ this.periodicCleanup.start();
435
+ if (this.loggingConfig?.verbose) {
436
+ console.log('๐Ÿงน Periodic cleanup system initialized and started');
437
+ }
438
+ }
400
439
  }
401
440
  checkReadOnly() {
402
441
  if (this.readOnly) {
@@ -1362,46 +1401,42 @@ export class BrainyData {
1362
1401
  // Always update updatedAt
1363
1402
  graphNoun.updatedAt = timestamp;
1364
1403
  }
1365
- // Create a copy of the metadata without modifying the original
1366
- let metadataToSave = metadata;
1367
- if (metadata && typeof metadata === 'object') {
1368
- // Always make a copy without adding the ID
1369
- metadataToSave = { ...metadata };
1370
- // Add domain metadata if distributed mode is enabled
1371
- if (this.domainDetector) {
1372
- // First check if domain is already in metadata
1373
- if (metadataToSave.domain) {
1374
- // Domain already specified, keep it
1375
- const domainInfo = this.domainDetector.detectDomain(metadataToSave);
1404
+ // Create properly namespaced metadata for new items
1405
+ let metadataToSave = createNamespacedMetadata(metadata);
1406
+ // Add domain metadata if distributed mode is enabled
1407
+ if (this.domainDetector) {
1408
+ // First check if domain is already in metadata
1409
+ if (metadataToSave.domain) {
1410
+ // Domain already specified, keep it
1411
+ const domainInfo = this.domainDetector.detectDomain(metadataToSave);
1412
+ if (domainInfo.domainMetadata) {
1413
+ ;
1414
+ metadataToSave.domainMetadata =
1415
+ domainInfo.domainMetadata;
1416
+ }
1417
+ }
1418
+ else {
1419
+ // Try to detect domain from the data
1420
+ const dataToAnalyze = Array.isArray(vectorOrData)
1421
+ ? metadata
1422
+ : vectorOrData;
1423
+ const domainInfo = this.domainDetector.detectDomain(dataToAnalyze);
1424
+ if (domainInfo.domain) {
1425
+ ;
1426
+ metadataToSave.domain = domainInfo.domain;
1376
1427
  if (domainInfo.domainMetadata) {
1377
1428
  ;
1378
1429
  metadataToSave.domainMetadata =
1379
1430
  domainInfo.domainMetadata;
1380
1431
  }
1381
1432
  }
1382
- else {
1383
- // Try to detect domain from the data
1384
- const dataToAnalyze = Array.isArray(vectorOrData)
1385
- ? metadata
1386
- : vectorOrData;
1387
- const domainInfo = this.domainDetector.detectDomain(dataToAnalyze);
1388
- if (domainInfo.domain) {
1389
- ;
1390
- metadataToSave.domain = domainInfo.domain;
1391
- if (domainInfo.domainMetadata) {
1392
- ;
1393
- metadataToSave.domainMetadata =
1394
- domainInfo.domainMetadata;
1395
- }
1396
- }
1397
- }
1398
- }
1399
- // Add partition information if distributed mode is enabled
1400
- if (this.partitioner) {
1401
- const partition = this.partitioner.getPartition(id);
1402
- metadataToSave.partition = partition;
1403
1433
  }
1404
1434
  }
1435
+ // Add partition information if distributed mode is enabled
1436
+ if (this.partitioner) {
1437
+ const partition = this.partitioner.getPartition(id);
1438
+ metadataToSave.partition = partition;
1439
+ }
1405
1440
  await this.storage.saveMetadata(id, metadataToSave);
1406
1441
  // Update metadata index (write-only mode should build indices!)
1407
1442
  if (this.index && !this.frozen) {
@@ -2077,10 +2112,11 @@ export class BrainyData {
2077
2112
  // This preserves pure vector searches without metadata
2078
2113
  if (metadataFilter && Object.keys(metadataFilter).length > 0) {
2079
2114
  // If no explicit deleted filter is provided, exclude soft-deleted items
2080
- if (!metadataFilter.deleted && !metadataFilter.anyOf) {
2115
+ // Use namespaced field for O(1) performance
2116
+ if (!metadataFilter['_brainy.deleted'] && !metadataFilter.anyOf) {
2081
2117
  metadataFilter = {
2082
2118
  ...metadataFilter,
2083
- deleted: { notEquals: true }
2119
+ ['_brainy.deleted']: false // O(1) positive match instead of notEquals
2084
2120
  };
2085
2121
  }
2086
2122
  }
@@ -2264,7 +2300,8 @@ export class BrainyData {
2264
2300
  if (result.metadata && typeof result.metadata === 'object') {
2265
2301
  const metadata = result.metadata;
2266
2302
  // Exclude deleted items from search results (soft delete)
2267
- if (metadata.deleted === true) {
2303
+ // Check namespaced field
2304
+ if (metadata._brainy?.deleted === true) {
2268
2305
  return false;
2269
2306
  }
2270
2307
  // Exclude placeholder nouns from search results
@@ -2877,9 +2914,9 @@ export class BrainyData {
2877
2914
  finalWeight = options.weight;
2878
2915
  }
2879
2916
  }
2880
- // Create complete verb metadata separately
2881
- // Merge original metadata with system metadata to preserve neural enhancements
2882
- const verbMetadata = {
2917
+ // Create complete verb metadata with proper namespace
2918
+ // First combine user metadata with verb-specific metadata
2919
+ const userAndVerbMetadata = {
2883
2920
  sourceId: sourceId,
2884
2921
  targetId: targetId,
2885
2922
  source: sourceId,
@@ -2899,6 +2936,8 @@ export class BrainyData {
2899
2936
  ...(options.metadata || {}),
2900
2937
  data: options.metadata // Also store in data field for backwards compatibility
2901
2938
  };
2939
+ // Now wrap with namespace for internal fields
2940
+ const verbMetadata = createNamespacedMetadata(userAndVerbMetadata);
2902
2941
  // Add to index
2903
2942
  await this.index.addItem({ id, vector: verbVector });
2904
2943
  // Get the noun from the index
@@ -2978,6 +3017,12 @@ export class BrainyData {
2978
3017
  const metadata = await this.storage.getVerbMetadata(id);
2979
3018
  if (!metadata) {
2980
3019
  console.warn(`Verb ${id} found but no metadata - creating minimal GraphVerb`);
3020
+ }
3021
+ else if (isDeleted(metadata)) {
3022
+ // Check if verb is soft-deleted
3023
+ return null;
3024
+ }
3025
+ if (!metadata) {
2981
3026
  // Return minimal GraphVerb if metadata is missing
2982
3027
  return {
2983
3028
  id: hnswVerb.id,
@@ -3226,50 +3271,65 @@ export class BrainyData {
3226
3271
  // Check if database is in read-only mode
3227
3272
  this.checkReadOnly();
3228
3273
  try {
3229
- // PERFORMANCE: Use soft delete by default for O(log n) filtering
3230
- // The MetadataIndex can efficiently filter out deleted items
3231
- if (!options.hard) {
3232
- // Soft delete: Just mark as deleted in metadata
3233
- try {
3234
- await this.storage.saveVerbMetadata(id, {
3235
- deleted: true,
3236
- deletedAt: new Date().toISOString(),
3237
- deletedBy: options.service || '2.0-api'
3238
- });
3239
- // Update MetadataIndex for O(log n) filtering
3240
- if (this.metadataIndex) {
3241
- await this.metadataIndex.updateIndex(id, { deleted: true });
3242
- }
3243
- return true;
3244
- }
3245
- catch (error) {
3246
- // If verb doesn't exist, return false (not an error)
3274
+ // CONSISTENT: Always use soft delete for graph integrity and recoverability
3275
+ // The MetadataIndex efficiently filters out deleted items with O(1) performance
3276
+ try {
3277
+ const existing = await this.storage.getVerb(id);
3278
+ if (!existing || !existing.metadata) {
3279
+ // Verb doesn't exist, return false (not an error)
3247
3280
  return false;
3248
3281
  }
3282
+ const updatedMetadata = markDeleted(existing.metadata);
3283
+ await this.storage.saveVerbMetadata(id, updatedMetadata);
3284
+ // Update MetadataIndex for O(1) filtering
3285
+ if (this.metadataIndex) {
3286
+ await this.metadataIndex.removeFromIndex(id, existing.metadata);
3287
+ await this.metadataIndex.addToIndex(id, updatedMetadata);
3288
+ }
3289
+ return true;
3249
3290
  }
3250
- // Hard delete path (explicit request only)
3251
- const existingMetadata = await this.storage.getVerbMetadata(id);
3252
- // Remove from index
3253
- const removed = this.index.removeItem(id);
3254
- if (!removed) {
3291
+ catch (error) {
3292
+ // If verb doesn't exist, return false (not an error)
3255
3293
  return false;
3256
3294
  }
3257
- // Remove from metadata index
3258
- if (this.index && existingMetadata) {
3259
- await this.metadataIndex?.removeFromIndex?.(id, existingMetadata);
3260
- }
3261
- // Remove from storage
3262
- await this.storage.deleteVerb(id);
3263
- // Track deletion statistics
3264
- const service = this.getServiceName(options);
3265
- await this.storage.decrementStatistic('verb', service);
3266
- return true;
3267
3295
  }
3268
3296
  catch (error) {
3269
3297
  console.error(`Failed to delete verb ${id}:`, error);
3270
3298
  throw new Error(`Failed to delete verb ${id}: ${error}`);
3271
3299
  }
3272
3300
  }
3301
+ /**
3302
+ * Restore a soft-deleted verb (complement to consistent soft delete)
3303
+ * @param id The verb ID to restore
3304
+ * @param options Options for the restore operation
3305
+ * @returns Promise<boolean> True if restored, false if not found or not deleted
3306
+ */
3307
+ async restoreVerb(id, options = {}) {
3308
+ await this.ensureInitialized();
3309
+ // Check if database is in read-only mode
3310
+ this.checkReadOnly();
3311
+ try {
3312
+ const existing = await this.storage.getVerb(id);
3313
+ if (!existing || !existing.metadata) {
3314
+ return false; // Verb doesn't exist
3315
+ }
3316
+ if (!isDeleted(existing.metadata)) {
3317
+ return false; // Verb not deleted, nothing to restore
3318
+ }
3319
+ const restoredMetadata = markRestored(existing.metadata);
3320
+ await this.storage.saveVerbMetadata(id, restoredMetadata);
3321
+ // Update MetadataIndex
3322
+ if (this.metadataIndex) {
3323
+ await this.metadataIndex.removeFromIndex(id, existing.metadata);
3324
+ await this.metadataIndex.addToIndex(id, restoredMetadata);
3325
+ }
3326
+ return true;
3327
+ }
3328
+ catch (error) {
3329
+ console.error(`Failed to restore verb ${id}:`, error);
3330
+ throw new Error(`Failed to restore verb ${id}: ${error}`);
3331
+ }
3332
+ }
3273
3333
  /**
3274
3334
  * Get the number of vectors in the database
3275
3335
  */
@@ -5474,8 +5534,8 @@ export class BrainyData {
5474
5534
  metadata = {};
5475
5535
  }
5476
5536
  else if (typeof metadata === 'object') {
5477
- // Check if this item is soft-deleted
5478
- if (metadata.deleted === true) {
5537
+ // Check if this item is soft-deleted using namespace
5538
+ if (isDeleted(metadata)) {
5479
5539
  // Return null for soft-deleted items to match expected API behavior
5480
5540
  return null;
5481
5541
  }
@@ -5527,17 +5587,34 @@ export class BrainyData {
5527
5587
  }
5528
5588
  }
5529
5589
  // For 2.0 API safety, we default to soft delete
5530
- // Soft delete: just mark as deleted - metadata filter will exclude from search
5590
+ // Soft delete: mark as deleted using namespace for O(1) filtering
5531
5591
  try {
5532
- await this.updateNounMetadata(actualId, {
5533
- deleted: true,
5534
- deletedAt: new Date().toISOString(),
5535
- deletedBy: '2.0-api'
5536
- });
5592
+ const existing = await this.getNoun(actualId);
5593
+ if (!existing) {
5594
+ // Item doesn't exist, return false (per API contract)
5595
+ return false;
5596
+ }
5597
+ if (existing.metadata) {
5598
+ // Directly save the metadata with deleted flag set
5599
+ const metadata = existing.metadata;
5600
+ const metadataWithNamespace = metadata._brainy
5601
+ ? metadata
5602
+ : createNamespacedMetadata(metadata);
5603
+ const updatedMetadata = markDeleted(metadataWithNamespace);
5604
+ // Save to storage
5605
+ await this.storage.saveMetadata(actualId, updatedMetadata);
5606
+ // CRITICAL: Update the metadata index for O(1) soft delete filtering
5607
+ if (this.metadataIndex) {
5608
+ // Remove old metadata from index
5609
+ await this.metadataIndex.removeFromIndex(actualId, metadataWithNamespace);
5610
+ // Add updated metadata with deleted flag
5611
+ await this.metadataIndex.addToIndex(actualId, updatedMetadata);
5612
+ }
5613
+ }
5537
5614
  return true;
5538
5615
  }
5539
5616
  catch (error) {
5540
- // If item doesn't exist, return false (delete of non-existent item is not an error)
5617
+ // If an actual error occurs, return false
5541
5618
  return false;
5542
5619
  }
5543
5620
  }
@@ -5546,6 +5623,62 @@ export class BrainyData {
5546
5623
  throw new Error(`Failed to delete vector ${id}: ${error}`);
5547
5624
  }
5548
5625
  }
5626
+ /**
5627
+ * Restore a soft-deleted noun (complement to consistent soft delete)
5628
+ * @param id The noun ID to restore
5629
+ * @returns Promise<boolean> True if restored, false if not found or not deleted
5630
+ */
5631
+ async restoreNoun(id) {
5632
+ // Validate id parameter first, before any other logic
5633
+ if (id === null || id === undefined) {
5634
+ throw new Error('ID cannot be null or undefined');
5635
+ }
5636
+ await this.ensureInitialized();
5637
+ // Check if database is in read-only mode
5638
+ this.checkReadOnly();
5639
+ try {
5640
+ // Handle content text vs ID resolution (same as deleteNoun)
5641
+ let actualId = id;
5642
+ if (!this.index.getNouns().has(id)) {
5643
+ // Try to find a noun with matching text content
5644
+ for (const [nounId, noun] of this.index.getNouns().entries()) {
5645
+ if (noun.metadata?.text === id) {
5646
+ actualId = nounId;
5647
+ break;
5648
+ }
5649
+ }
5650
+ }
5651
+ const existing = await this.getNoun(actualId);
5652
+ if (!existing) {
5653
+ return false; // Noun doesn't exist
5654
+ }
5655
+ if (!existing.metadata) {
5656
+ return false; // No metadata
5657
+ }
5658
+ // Ensure metadata has namespace structure before checking if deleted
5659
+ const metadata = existing.metadata;
5660
+ const metadataWithNamespace = metadata._brainy
5661
+ ? metadata
5662
+ : createNamespacedMetadata(getUserMetadata(metadata));
5663
+ if (!isDeleted(metadataWithNamespace)) {
5664
+ return false; // Noun not deleted, nothing to restore
5665
+ }
5666
+ // Restore the noun using the namespace-aware metadata
5667
+ const restoredMetadata = markRestored(metadataWithNamespace);
5668
+ // Save to storage
5669
+ await this.storage.saveMetadata(actualId, restoredMetadata);
5670
+ // Update the metadata index
5671
+ if (this.metadataIndex) {
5672
+ await this.metadataIndex.removeFromIndex(actualId, metadataWithNamespace);
5673
+ await this.metadataIndex.addToIndex(actualId, restoredMetadata);
5674
+ }
5675
+ return true;
5676
+ }
5677
+ catch (error) {
5678
+ console.error(`Failed to restore noun ${id}:`, error);
5679
+ throw new Error(`Failed to restore noun ${id}: ${error}`);
5680
+ }
5681
+ }
5549
5682
  /**
5550
5683
  * Delete multiple nouns by IDs
5551
5684
  * @param ids Array of noun IDs
@@ -5611,6 +5744,8 @@ export class BrainyData {
5611
5744
  else if (!metadata && existingMetadata) {
5612
5745
  finalMetadata = existingMetadata;
5613
5746
  }
5747
+ // Update metadata while preserving namespaces
5748
+ finalMetadata = updateNamespacedMetadata(existingMetadata || {}, finalMetadata);
5614
5749
  // Update the noun with new data and vector
5615
5750
  const updatedNoun = {
5616
5751
  ...existingNoun,
@@ -5668,8 +5803,17 @@ export class BrainyData {
5668
5803
  if (!noun) {
5669
5804
  throw new Error(`Vector with ID ${id} does not exist`);
5670
5805
  }
5806
+ // Get existing metadata to preserve namespaces
5807
+ const existing = await this.storage.getMetadata(id) || {};
5808
+ // Update metadata while preserving namespace structure
5809
+ const metadataToSave = updateNamespacedMetadata(existing, metadata);
5671
5810
  // Save updated metadata to storage
5672
- await this.storage.saveMetadata(id, metadata);
5811
+ await this.storage.saveMetadata(id, metadataToSave);
5812
+ // Update metadata index for efficient filtering
5813
+ if (this.metadataIndex) {
5814
+ await this.metadataIndex.removeFromIndex(id, existing);
5815
+ await this.metadataIndex.addToIndex(id, metadataToSave);
5816
+ }
5673
5817
  // Invalidate search cache since metadata has changed
5674
5818
  this.cache?.invalidateOnDataChange('update');
5675
5819
  }
@@ -5803,12 +5947,14 @@ export class BrainyData {
5803
5947
  else if (offset > 0) {
5804
5948
  processedQuery.offset = offset;
5805
5949
  }
5806
- // Apply soft-delete filtering if needed
5950
+ // Add soft-delete filter using POSITIVE match (O(1) hash lookup)
5951
+ // We use _brainy.deleted to avoid conflicts with user metadata
5807
5952
  if (excludeDeleted) {
5808
5953
  if (!processedQuery.where) {
5809
5954
  processedQuery.where = {};
5810
5955
  }
5811
- processedQuery.where.deleted = { notEquals: true };
5956
+ // Use namespaced field for O(1) hash lookup in metadata index
5957
+ processedQuery.where['_brainy.deleted'] = false; // or { equals: false }
5812
5958
  }
5813
5959
  // Apply mode-specific optimizations
5814
5960
  if (mode !== 'auto') {
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Model Manager - Ensures transformer models are available at runtime
3
3
  *
4
- * Strategy:
5
- * 1. Check local cache first
6
- * 2. Try GitHub releases (our backup)
7
- * 3. Fall back to Hugging Face
8
- * 4. Future: CDN at models.soulcraft.com
4
+ * Strategy (in order):
5
+ * 1. Check local cache first (instant)
6
+ * 2. Try Soulcraft CDN (fastest when available)
7
+ * 3. Try GitHub release tar.gz with extraction (reliable backup)
8
+ * 4. Fall back to Hugging Face (always works)
9
+ *
10
+ * NO USER CONFIGURATION REQUIRED - Everything is automatic!
9
11
  */
10
12
  export declare class ModelManager {
11
13
  private static instance;
@@ -16,9 +18,8 @@ export declare class ModelManager {
16
18
  private getModelsPath;
17
19
  ensureModels(modelName?: string): Promise<boolean>;
18
20
  private verifyModelFiles;
19
- private downloadFromGitHub;
20
- private downloadFromCDN;
21
- private configureTransformers;
21
+ private tryModelSource;
22
+ private downloadAndExtractFromGitHub;
22
23
  /**
23
24
  * Pre-download models for deployment
24
25
  * This is what npm run download-models calls