@soulcraft/brainy 2.0.2 → 2.3.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 (60) hide show
  1. package/README.md +2 -2
  2. package/dist/augmentations/AugmentationMetadataContract.d.ts +94 -0
  3. package/dist/augmentations/AugmentationMetadataContract.js +306 -0
  4. package/dist/augmentations/apiServerAugmentation.d.ts +1 -0
  5. package/dist/augmentations/apiServerAugmentation.js +1 -0
  6. package/dist/augmentations/batchProcessingAugmentation.d.ts +1 -0
  7. package/dist/augmentations/batchProcessingAugmentation.js +1 -0
  8. package/dist/augmentations/brainyAugmentation.d.ts +16 -0
  9. package/dist/augmentations/cacheAugmentation.d.ts +1 -0
  10. package/dist/augmentations/cacheAugmentation.js +1 -0
  11. package/dist/augmentations/conduitAugmentations.d.ts +1 -0
  12. package/dist/augmentations/conduitAugmentations.js +1 -0
  13. package/dist/augmentations/connectionPoolAugmentation.d.ts +1 -0
  14. package/dist/augmentations/connectionPoolAugmentation.js +1 -0
  15. package/dist/augmentations/entityRegistryAugmentation.d.ts +2 -0
  16. package/dist/augmentations/entityRegistryAugmentation.js +2 -0
  17. package/dist/augmentations/indexAugmentation.d.ts +1 -0
  18. package/dist/augmentations/indexAugmentation.js +1 -0
  19. package/dist/augmentations/intelligentVerbScoringAugmentation.d.ts +4 -0
  20. package/dist/augmentations/intelligentVerbScoringAugmentation.js +4 -0
  21. package/dist/augmentations/metadataEnforcer.d.ts +20 -0
  22. package/dist/augmentations/metadataEnforcer.js +171 -0
  23. package/dist/augmentations/metricsAugmentation.d.ts +2 -7
  24. package/dist/augmentations/metricsAugmentation.js +1 -0
  25. package/dist/augmentations/monitoringAugmentation.d.ts +1 -0
  26. package/dist/augmentations/monitoringAugmentation.js +1 -0
  27. package/dist/augmentations/neuralImport.d.ts +16 -3
  28. package/dist/augmentations/neuralImport.js +199 -55
  29. package/dist/augmentations/requestDeduplicatorAugmentation.d.ts +1 -0
  30. package/dist/augmentations/requestDeduplicatorAugmentation.js +1 -0
  31. package/dist/augmentations/serverSearchAugmentations.d.ts +2 -0
  32. package/dist/augmentations/serverSearchAugmentations.js +2 -0
  33. package/dist/augmentations/storageAugmentation.d.ts +1 -0
  34. package/dist/augmentations/storageAugmentation.js +1 -0
  35. package/dist/augmentations/synapseAugmentation.d.ts +4 -0
  36. package/dist/augmentations/synapseAugmentation.js +4 -0
  37. package/dist/augmentations/typeMatching/intelligentTypeMatcher.d.ts +83 -0
  38. package/dist/augmentations/typeMatching/intelligentTypeMatcher.js +425 -0
  39. package/dist/augmentations/walAugmentation.d.ts +1 -0
  40. package/dist/augmentations/walAugmentation.js +1 -0
  41. package/dist/brainyData.d.ts +32 -5
  42. package/dist/brainyData.js +263 -111
  43. package/dist/importManager.d.ts +78 -0
  44. package/dist/importManager.js +258 -0
  45. package/dist/neural/embeddedPatterns.d.ts +1 -1
  46. package/dist/neural/embeddedPatterns.js +1 -1
  47. package/dist/triple/TripleIntelligence.d.ts +4 -0
  48. package/dist/triple/TripleIntelligence.js +39 -9
  49. package/dist/utils/deletedItemsIndex.d.ts +59 -0
  50. package/dist/utils/deletedItemsIndex.js +98 -0
  51. package/dist/utils/ensureDeleted.d.ts +38 -0
  52. package/dist/utils/ensureDeleted.js +79 -0
  53. package/dist/utils/metadataFilter.js +5 -0
  54. package/dist/utils/metadataIndex.d.ts +4 -0
  55. package/dist/utils/metadataIndex.js +45 -0
  56. package/dist/utils/metadataNamespace.d.ts +113 -0
  57. package/dist/utils/metadataNamespace.js +162 -0
  58. package/dist/utils/periodicCleanup.d.ts +87 -0
  59. package/dist/utils/periodicCleanup.js +219 -0
  60. package/package.json +13 -5
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Import Manager - Comprehensive data import with intelligent type detection
3
+ *
4
+ * Handles multiple data sources:
5
+ * - Direct data (objects, arrays)
6
+ * - Files (JSON, CSV, text)
7
+ * - URLs (fetch and parse)
8
+ * - Streams (for large files)
9
+ *
10
+ * Uses NeuralImportAugmentation for intelligent processing
11
+ */
12
+ import { VerbType } from './types/graphTypes.js';
13
+ import { NeuralImportAugmentation } from './augmentations/neuralImport.js';
14
+ import * as fs from './universal/fs.js';
15
+ import * as path from './universal/path.js';
16
+ import { prodLog } from './utils/logger.js';
17
+ export class ImportManager {
18
+ constructor(brain) {
19
+ this.typeMatcher = null;
20
+ this.brain = brain;
21
+ this.neuralImport = new NeuralImportAugmentation();
22
+ }
23
+ /**
24
+ * Initialize the import manager
25
+ */
26
+ async init() {
27
+ // Initialize neural import with proper context
28
+ const context = {
29
+ brain: this.brain,
30
+ storage: this.brain.storage,
31
+ config: {},
32
+ log: (message, level) => {
33
+ if (level === 'error') {
34
+ prodLog.error(message);
35
+ }
36
+ else if (level === 'warn') {
37
+ prodLog.warn(message);
38
+ }
39
+ else {
40
+ prodLog.info(message);
41
+ }
42
+ }
43
+ };
44
+ await this.neuralImport.initialize(context);
45
+ // Get type matcher
46
+ const { getTypeMatcher } = await import('./augmentations/typeMatching/intelligentTypeMatcher.js');
47
+ this.typeMatcher = await getTypeMatcher();
48
+ }
49
+ /**
50
+ * Main import method - handles all sources
51
+ */
52
+ async import(source, options = {}) {
53
+ const result = {
54
+ success: false,
55
+ nouns: [],
56
+ verbs: [],
57
+ errors: [],
58
+ stats: {
59
+ total: 0,
60
+ imported: 0,
61
+ failed: 0,
62
+ relationships: 0
63
+ }
64
+ };
65
+ try {
66
+ // Detect source type
67
+ const sourceType = await this.detectSourceType(source, options.source);
68
+ // Get data based on source type
69
+ let data;
70
+ let format = options.format || 'auto';
71
+ switch (sourceType) {
72
+ case 'url':
73
+ data = await this.fetchFromUrl(source);
74
+ break;
75
+ case 'file':
76
+ const filePath = source;
77
+ data = await this.readFile(filePath);
78
+ if (format === 'auto') {
79
+ format = this.detectFormatFromPath(filePath);
80
+ }
81
+ break;
82
+ case 'data':
83
+ default:
84
+ data = source;
85
+ break;
86
+ }
87
+ // Process data through neural import
88
+ let items;
89
+ let relationships = [];
90
+ if (Buffer.isBuffer(data) || typeof data === 'string') {
91
+ // Use neural import for parsing and analysis
92
+ const analysis = await this.neuralImport.getNeuralAnalysis(data, format);
93
+ // Extract items and relationships
94
+ items = analysis.detectedEntities.map(entity => ({
95
+ data: entity.originalData,
96
+ type: entity.nounType,
97
+ confidence: entity.confidence,
98
+ id: entity.suggestedId
99
+ }));
100
+ if (options.extractRelationships !== false) {
101
+ relationships = analysis.detectedRelationships;
102
+ }
103
+ // Log insights
104
+ for (const insight of analysis.insights) {
105
+ prodLog.info(`🧠 ${insight.description} (confidence: ${insight.confidence})`);
106
+ }
107
+ }
108
+ else if (Array.isArray(data)) {
109
+ items = data;
110
+ }
111
+ else {
112
+ items = [data];
113
+ }
114
+ result.stats.total = items.length;
115
+ // Import items in batches
116
+ const batchSize = options.batchSize || 50;
117
+ for (let i = 0; i < items.length; i += batchSize) {
118
+ const batch = items.slice(i, i + batchSize);
119
+ // Process batch in parallel if enabled
120
+ const promises = batch.map(async (item) => {
121
+ try {
122
+ // Detect type if needed
123
+ let nounType = item.type || options.typeHint;
124
+ if (!nounType && options.autoDetect !== false && this.typeMatcher) {
125
+ const match = await this.typeMatcher.matchNounType(item.data || item);
126
+ nounType = match.type;
127
+ }
128
+ // Prepare the data to import
129
+ const dataToImport = item.data || item;
130
+ // Create metadata combining original data with import metadata
131
+ const metadata = {
132
+ ...(typeof dataToImport === 'object' ? dataToImport : {}),
133
+ ...(item.data?.metadata || {}),
134
+ nounType,
135
+ _importedAt: new Date().toISOString(),
136
+ _confidence: item.confidence
137
+ };
138
+ // Add to brain - pass object once, it becomes both vector source and metadata
139
+ const id = await this.brain.addNoun(metadata);
140
+ result.nouns.push(id);
141
+ result.stats.imported++;
142
+ return id;
143
+ }
144
+ catch (error) {
145
+ result.errors.push(`Failed to import item: ${error.message}`);
146
+ result.stats.failed++;
147
+ return null;
148
+ }
149
+ });
150
+ if (options.parallel !== false) {
151
+ await Promise.all(promises);
152
+ }
153
+ else {
154
+ for (const promise of promises) {
155
+ await promise;
156
+ }
157
+ }
158
+ }
159
+ // Import relationships
160
+ for (const rel of relationships) {
161
+ try {
162
+ // Match verb type if needed
163
+ let verbType = rel.verbType;
164
+ if (!Object.values(VerbType).includes(verbType) && this.typeMatcher) {
165
+ const match = await this.typeMatcher.matchVerbType({ id: rel.sourceId }, { id: rel.targetId }, rel.verbType);
166
+ verbType = match.type;
167
+ }
168
+ const verbId = await this.brain.addVerb(rel.sourceId, rel.targetId, verbType, rel.metadata, rel.weight);
169
+ result.verbs.push(verbId);
170
+ result.stats.relationships++;
171
+ }
172
+ catch (error) {
173
+ result.errors.push(`Failed to create relationship: ${error.message}`);
174
+ }
175
+ }
176
+ result.success = result.stats.imported > 0;
177
+ prodLog.info(`✨ Import complete: ${result.stats.imported}/${result.stats.total} items, ${result.stats.relationships} relationships`);
178
+ }
179
+ catch (error) {
180
+ result.errors.push(`Import failed: ${error.message}`);
181
+ prodLog.error('Import failed:', error);
182
+ }
183
+ return result;
184
+ }
185
+ /**
186
+ * Import from file
187
+ */
188
+ async importFile(filePath, options = {}) {
189
+ return this.import(filePath, { ...options, source: 'file' });
190
+ }
191
+ /**
192
+ * Import from URL
193
+ */
194
+ async importUrl(url, options = {}) {
195
+ return this.import(url, { ...options, source: 'url' });
196
+ }
197
+ /**
198
+ * Detect source type
199
+ */
200
+ async detectSourceType(source, hint) {
201
+ if (hint && hint !== 'auto') {
202
+ return hint;
203
+ }
204
+ if (typeof source === 'string') {
205
+ // Check if URL
206
+ if (source.startsWith('http://') || source.startsWith('https://')) {
207
+ return 'url';
208
+ }
209
+ // Check if file path exists
210
+ try {
211
+ if (await fs.exists(source)) {
212
+ return 'file';
213
+ }
214
+ }
215
+ catch { }
216
+ }
217
+ return 'data';
218
+ }
219
+ /**
220
+ * Detect format from file path
221
+ */
222
+ detectFormatFromPath(filePath) {
223
+ const ext = path.extname(filePath).toLowerCase();
224
+ switch (ext) {
225
+ case '.json': return 'json';
226
+ case '.csv': return 'csv';
227
+ case '.txt': return 'text';
228
+ case '.md': return 'text';
229
+ case '.yaml':
230
+ case '.yml': return 'yaml';
231
+ default: return 'auto';
232
+ }
233
+ }
234
+ /**
235
+ * Read file
236
+ */
237
+ async readFile(filePath) {
238
+ const content = await fs.readFile(filePath, 'utf8');
239
+ return Buffer.from(content, 'utf8');
240
+ }
241
+ /**
242
+ * Fetch from URL
243
+ */
244
+ async fetchFromUrl(url) {
245
+ const response = await fetch(url);
246
+ if (!response.ok) {
247
+ throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
248
+ }
249
+ return response.text();
250
+ }
251
+ }
252
+ /**
253
+ * Create an import manager instance
254
+ */
255
+ export function createImportManager(brain) {
256
+ return new ImportManager(brain);
257
+ }
258
+ //# sourceMappingURL=importManager.js.map
@@ -2,7 +2,7 @@
2
2
  * 🧠 BRAINY EMBEDDED PATTERNS
3
3
  *
4
4
  * AUTO-GENERATED - DO NOT EDIT
5
- * Generated: 2025-08-26T19:59:35.203Z
5
+ * Generated: 2025-08-27T16:58:35.801Z
6
6
  * Patterns: 220
7
7
  * Coverage: 94-98% of all queries
8
8
  *
@@ -2,7 +2,7 @@
2
2
  * 🧠 BRAINY EMBEDDED PATTERNS
3
3
  *
4
4
  * AUTO-GENERATED - DO NOT EDIT
5
- * Generated: 2025-08-26T19:59:35.203Z
5
+ * Generated: 2025-08-27T16:58:35.801Z
6
6
  * Patterns: 220
7
7
  * Coverage: 94-98% of all queries
8
8
  *
@@ -83,6 +83,10 @@ export declare class TripleIntelligenceEngine {
83
83
  * Field-based filtering
84
84
  */
85
85
  private fieldFilter;
86
+ /**
87
+ * Fallback manual metadata filtering when index is not available
88
+ */
89
+ private manualMetadataFilter;
86
90
  /**
87
91
  * Fusion ranking combines all signals
88
92
  */
@@ -171,25 +171,30 @@ export class TripleIntelligenceEngine {
171
171
  }
172
172
  break;
173
173
  case 'vector':
174
- if (candidates.length === 0) {
175
- // Initial vector search
174
+ // CRITICAL: If we have a previous step that returned 0 candidates,
175
+ // we must respect that and not do a fresh search
176
+ if (candidates.length === 0 && plan.steps[0].type === 'vector') {
177
+ // This is the first step - do initial vector search
176
178
  const results = await this.vectorSearch(query.like || query.similar, query.limit);
177
179
  candidates = results;
178
180
  }
179
- else {
180
- // Vector search within candidates
181
+ else if (candidates.length > 0) {
182
+ // Vector search within existing candidates
181
183
  candidates = await this.vectorSearchWithin(query.like || query.similar, candidates);
182
184
  }
185
+ // If candidates.length === 0 and this isn't the first step, keep empty candidates
183
186
  break;
184
187
  case 'graph':
185
- if (candidates.length === 0) {
186
- // Initial graph traversal
188
+ // CRITICAL: Same logic as vector - respect empty candidates from previous steps
189
+ if (candidates.length === 0 && plan.steps[0].type === 'graph') {
190
+ // This is the first step - do initial graph traversal
187
191
  candidates = await this.graphTraversal(query.connected);
188
192
  }
189
- else {
190
- // Graph expansion from candidates
193
+ else if (candidates.length > 0) {
194
+ // Graph expansion from existing candidates
191
195
  candidates = await this.graphExpand(candidates, query.connected);
192
196
  }
197
+ // If candidates.length === 0 and this isn't the first step, keep empty candidates
193
198
  break;
194
199
  case 'fusion':
195
200
  // Final fusion ranking
@@ -248,7 +253,13 @@ export class TripleIntelligenceEngine {
248
253
  // Use the MetadataIndex directly for FAST field queries!
249
254
  // This uses B-tree indexes for O(log n) range queries
250
255
  // and hash indexes for O(1) exact matches
251
- const matchingIds = await this.brain.metadataIndex?.getIdsForFilter(where) || [];
256
+ const metadataIndex = this.brain.metadataIndex;
257
+ // Check if metadata index is properly initialized
258
+ if (!metadataIndex || typeof metadataIndex.getIdsForFilter !== 'function') {
259
+ // Fallback to manual filtering - slower but works
260
+ return this.manualMetadataFilter(where);
261
+ }
262
+ const matchingIds = await metadataIndex.getIdsForFilter(where) || [];
252
263
  // Convert to result format with metadata
253
264
  const results = [];
254
265
  for (const id of matchingIds.slice(0, 1000)) {
@@ -263,6 +274,25 @@ export class TripleIntelligenceEngine {
263
274
  }
264
275
  return results;
265
276
  }
277
+ /**
278
+ * Fallback manual metadata filtering when index is not available
279
+ */
280
+ async manualMetadataFilter(where) {
281
+ const { matchesMetadataFilter } = await import('../utils/metadataFilter.js');
282
+ const results = [];
283
+ // Get all nouns and manually filter them
284
+ const allNouns = this.brain.index.getNouns();
285
+ for (const [id, noun] of Array.from(allNouns.entries()).slice(0, 1000)) {
286
+ if (noun && matchesMetadataFilter(noun.metadata || {}, where)) {
287
+ results.push({
288
+ id,
289
+ score: 1.0,
290
+ metadata: noun.metadata || {}
291
+ });
292
+ }
293
+ }
294
+ return results;
295
+ }
266
296
  /**
267
297
  * Fusion ranking combines all signals
268
298
  */
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Dedicated index for tracking soft-deleted items
3
+ * This is MUCH more efficient than checking every item in the database
4
+ *
5
+ * Performance characteristics:
6
+ * - Add deleted item: O(1)
7
+ * - Remove deleted item: O(1)
8
+ * - Check if deleted: O(1)
9
+ * - Get all deleted: O(d) where d = number of deleted items << total items
10
+ */
11
+ export declare class DeletedItemsIndex {
12
+ private deletedIds;
13
+ private deletedCount;
14
+ /**
15
+ * Mark an item as deleted
16
+ */
17
+ markDeleted(id: string): void;
18
+ /**
19
+ * Mark an item as not deleted (restored)
20
+ */
21
+ markRestored(id: string): void;
22
+ /**
23
+ * Check if an item is deleted - O(1)
24
+ */
25
+ isDeleted(id: string): boolean;
26
+ /**
27
+ * Get all deleted item IDs - O(d)
28
+ */
29
+ getAllDeleted(): string[];
30
+ /**
31
+ * Filter out deleted items from results - O(k) where k = result count
32
+ */
33
+ filterDeleted<T extends {
34
+ id?: string;
35
+ }>(items: T[]): T[];
36
+ /**
37
+ * Get statistics
38
+ */
39
+ getStats(): {
40
+ deletedCount: number;
41
+ memoryUsage: number;
42
+ };
43
+ /**
44
+ * Clear all deleted items (for testing)
45
+ */
46
+ clear(): void;
47
+ /**
48
+ * Serialize for persistence
49
+ */
50
+ serialize(): string;
51
+ /**
52
+ * Deserialize from persistence
53
+ */
54
+ deserialize(data: string): void;
55
+ }
56
+ /**
57
+ * Global singleton for deleted items tracking
58
+ */
59
+ export declare const deletedItemsIndex: DeletedItemsIndex;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Dedicated index for tracking soft-deleted items
3
+ * This is MUCH more efficient than checking every item in the database
4
+ *
5
+ * Performance characteristics:
6
+ * - Add deleted item: O(1)
7
+ * - Remove deleted item: O(1)
8
+ * - Check if deleted: O(1)
9
+ * - Get all deleted: O(d) where d = number of deleted items << total items
10
+ */
11
+ export class DeletedItemsIndex {
12
+ constructor() {
13
+ this.deletedIds = new Set();
14
+ this.deletedCount = 0;
15
+ }
16
+ /**
17
+ * Mark an item as deleted
18
+ */
19
+ markDeleted(id) {
20
+ if (!this.deletedIds.has(id)) {
21
+ this.deletedIds.add(id);
22
+ this.deletedCount++;
23
+ }
24
+ }
25
+ /**
26
+ * Mark an item as not deleted (restored)
27
+ */
28
+ markRestored(id) {
29
+ if (this.deletedIds.delete(id)) {
30
+ this.deletedCount--;
31
+ }
32
+ }
33
+ /**
34
+ * Check if an item is deleted - O(1)
35
+ */
36
+ isDeleted(id) {
37
+ return this.deletedIds.has(id);
38
+ }
39
+ /**
40
+ * Get all deleted item IDs - O(d)
41
+ */
42
+ getAllDeleted() {
43
+ return Array.from(this.deletedIds);
44
+ }
45
+ /**
46
+ * Filter out deleted items from results - O(k) where k = result count
47
+ */
48
+ filterDeleted(items) {
49
+ if (this.deletedCount === 0) {
50
+ // Fast path - no deleted items
51
+ return items;
52
+ }
53
+ return items.filter(item => {
54
+ const id = item.id;
55
+ return id ? !this.deletedIds.has(id) : true;
56
+ });
57
+ }
58
+ /**
59
+ * Get statistics
60
+ */
61
+ getStats() {
62
+ return {
63
+ deletedCount: this.deletedCount,
64
+ memoryUsage: this.deletedCount * 100 // Rough estimate: 100 bytes per ID
65
+ };
66
+ }
67
+ /**
68
+ * Clear all deleted items (for testing)
69
+ */
70
+ clear() {
71
+ this.deletedIds.clear();
72
+ this.deletedCount = 0;
73
+ }
74
+ /**
75
+ * Serialize for persistence
76
+ */
77
+ serialize() {
78
+ return JSON.stringify(Array.from(this.deletedIds));
79
+ }
80
+ /**
81
+ * Deserialize from persistence
82
+ */
83
+ deserialize(data) {
84
+ try {
85
+ const ids = JSON.parse(data);
86
+ this.deletedIds = new Set(ids);
87
+ this.deletedCount = this.deletedIds.size;
88
+ }
89
+ catch (e) {
90
+ console.warn('Failed to deserialize deleted items index');
91
+ }
92
+ }
93
+ }
94
+ /**
95
+ * Global singleton for deleted items tracking
96
+ */
97
+ export const deletedItemsIndex = new DeletedItemsIndex();
98
+ //# sourceMappingURL=deletedItemsIndex.js.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Utility to ensure all metadata has the deleted field set properly
3
+ * This is CRITICAL for O(1) soft delete filtering performance
4
+ *
5
+ * Uses _brainy namespace to avoid conflicts with user metadata
6
+ */
7
+ /**
8
+ * Ensure metadata has internal Brainy fields set
9
+ * @param metadata The metadata object (could be null/undefined)
10
+ * @param preserveExisting If true, preserve existing deleted value
11
+ * @returns Metadata with internal fields guaranteed
12
+ */
13
+ export declare function ensureDeletedField(metadata: any, preserveExisting?: boolean): any;
14
+ /**
15
+ * Mark an item as soft deleted
16
+ * @param metadata The metadata object
17
+ * @returns Metadata with _brainy.deleted=true
18
+ */
19
+ export declare function markAsDeleted(metadata: any): any;
20
+ /**
21
+ * Mark an item as restored (not deleted)
22
+ * @param metadata The metadata object
23
+ * @returns Metadata with _brainy.deleted=false
24
+ */
25
+ export declare function markAsRestored(metadata: any): any;
26
+ /**
27
+ * Check if an item is deleted
28
+ * @param metadata The metadata object
29
+ * @returns true if deleted, false otherwise (including if field missing)
30
+ */
31
+ export declare function isDeleted(metadata: any): boolean;
32
+ /**
33
+ * Check if an item is active (not deleted)
34
+ * @param metadata The metadata object
35
+ * @returns true if not deleted (default), false if deleted
36
+ */
37
+ export declare function isActive(metadata: any): boolean;
38
+ export declare const BRAINY_DELETED_FIELD = "_brainy.deleted";
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Utility to ensure all metadata has the deleted field set properly
3
+ * This is CRITICAL for O(1) soft delete filtering performance
4
+ *
5
+ * Uses _brainy namespace to avoid conflicts with user metadata
6
+ */
7
+ const BRAINY_NAMESPACE = '_brainy';
8
+ /**
9
+ * Ensure metadata has internal Brainy fields set
10
+ * @param metadata The metadata object (could be null/undefined)
11
+ * @param preserveExisting If true, preserve existing deleted value
12
+ * @returns Metadata with internal fields guaranteed
13
+ */
14
+ export function ensureDeletedField(metadata, preserveExisting = true) {
15
+ // Handle null/undefined metadata
16
+ if (!metadata) {
17
+ return {
18
+ [BRAINY_NAMESPACE]: {
19
+ deleted: false,
20
+ version: 1
21
+ }
22
+ };
23
+ }
24
+ // Clone to avoid mutation
25
+ const result = { ...metadata };
26
+ // Ensure _brainy namespace exists
27
+ if (!result[BRAINY_NAMESPACE]) {
28
+ result[BRAINY_NAMESPACE] = {};
29
+ }
30
+ // Set deleted field if not present
31
+ if (!('deleted' in result[BRAINY_NAMESPACE])) {
32
+ result[BRAINY_NAMESPACE].deleted = false;
33
+ }
34
+ else if (!preserveExisting) {
35
+ // Force to false if not preserving
36
+ result[BRAINY_NAMESPACE].deleted = false;
37
+ }
38
+ return result;
39
+ }
40
+ /**
41
+ * Mark an item as soft deleted
42
+ * @param metadata The metadata object
43
+ * @returns Metadata with _brainy.deleted=true
44
+ */
45
+ export function markAsDeleted(metadata) {
46
+ const result = ensureDeletedField(metadata);
47
+ result[BRAINY_NAMESPACE].deleted = true;
48
+ return result;
49
+ }
50
+ /**
51
+ * Mark an item as restored (not deleted)
52
+ * @param metadata The metadata object
53
+ * @returns Metadata with _brainy.deleted=false
54
+ */
55
+ export function markAsRestored(metadata) {
56
+ const result = ensureDeletedField(metadata);
57
+ result[BRAINY_NAMESPACE].deleted = false;
58
+ return result;
59
+ }
60
+ /**
61
+ * Check if an item is deleted
62
+ * @param metadata The metadata object
63
+ * @returns true if deleted, false otherwise (including if field missing)
64
+ */
65
+ export function isDeleted(metadata) {
66
+ return metadata?.[BRAINY_NAMESPACE]?.deleted === true;
67
+ }
68
+ /**
69
+ * Check if an item is active (not deleted)
70
+ * @param metadata The metadata object
71
+ * @returns true if not deleted (default), false if deleted
72
+ */
73
+ export function isActive(metadata) {
74
+ // If no deleted field or deleted=false, item is active
75
+ return !isDeleted(metadata);
76
+ }
77
+ // Export the namespace constant for use in queries
78
+ export const BRAINY_DELETED_FIELD = `${BRAINY_NAMESPACE}.deleted`;
79
+ //# sourceMappingURL=ensureDeleted.js.map
@@ -24,8 +24,13 @@ function matchesQuery(value, query) {
24
24
  case 'notEquals':
25
25
  case 'isNot':
26
26
  case 'ne':
27
+ // Special handling: if value is undefined and operand is not undefined,
28
+ // they are not equal (so the condition passes)
29
+ // This ensures items without a 'deleted' field match 'deleted !== true'
27
30
  if (value === operand)
28
31
  return false;
32
+ // If value is undefined and operand is not, they're not equal (pass)
33
+ // If both are undefined, they're equal (fail, handled above)
29
34
  break;
30
35
  // Comparison operators
31
36
  case 'greaterThan':
@@ -107,6 +107,10 @@ export declare class MetadataIndexManager {
107
107
  * Remove item from metadata indexes
108
108
  */
109
109
  removeFromIndex(id: string, metadata?: any): Promise<void>;
110
+ /**
111
+ * Get all IDs in the index
112
+ */
113
+ getAllIds(): Promise<string[]>;
110
114
  /**
111
115
  * Get IDs for a specific field-value combination with caching
112
116
  */