cozo-memory 1.1.4 โ†’ 1.1.6

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.
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const cozo_node_1 = require("cozo-node");
37
+ const embedding_service_1 = require("./embedding-service");
38
+ const proactive_suggestions_1 = require("./proactive-suggestions");
39
+ const fs = __importStar(require("fs"));
40
+ const DB_PATH = 'test_proactive.db';
41
+ async function runTests() {
42
+ console.error('[Test] Starting Proactive Suggestions Tests...\n');
43
+ // Clean up old database
44
+ if (fs.existsSync(DB_PATH)) {
45
+ try {
46
+ fs.unlinkSync(DB_PATH);
47
+ }
48
+ catch (e) { }
49
+ }
50
+ const db = new cozo_node_1.CozoDb('sqlite', DB_PATH);
51
+ const embeddings = new embedding_service_1.EmbeddingService();
52
+ // Create a wrapper to make ProactiveSuggestionsService work with CozoDb
53
+ const dbWrapper = {
54
+ getEntity: async (id) => {
55
+ const res = await db.run('?[name, type, embedding] := *entity{id: $id, name, type, embedding, @ "NOW"}', { id });
56
+ if (res.rows.length === 0)
57
+ return null;
58
+ return {
59
+ id,
60
+ name: res.rows[0][0],
61
+ type: res.rows[0][1],
62
+ embedding: res.rows[0][2],
63
+ };
64
+ },
65
+ getRelations: async (fromId, toId) => {
66
+ let query = '?[from_id, to_id, relation_type, strength] := *relationship{from_id, to_id, relation_type, strength, @ "NOW"}';
67
+ const params = {};
68
+ if (fromId) {
69
+ query += ', from_id = $from_id';
70
+ params.from_id = fromId;
71
+ }
72
+ if (toId) {
73
+ query += ', to_id = $to_id';
74
+ params.to_id = toId;
75
+ }
76
+ const res = await db.run(query, params);
77
+ return res.rows.map((r) => ({
78
+ from_id: r[0],
79
+ to_id: r[1],
80
+ relation_type: r[2],
81
+ strength: r[3],
82
+ }));
83
+ },
84
+ vectorSearchEntity: async (embedding, limit) => {
85
+ const res = await db.run('?[id, name, type] := *entity{id, name, type, @ "NOW"} :limit $limit', { limit });
86
+ return res.rows.map((r) => [r[0], r[1], r[2], {}, 0.8]);
87
+ },
88
+ };
89
+ const suggestions = new proactive_suggestions_1.ProactiveSuggestionsService(dbWrapper, embeddings, {
90
+ maxSuggestions: 5,
91
+ minConfidence: 0.5,
92
+ enableVectorSimilarity: true,
93
+ enableCommonNeighbors: true,
94
+ enableInference: true,
95
+ enableGraphProximity: true,
96
+ });
97
+ try {
98
+ // Setup: Create test entities and relationships
99
+ console.error('[Test] Setting up test data...');
100
+ const EMBEDDING_DIM = embeddings.getDimensions();
101
+ // Create entity table
102
+ try {
103
+ await db.run(`{:create entity {id: String, created_at: Validity => name: String, type: String, embedding: <F32; ${EMBEDDING_DIM}>, name_embedding: <F32; ${EMBEDDING_DIM}>, metadata: Json}}`);
104
+ }
105
+ catch (e) { }
106
+ // Create relationship table
107
+ try {
108
+ await db.run('{:create relationship {from_id: String, to_id: String, relation_type: String, created_at: Validity => strength: Float, metadata: Json}}');
109
+ }
110
+ catch (e) { }
111
+ // Create entities
112
+ const aliceEmb = await embeddings.embed('Alice Developer');
113
+ const bobEmb = await embeddings.embed('Bob Engineer');
114
+ const charlieEmb = await embeddings.embed('Charlie Manager');
115
+ const projectXEmb = await embeddings.embed('Project X Development');
116
+ const projectYEmb = await embeddings.embed('Project Y Research');
117
+ const now = Date.now() * 1000;
118
+ await db.run(`
119
+ ?[id, created_at, name, type, embedding, name_embedding, metadata] <- [
120
+ ['alice-id', [${now}, true], 'Alice', 'Person', $emb, $emb, {}]
121
+ ] :insert entity {id, created_at => name, type, embedding, name_embedding, metadata}
122
+ `, { emb: aliceEmb });
123
+ await db.run(`
124
+ ?[id, created_at, name, type, embedding, name_embedding, metadata] <- [
125
+ ['bob-id', [${now}, true], 'Bob', 'Person', $emb, $emb, {}]
126
+ ] :insert entity {id, created_at => name, type, embedding, name_embedding, metadata}
127
+ `, { emb: bobEmb });
128
+ await db.run(`
129
+ ?[id, created_at, name, type, embedding, name_embedding, metadata] <- [
130
+ ['charlie-id', [${now}, true], 'Charlie', 'Person', $emb, $emb, {}]
131
+ ] :insert entity {id, created_at => name, type, embedding, name_embedding, metadata}
132
+ `, { emb: charlieEmb });
133
+ await db.run(`
134
+ ?[id, created_at, name, type, embedding, name_embedding, metadata] <- [
135
+ ['project-x-id', [${now}, true], 'Project X', 'Project', $emb, $emb, {}]
136
+ ] :insert entity {id, created_at => name, type, embedding, name_embedding, metadata}
137
+ `, { emb: projectXEmb });
138
+ await db.run(`
139
+ ?[id, created_at, name, type, embedding, name_embedding, metadata] <- [
140
+ ['project-y-id', [${now}, true], 'Project Y', 'Project', $emb, $emb, {}]
141
+ ] :insert entity {id, created_at => name, type, embedding, name_embedding, metadata}
142
+ `, { emb: projectYEmb });
143
+ // Create relationships
144
+ await db.run(`
145
+ ?[from_id, to_id, relation_type, created_at, strength, metadata] <- [
146
+ ['alice-id', 'project-x-id', 'works_on', [${now}, true], 1.0, {}],
147
+ ['bob-id', 'project-x-id', 'works_on', [${now}, true], 1.0, {}],
148
+ ['charlie-id', 'alice-id', 'manages', [${now}, true], 0.9, {}],
149
+ ['bob-id', 'project-y-id', 'works_on', [${now}, true], 0.8, {}],
150
+ ['alice-id', 'bob-id', 'colleague_of', [${now}, true], 0.85, {}]
151
+ ] :insert relationship {from_id, to_id, relation_type, created_at => strength, metadata}
152
+ `);
153
+ console.error('[Test] Test data created.\n');
154
+ // Test 1: Vector Similarity
155
+ console.error('[Test 1] Vector Similarity Discovery');
156
+ const vectorSuggestions = await suggestions.suggestConnections('alice-id');
157
+ console.error(` Found ${vectorSuggestions.length} suggestions for Alice`);
158
+ vectorSuggestions.forEach(s => {
159
+ console.error(` - ${s.entity_name} (${s.source}): confidence=${s.confidence.toFixed(2)}`);
160
+ });
161
+ console.error('');
162
+ // Test 2: Common Neighbors
163
+ console.error('[Test 2] Common Neighbors Detection');
164
+ const commonNeighborSuggestions = vectorSuggestions.filter(s => s.source === proactive_suggestions_1.SuggestionSource.COMMON_NEIGHBORS);
165
+ console.error(` Found ${commonNeighborSuggestions.length} common neighbor suggestions`);
166
+ commonNeighborSuggestions.forEach(s => {
167
+ console.error(` - ${s.entity_name}: ${s.reason}`);
168
+ });
169
+ console.error('');
170
+ // Test 3: Graph Proximity
171
+ console.error('[Test 3] Graph Proximity Search');
172
+ const graphProximitySuggestions = vectorSuggestions.filter(s => s.source === proactive_suggestions_1.SuggestionSource.GRAPH_PROXIMITY);
173
+ console.error(` Found ${graphProximitySuggestions.length} graph proximity suggestions`);
174
+ graphProximitySuggestions.forEach(s => {
175
+ console.error(` - ${s.entity_name}: ${s.reason}`);
176
+ });
177
+ console.error('');
178
+ // Test 4: Inference
179
+ console.error('[Test 4] Inference-Based Suggestions');
180
+ const inferenceSuggestions = vectorSuggestions.filter(s => s.source === proactive_suggestions_1.SuggestionSource.INFERENCE);
181
+ console.error(` Found ${inferenceSuggestions.length} inferred suggestions`);
182
+ inferenceSuggestions.forEach(s => {
183
+ console.error(` - ${s.entity_name}: ${s.reason}`);
184
+ });
185
+ console.error('');
186
+ // Test 5: Confidence Levels
187
+ console.error('[Test 5] Confidence Level Distribution');
188
+ const highConfidence = vectorSuggestions.filter(s => s.confidence_level === proactive_suggestions_1.ConfidenceLevel.HIGH);
189
+ const mediumConfidence = vectorSuggestions.filter(s => s.confidence_level === proactive_suggestions_1.ConfidenceLevel.MEDIUM);
190
+ const lowConfidence = vectorSuggestions.filter(s => s.confidence_level === proactive_suggestions_1.ConfidenceLevel.LOW);
191
+ console.error(` High: ${highConfidence.length}, Medium: ${mediumConfidence.length}, Low: ${lowConfidence.length}`);
192
+ console.error('');
193
+ // Test 6: Batch Operations
194
+ console.error('[Test 6] Batch Suggestions');
195
+ const batchResults = await suggestions.suggestConnectionsBatch(['alice-id', 'bob-id', 'charlie-id']);
196
+ console.error(` Processed ${batchResults.size} entities`);
197
+ for (const [entityId, suggs] of batchResults.entries()) {
198
+ console.error(` ${entityId}: ${suggs.length} suggestions`);
199
+ }
200
+ console.error('');
201
+ // Test 7: Configuration
202
+ console.error('[Test 7] Configuration Management');
203
+ const config = suggestions.getConfig();
204
+ console.error(` Max Suggestions: ${config.maxSuggestions}`);
205
+ console.error(` Min Confidence: ${config.minConfidence}`);
206
+ console.error(` Vector Similarity Weight: ${config.vectorSimilarityWeight}`);
207
+ console.error(` Common Neighbors Weight: ${config.commonNeighborsWeight}`);
208
+ console.error(` Inference Weight: ${config.inferenceWeight}`);
209
+ console.error(` Graph Proximity Weight: ${config.graphProximityWeight}`);
210
+ console.error('');
211
+ // Test 8: Updated Configuration
212
+ console.error('[Test 8] Updated Configuration');
213
+ suggestions.updateConfig({
214
+ maxSuggestions: 3,
215
+ minConfidence: 0.7,
216
+ });
217
+ const updatedConfig = suggestions.getConfig();
218
+ console.error(` Max Suggestions (updated): ${updatedConfig.maxSuggestions}`);
219
+ console.error(` Min Confidence (updated): ${updatedConfig.minConfidence}`);
220
+ const updatedSuggestions = await suggestions.suggestConnections('alice-id');
221
+ console.error(` Suggestions with new config: ${updatedSuggestions.length}`);
222
+ console.error('');
223
+ console.error('[Test] โœ… All tests completed successfully!\n');
224
+ }
225
+ catch (error) {
226
+ console.error('[Test] โŒ Error during testing:', error);
227
+ }
228
+ finally {
229
+ await db.close();
230
+ // Clean up
231
+ if (fs.existsSync(DB_PATH)) {
232
+ try {
233
+ fs.unlinkSync(DB_PATH);
234
+ }
235
+ catch (e) { }
236
+ }
237
+ }
238
+ }
239
+ // Run tests
240
+ runTests().catch(console.error);
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+ /**
3
+ * Test suite for Temporal Conflict Resolution Service
4
+ *
5
+ * Tests T-GRAG-inspired conflict detection and resolution
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ const cozo_node_1 = require("cozo-node");
9
+ const embedding_service_1 = require("./embedding-service");
10
+ const temporal_conflict_resolution_1 = require("./temporal-conflict-resolution");
11
+ const uuid_1 = require("uuid");
12
+ const DB_PATH = 'test_temporal_conflict.cozo.db';
13
+ async function setupDatabase() {
14
+ const db = new cozo_node_1.CozoDb('sqlite', DB_PATH);
15
+ // Create schema matching the main codebase format
16
+ try {
17
+ await db.run(`
18
+ :create entity {
19
+ id: String,
20
+ created_at: Validity
21
+ =>
22
+ name: String,
23
+ type: String,
24
+ embedding: <F32; 1024>,
25
+ name_embedding: <F32; 1024>,
26
+ metadata: Json
27
+ }
28
+ `);
29
+ }
30
+ catch (e) {
31
+ // Schema already exists, ignore
32
+ }
33
+ try {
34
+ await db.run(`
35
+ :create observation {
36
+ id: String,
37
+ created_at: Validity
38
+ =>
39
+ entity_id: String,
40
+ text: String,
41
+ embedding: <F32; 1024>,
42
+ metadata: Json
43
+ }
44
+ `);
45
+ }
46
+ catch (e) {
47
+ // Schema already exists, ignore
48
+ }
49
+ return db;
50
+ }
51
+ async function createTestEntity(db, embeddings, name) {
52
+ const entityId = (0, uuid_1.v4)();
53
+ const embedding = await embeddings.embed(name);
54
+ const now = Date.now() * 1000; // Convert to microseconds
55
+ await db.run(`
56
+ ?[id, created_at, name, type, embedding, name_embedding, metadata] :=
57
+ id = $id,
58
+ created_at = $created_at,
59
+ name = $name,
60
+ type = $type,
61
+ embedding = $embedding,
62
+ name_embedding = $name_embedding,
63
+ metadata = $metadata
64
+
65
+ :put entity {
66
+ id, created_at => name, type, embedding, name_embedding, metadata
67
+ }
68
+ `, {
69
+ id: entityId,
70
+ created_at: [now, true], // Validity format: [timestamp_microseconds, is_valid]
71
+ name,
72
+ type: 'test',
73
+ embedding,
74
+ name_embedding: embedding,
75
+ metadata: {},
76
+ });
77
+ return entityId;
78
+ }
79
+ async function createTestObservation(db, embeddings, entityId, text, createdAt) {
80
+ const obsId = (0, uuid_1.v4)();
81
+ const embedding = await embeddings.embed(text);
82
+ await db.run(`
83
+ ?[id, created_at, entity_id, text, embedding, metadata] :=
84
+ id = $id,
85
+ created_at = $created_at,
86
+ entity_id = $entity_id,
87
+ text = $text,
88
+ embedding = $embedding,
89
+ metadata = $metadata
90
+
91
+ :put observation {
92
+ id, created_at => entity_id, text, embedding, metadata
93
+ }
94
+ `, {
95
+ id: obsId,
96
+ created_at: [createdAt * 1000, true], // Validity format: [timestamp_microseconds, is_valid]
97
+ entity_id: entityId,
98
+ text,
99
+ embedding,
100
+ metadata: {},
101
+ });
102
+ return obsId;
103
+ }
104
+ async function runTests() {
105
+ console.log('๐Ÿงช Starting Temporal Conflict Resolution Tests\n');
106
+ const db = await setupDatabase();
107
+ const embeddings = new embedding_service_1.EmbeddingService();
108
+ const conflictService = new temporal_conflict_resolution_1.TemporalConflictResolutionService(db, embeddings);
109
+ try {
110
+ // Test 1: Temporal Redundancy Detection
111
+ console.log('Test 1: Temporal Redundancy Detection');
112
+ const entity1 = await createTestEntity(db, embeddings, 'Project Alpha');
113
+ const now = Date.now();
114
+ const oneMonthAgo = now - (30 * 24 * 60 * 60 * 1000);
115
+ await createTestObservation(db, embeddings, entity1, 'Project Alpha is in active development with 5 team members', oneMonthAgo);
116
+ await createTestObservation(db, embeddings, entity1, 'Project Alpha is actively being developed by a team of 5 people', now);
117
+ const conflicts1 = await conflictService.detectConflicts(entity1);
118
+ console.log(`โœ“ Found ${conflicts1.length} conflict(s)`);
119
+ if (conflicts1.length > 0) {
120
+ console.log(` Type: ${conflicts1[0].conflict_type}`);
121
+ console.log(` Confidence: ${(conflicts1[0].confidence * 100).toFixed(1)}%`);
122
+ console.log(` Reason: ${conflicts1[0].reason}`);
123
+ }
124
+ console.log('');
125
+ // Test 2: Semantic Contradiction Detection
126
+ console.log('Test 2: Semantic Contradiction Detection');
127
+ const entity2 = await createTestEntity(db, embeddings, 'Service Beta');
128
+ await createTestObservation(db, embeddings, entity2, 'Service Beta is active and running in production', oneMonthAgo);
129
+ await createTestObservation(db, embeddings, entity2, 'Service Beta has been discontinued and shut down', now);
130
+ const conflicts2 = await conflictService.detectConflicts(entity2);
131
+ console.log(`โœ“ Found ${conflicts2.length} conflict(s)`);
132
+ if (conflicts2.length > 0) {
133
+ console.log(` Type: ${conflicts2[0].conflict_type}`);
134
+ console.log(` Confidence: ${(conflicts2[0].confidence * 100).toFixed(1)}%`);
135
+ console.log(` Reason: ${conflicts2[0].reason}`);
136
+ }
137
+ console.log('');
138
+ // Test 3: Superseded Fact Detection
139
+ console.log('Test 3: Superseded Fact Detection');
140
+ const entity3 = await createTestEntity(db, embeddings, 'Company Revenue');
141
+ await createTestObservation(db, embeddings, entity3, 'Company revenue was $10 million in Q1 2025', oneMonthAgo);
142
+ await createTestObservation(db, embeddings, entity3, 'Updated: Company revenue is now $12 million in Q1 2025 after corrections', now);
143
+ const conflicts3 = await conflictService.detectConflicts(entity3);
144
+ console.log(`โœ“ Found ${conflicts3.length} conflict(s)`);
145
+ if (conflicts3.length > 0) {
146
+ console.log(` Type: ${conflicts3[0].conflict_type}`);
147
+ console.log(` Confidence: ${(conflicts3[0].confidence * 100).toFixed(1)}%`);
148
+ console.log(` Reason: ${conflicts3[0].reason}`);
149
+ }
150
+ console.log('');
151
+ // Test 4: Conflict Resolution
152
+ console.log('Test 4: Conflict Resolution');
153
+ const entity4 = await createTestEntity(db, embeddings, 'Product Gamma');
154
+ await createTestObservation(db, embeddings, entity4, 'Product Gamma is available for purchase', oneMonthAgo);
155
+ await createTestObservation(db, embeddings, entity4, 'Product Gamma is no longer available and has been removed from catalog', now);
156
+ const resolution = await conflictService.resolveConflicts(entity4);
157
+ console.log(`โœ“ Resolved ${resolution.resolved_conflicts} conflict(s)`);
158
+ console.log(` Invalidated observations: ${resolution.invalidated_observations.length}`);
159
+ console.log(` Audit observations created: ${resolution.audit_observations.length}`);
160
+ console.log('');
161
+ // Test 5: No Conflicts (Different Topics)
162
+ console.log('Test 5: No Conflicts (Different Topics)');
163
+ const entity5 = await createTestEntity(db, embeddings, 'Team Delta');
164
+ await createTestObservation(db, embeddings, entity5, 'Team Delta is working on the frontend', oneMonthAgo);
165
+ await createTestObservation(db, embeddings, entity5, 'Team Delta completed the backend integration', now);
166
+ const conflicts5 = await conflictService.detectConflicts(entity5);
167
+ console.log(`โœ“ Found ${conflicts5.length} conflict(s) (expected: 0)`);
168
+ if (conflicts5.length > 0) {
169
+ console.log(` Unexpected conflict found:`);
170
+ console.log(` Type: ${conflicts5[0].conflict_type}`);
171
+ console.log(` Confidence: ${(conflicts5[0].confidence * 100).toFixed(1)}%`);
172
+ console.log(` Reason: ${conflicts5[0].reason}`);
173
+ }
174
+ console.log('');
175
+ // Test 6: Configuration Update
176
+ console.log('Test 6: Configuration Update');
177
+ const originalConfig = conflictService.getConfig();
178
+ console.log(` Original similarity threshold: ${originalConfig.similarityThreshold}`);
179
+ conflictService.updateConfig({ similarityThreshold: 0.9 });
180
+ const updatedConfig = conflictService.getConfig();
181
+ console.log(` Updated similarity threshold: ${updatedConfig.similarityThreshold}`);
182
+ console.log('โœ“ Configuration updated successfully');
183
+ console.log('');
184
+ // Test 7: Time Window Filtering
185
+ console.log('Test 7: Time Window Filtering');
186
+ const entity7 = await createTestEntity(db, embeddings, 'Old Project');
187
+ const twoYearsAgo = now - (730 * 24 * 60 * 60 * 1000);
188
+ await createTestObservation(db, embeddings, entity7, 'Old Project is active', twoYearsAgo);
189
+ await createTestObservation(db, embeddings, entity7, 'Old Project is inactive', now);
190
+ // Should not detect conflict due to time window (default 365 days)
191
+ const conflicts7 = await conflictService.detectConflicts(entity7);
192
+ console.log(`โœ“ Found ${conflicts7.length} conflict(s) (expected: 0 due to time window)`);
193
+ console.log('');
194
+ // Test 8: Multiple Conflicts
195
+ console.log('Test 8: Multiple Conflicts');
196
+ const entity8 = await createTestEntity(db, embeddings, 'Multi Conflict');
197
+ const threeMonthsAgo = now - (90 * 24 * 60 * 60 * 1000);
198
+ const twoMonthsAgo = now - (60 * 24 * 60 * 60 * 1000);
199
+ await createTestObservation(db, embeddings, entity8, 'Status is enabled and operational', threeMonthsAgo);
200
+ await createTestObservation(db, embeddings, entity8, 'Status is disabled temporarily', twoMonthsAgo);
201
+ await createTestObservation(db, embeddings, entity8, 'Status is permanently shut down', now);
202
+ const conflicts8 = await conflictService.detectConflicts(entity8);
203
+ console.log(`โœ“ Found ${conflicts8.length} conflict(s)`);
204
+ conflicts8.forEach((c, i) => {
205
+ console.log(` Conflict ${i + 1}: ${c.conflict_type} (confidence: ${(c.confidence * 100).toFixed(1)}%)`);
206
+ });
207
+ console.log('');
208
+ console.log('โœ… All tests completed successfully!');
209
+ }
210
+ catch (error) {
211
+ console.error('โŒ Test failed:', error);
212
+ throw error;
213
+ }
214
+ finally {
215
+ // Cleanup
216
+ try {
217
+ const fs = await import('fs');
218
+ if (fs.existsSync(DB_PATH)) {
219
+ fs.unlinkSync(DB_PATH);
220
+ }
221
+ }
222
+ catch (e) {
223
+ // Ignore cleanup errors
224
+ }
225
+ }
226
+ }
227
+ // Run tests
228
+ runTests().catch(console.error);