@unrdf/dark-matter 5.0.1 → 26.4.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unrdf/dark-matter",
3
- "version": "5.0.1",
3
+ "version": "26.4.3",
4
4
  "description": "UNRDF Dark Matter - Query Optimization and Performance Analysis (Optional Extension)",
5
5
  "type": "module",
6
6
  "main": "src/index.mjs",
@@ -24,8 +24,9 @@
24
24
  "80-20"
25
25
  ],
26
26
  "dependencies": {
27
- "typhonjs-escomplex": "^0.1.0",
28
- "@unrdf/core": "5.0.1"
27
+ "@unrdf/core": "26.4.3",
28
+ "@unrdf/oxigraph": "26.4.3",
29
+ "typhonjs-escomplex": "^0.1.0"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@types/node": "^24.10.1",
@@ -52,10 +53,10 @@
52
53
  "test:fast": "vitest run --coverage",
53
54
  "test:watch": "vitest --coverage",
54
55
  "build": "node build.config.mjs",
55
- "lint": "eslint src/ test/ --max-warnings=0",
56
- "lint:fix": "eslint src/ test/ --fix",
57
- "format": "prettier --write src/ test/",
58
- "format:check": "prettier --check src/ test/",
56
+ "lint": "eslint src/ --max-warnings=0",
57
+ "lint:fix": "eslint src/ --fix",
58
+ "format": "prettier --write src/",
59
+ "format:check": "prettier --check src/",
59
60
  "clean": "rm -rf dist/ .nyc_output/ coverage/",
60
61
  "dev": "echo 'Development mode for @unrdf/dark-matter'"
61
62
  }
@@ -7,7 +7,7 @@
7
7
  * and identify expensive operations for 80/20 optimization.
8
8
  */
9
9
 
10
- import { analyzeSPARQLQuery, extractVariables } from '../../utils/sparql-utils.mjs';
10
+ import { analyzeSPARQLQuery, extractVariables } from '@unrdf/core/utils/sparql-utils';
11
11
  import { z } from 'zod';
12
12
 
13
13
  /**
package/README.md DELETED
@@ -1,81 +0,0 @@
1
- # @unrdf/dark-matter
2
-
3
- ![Version](https://img.shields.io/badge/version-5.0.0--beta.1-blue) ![Production Ready](https://img.shields.io/badge/production-ready-green)
4
-
5
-
6
- **Query Optimization and Performance Analysis** *(Optional Extension)*
7
-
8
- Optimize SPARQL queries and analyze RDF graph performance. Implements the 80/20 principle for query optimization.
9
-
10
- ## Installation
11
-
12
- ```bash
13
- pnpm add @unrdf/dark-matter
14
- ```
15
-
16
- ## 📚 Examples
17
-
18
- See these examples that demonstrate @unrdf/dark-matter:
19
-
20
- - **[dark-matter-80-20.mjs](../../examples/dark-matter-80-20.mjs)** - Query optimization basics (30 min, 5-10x speedup)
21
- - **[dark-matter-query-optimization.mjs](../../examples/dark-matter-query-optimization.mjs)** - Advanced optimization strategies
22
- - **[profiling-example.mjs](../../examples/profiling-example.mjs)** - Performance profiling and analysis
23
- - **[lockchain-dark-matter-test.mjs](../../examples/lockchain-dark-matter-test.mjs)** - Combined optimization + audit
24
-
25
- **Need faster queries?** Start with [dark-matter-80-20.mjs](../../examples/dark-matter-80-20.mjs).
26
-
27
- ## Quick Start
28
-
29
- ```javascript
30
- import { optimizeQuery, analyzePerformance } from '@unrdf/dark-matter'
31
-
32
- // Optimize a SPARQL query
33
- const optimized = optimizeQuery(
34
- 'SELECT ?name WHERE { ?s foaf:name ?name. ?s foaf:age ?age }'
35
- )
36
-
37
- // Analyze graph performance
38
- const metrics = await analyzePerformance(store)
39
- console.log('Query execution time:', metrics.queryTime)
40
- ```
41
-
42
- ## Features
43
-
44
- - ✅ SPARQL query optimization
45
- - ✅ Critical path analysis
46
- - ✅ Performance metrics collection
47
- - ✅ Query plan analysis
48
- - ✅ Index recommendations
49
- - ✅ Bottleneck identification
50
-
51
- ## Use Cases
52
-
53
- - **Performance tuning**: Optimize slow queries
54
- - **Capacity planning**: Understand resource usage
55
- - **Monitoring**: Track graph performance
56
- - **Optimization**: Find and fix bottlenecks
57
- - **Benchmarking**: Compare query strategies
58
-
59
- ## Documentation
60
-
61
- - **[API Reference](./docs/API.md)** - Complete API documentation
62
- - **[User Guide](./docs/GUIDE.md)** - Optimization guide
63
- - **[Examples](./examples/)** - Code examples
64
- - **[Contributing](./docs/CONTRIBUTING.md)** - How to contribute
65
-
66
- ## Status
67
-
68
- **Optional Extension** - Use only if you need query optimization.
69
- Most applications don't need this.
70
-
71
- ## Depends On
72
-
73
- - `@unrdf/core` - RDF substrate
74
-
75
- ## VOC Usage
76
-
77
- - Performance-critical applications
78
-
79
- ## License
80
-
81
- MIT
@@ -1,367 +0,0 @@
1
- /**
2
- * @file Dark Matter 80/20 Critical Path Identification
3
- * @module dark-matter/critical-path
4
- *
5
- * @description
6
- * Implements the 80/20 algorithm to identify the top 20% slowest queries
7
- * that account for 80% of performance impact.
8
- */
9
-
10
- import { z } from 'zod';
11
-
12
- /**
13
- * Schema for query execution log
14
- */
15
- const QueryExecutionLogSchema = z.object({
16
- queryId: z.string(),
17
- query: z.string(),
18
- executionTime: z.number(),
19
- timestamp: z.number(),
20
- metadata: z.object({}).passthrough().optional(),
21
- });
22
-
23
- /**
24
- * Schema for critical path result
25
- */
26
- const CriticalPathResultSchema = z.object({
27
- criticalQueries: z.array(
28
- z.object({
29
- queryId: z.string(),
30
- query: z.string(),
31
- executionTime: z.number(),
32
- occurrences: z.number(),
33
- totalTime: z.number(),
34
- percentageOfTotal: z.number(),
35
- rank: z.number(),
36
- })
37
- ),
38
- metrics: z.object({
39
- totalQueries: z.number(),
40
- criticalQueryCount: z.number(),
41
- criticalQueryPercentage: z.number(),
42
- totalExecutionTime: z.number(),
43
- criticalExecutionTime: z.number(),
44
- impactRatio: z.number(),
45
- avgExecutionTime: z.number(),
46
- p50: z.number(),
47
- p90: z.number(),
48
- p99: z.number(),
49
- }),
50
- timestamp: z.number(),
51
- });
52
-
53
- /**
54
- * Critical Path Identifier for Dark Matter 80/20
55
- */
56
- export class CriticalPathIdentifier {
57
- /**
58
- * Create a new critical path identifier
59
- * @param {Object} [config] - Configuration
60
- */
61
- constructor(config = {}) {
62
- this.config = {
63
- targetImpactRatio: config.targetImpactRatio || 0.8, // 80% of impact
64
- targetQueryRatio: config.targetQueryRatio || 0.2, // from 20% of queries
65
- minSampleSize: config.minSampleSize || 10,
66
- windowSize: config.windowSize || 1000, // Keep last 1000 queries
67
- ...config,
68
- };
69
-
70
- this.executionLogs = [];
71
- this.cache = new Map();
72
- }
73
-
74
- /**
75
- * Log a query execution
76
- * @param {string} queryId - Query identifier
77
- * @param {string} query - Query string
78
- * @param {number} executionTime - Execution time in ms
79
- * @param {Object} [metadata] - Optional metadata
80
- */
81
- logExecution(queryId, query, executionTime, metadata = {}) {
82
- const log = {
83
- queryId,
84
- query,
85
- executionTime,
86
- timestamp: Date.now(),
87
- metadata,
88
- };
89
-
90
- this.executionLogs.push(QueryExecutionLogSchema.parse(log));
91
-
92
- // Keep window size
93
- if (this.executionLogs.length > this.config.windowSize) {
94
- this.executionLogs.shift();
95
- }
96
-
97
- // Invalidate cache
98
- this.cache.clear();
99
- }
100
-
101
- /**
102
- * Analyze and identify critical path
103
- * @returns {Object} Critical path analysis
104
- */
105
- identify() {
106
- if (this.executionLogs.length < this.config.minSampleSize) {
107
- throw new Error(
108
- `Insufficient data: ${this.executionLogs.length} queries logged, ` +
109
- `minimum ${this.config.minSampleSize} required`
110
- );
111
- }
112
-
113
- // Check cache
114
- const cacheKey = 'critical-path';
115
- if (this.cache.has(cacheKey)) {
116
- return this.cache.get(cacheKey);
117
- }
118
-
119
- // Group queries by queryId and calculate statistics
120
- const queryStats = this._aggregateQueryStats();
121
-
122
- // Sort by total execution time (descending)
123
- const sortedQueries = Array.from(queryStats.values()).sort((a, b) => b.totalTime - a.totalTime);
124
-
125
- // Calculate total execution time
126
- const totalExecutionTime = sortedQueries.reduce((sum, q) => sum + q.totalTime, 0);
127
-
128
- // Find critical queries (top 20% that account for 80% of time)
129
- const criticalQueries = this._findCriticalQueries(sortedQueries, totalExecutionTime);
130
-
131
- // Calculate metrics
132
- const metrics = this._calculateMetrics(sortedQueries, criticalQueries, totalExecutionTime);
133
-
134
- const result = {
135
- criticalQueries: criticalQueries.map((q, index) => ({
136
- ...q,
137
- rank: index + 1,
138
- })),
139
- metrics,
140
- timestamp: Date.now(),
141
- };
142
-
143
- const validated = CriticalPathResultSchema.parse(result);
144
-
145
- // Cache result
146
- this.cache.set(cacheKey, validated);
147
-
148
- return validated;
149
- }
150
-
151
- /**
152
- * Aggregate query statistics
153
- * @returns {Map} Query statistics
154
- * @private
155
- */
156
- _aggregateQueryStats() {
157
- const stats = new Map();
158
-
159
- for (const log of this.executionLogs) {
160
- if (!stats.has(log.queryId)) {
161
- stats.set(log.queryId, {
162
- queryId: log.queryId,
163
- query: log.query,
164
- executionTime: 0,
165
- occurrences: 0,
166
- totalTime: 0,
167
- percentageOfTotal: 0,
168
- });
169
- }
170
-
171
- const stat = stats.get(log.queryId);
172
- stat.occurrences++;
173
- stat.totalTime += log.executionTime;
174
- stat.executionTime = stat.totalTime / stat.occurrences; // Average
175
- }
176
-
177
- return stats;
178
- }
179
-
180
- /**
181
- * Find critical queries using 80/20 algorithm
182
- * @param {Array} sortedQueries - Queries sorted by total time
183
- * @param {number} totalExecutionTime - Total execution time
184
- * @returns {Array} Critical queries
185
- * @private
186
- */
187
- _findCriticalQueries(sortedQueries, totalExecutionTime) {
188
- const critical = [];
189
- let cumulativeTime = 0;
190
- let cumulativePercentage = 0;
191
-
192
- // Find queries that contribute to target impact ratio (80%)
193
- for (const query of sortedQueries) {
194
- cumulativeTime += query.totalTime;
195
- cumulativePercentage = cumulativeTime / totalExecutionTime;
196
-
197
- query.percentageOfTotal = (query.totalTime / totalExecutionTime) * 100;
198
- critical.push(query);
199
-
200
- // Stop when we reach the target impact ratio
201
- if (cumulativePercentage >= this.config.targetImpactRatio) {
202
- break;
203
- }
204
- }
205
-
206
- // Ensure we don't exceed target query ratio (20%)
207
- const maxCriticalQueries = Math.ceil(sortedQueries.length * this.config.targetQueryRatio);
208
-
209
- // Return either the queries that hit 80% impact or top 20%, whichever is smaller
210
- return critical.slice(0, Math.min(critical.length, maxCriticalQueries));
211
- }
212
-
213
- /**
214
- * Calculate performance metrics
215
- * @param {Array} allQueries - All queries
216
- * @param {Array} criticalQueries - Critical queries
217
- * @param {number} totalExecutionTime - Total execution time
218
- * @returns {Object} Metrics
219
- * @private
220
- */
221
- _calculateMetrics(allQueries, criticalQueries, totalExecutionTime) {
222
- const criticalExecutionTime = criticalQueries.reduce((sum, q) => sum + q.totalTime, 0);
223
-
224
- // Calculate percentiles
225
- const executionTimes = this.executionLogs.map(log => log.executionTime).sort((a, b) => a - b);
226
-
227
- const p50 = this._percentile(executionTimes, 0.5);
228
- const p90 = this._percentile(executionTimes, 0.9);
229
- const p99 = this._percentile(executionTimes, 0.99);
230
-
231
- return {
232
- totalQueries: allQueries.length,
233
- criticalQueryCount: criticalQueries.length,
234
- criticalQueryPercentage: (criticalQueries.length / allQueries.length) * 100,
235
- totalExecutionTime,
236
- criticalExecutionTime,
237
- impactRatio: criticalExecutionTime / totalExecutionTime,
238
- avgExecutionTime: totalExecutionTime / this.executionLogs.length,
239
- p50,
240
- p90,
241
- p99,
242
- };
243
- }
244
-
245
- /**
246
- * Calculate percentile
247
- * @param {Array} sortedValues - Sorted array of values
248
- * @param {number} percentile - Percentile (0-1)
249
- * @returns {number} Percentile value
250
- * @private
251
- */
252
- _percentile(sortedValues, percentile) {
253
- if (sortedValues.length === 0) return 0;
254
-
255
- const index = Math.ceil(sortedValues.length * percentile) - 1;
256
- return sortedValues[Math.max(0, index)];
257
- }
258
-
259
- /**
260
- * Get report in markdown format
261
- * @returns {string} Markdown report
262
- */
263
- getReport() {
264
- const analysis = this.identify();
265
-
266
- let report = '# Dark Matter 80/20 Critical Path Report\n\n';
267
-
268
- // Summary
269
- report += '## Summary\n\n';
270
- report += `- **Total Queries Analyzed**: ${analysis.metrics.totalQueries}\n`;
271
- report += `- **Critical Queries (Top ${(this.config.targetQueryRatio * 100).toFixed(0)}%)**: ${analysis.metrics.criticalQueryCount}\n`;
272
- report += `- **Impact Ratio**: ${(analysis.metrics.impactRatio * 100).toFixed(1)}% of total execution time\n`;
273
- report += `- **Average Execution Time**: ${analysis.metrics.avgExecutionTime.toFixed(2)}ms\n`;
274
- report += `- **P50 Latency**: ${analysis.metrics.p50.toFixed(2)}ms\n`;
275
- report += `- **P90 Latency**: ${analysis.metrics.p90.toFixed(2)}ms\n`;
276
- report += `- **P99 Latency**: ${analysis.metrics.p99.toFixed(2)}ms\n\n`;
277
-
278
- // Critical queries
279
- report += '## Critical Queries\n\n';
280
- report += '| Rank | Query ID | Occurrences | Avg Time | Total Time | % of Total |\n';
281
- report += '|------|----------|-------------|----------|------------|------------|\n';
282
-
283
- for (const query of analysis.criticalQueries) {
284
- report += `| ${query.rank} | ${query.queryId} | ${query.occurrences} | ${query.executionTime.toFixed(2)}ms | ${query.totalTime.toFixed(2)}ms | ${query.percentageOfTotal.toFixed(1)}% |\n`;
285
- }
286
-
287
- report += '\n## Recommendations\n\n';
288
- report +=
289
- 'Focus optimization efforts on the queries listed above. These represent the critical path:\n\n';
290
- report += `- Optimizing the top ${analysis.metrics.criticalQueryCount} queries will improve ${(analysis.metrics.impactRatio * 100).toFixed(1)}% of query performance\n`;
291
- report +=
292
- '- Consider adding indexes, rewriting queries, or caching results for critical queries\n';
293
- report += '- Monitor P99 latency to catch performance regressions\n';
294
-
295
- return report;
296
- }
297
-
298
- /**
299
- * Get execution logs
300
- * @param {Object} [filter] - Optional filter
301
- * @returns {Array} Execution logs
302
- */
303
- getLogs(filter = {}) {
304
- let logs = [...this.executionLogs];
305
-
306
- if (filter.queryId) {
307
- logs = logs.filter(log => log.queryId === filter.queryId);
308
- }
309
-
310
- if (filter.minExecutionTime) {
311
- logs = logs.filter(log => log.executionTime >= filter.minExecutionTime);
312
- }
313
-
314
- if (filter.startTime && filter.endTime) {
315
- logs = logs.filter(
316
- log => log.timestamp >= filter.startTime && log.timestamp <= filter.endTime
317
- );
318
- }
319
-
320
- return logs;
321
- }
322
-
323
- /**
324
- * Clear all logs
325
- */
326
- clearLogs() {
327
- this.executionLogs = [];
328
- this.cache.clear();
329
- }
330
-
331
- /**
332
- * Export logs to JSON
333
- * @returns {string} JSON string
334
- */
335
- exportLogs() {
336
- return JSON.stringify(
337
- {
338
- logs: this.executionLogs,
339
- config: this.config,
340
- timestamp: Date.now(),
341
- },
342
- null,
343
- 2
344
- );
345
- }
346
-
347
- /**
348
- * Import logs from JSON
349
- * @param {string} json - JSON string
350
- */
351
- importLogs(json) {
352
- const data = JSON.parse(json);
353
- this.executionLogs = data.logs.map(log => QueryExecutionLogSchema.parse(log));
354
- this.cache.clear();
355
- }
356
- }
357
-
358
- /**
359
- * Create a critical path identifier instance
360
- * @param {Object} [config] - Configuration
361
- * @returns {CriticalPathIdentifier} Critical path identifier
362
- */
363
- export function createCriticalPathIdentifier(config = {}) {
364
- return new CriticalPathIdentifier(config);
365
- }
366
-
367
- export default CriticalPathIdentifier;