@skillsmith/core 0.2.0 → 2.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.
- package/README.md +233 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/__tests__/incremental.test.d.ts +13 -0
- package/dist/src/analysis/__tests__/incremental.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/incremental.test.js +515 -0
- package/dist/src/analysis/__tests__/incremental.test.js.map +1 -0
- package/dist/src/analysis/__tests__/integration.test.d.ts +14 -0
- package/dist/src/analysis/__tests__/integration.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/integration.test.js +1059 -0
- package/dist/src/analysis/__tests__/integration.test.js.map +1 -0
- package/dist/src/analysis/__tests__/metrics.test.d.ts +9 -0
- package/dist/src/analysis/__tests__/metrics.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/metrics.test.js +369 -0
- package/dist/src/analysis/__tests__/metrics.test.js.map +1 -0
- package/dist/src/analysis/__tests__/performance.test.d.ts +15 -0
- package/dist/src/analysis/__tests__/performance.test.d.ts.map +1 -0
- package/dist/src/analysis/__tests__/performance.test.js +402 -0
- package/dist/src/analysis/__tests__/performance.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/go.test.d.ts +12 -0
- package/dist/src/analysis/adapters/__tests__/go.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/go.test.js +561 -0
- package/dist/src/analysis/adapters/__tests__/go.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/python.test.d.ts +11 -0
- package/dist/src/analysis/adapters/__tests__/python.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/python.test.js +669 -0
- package/dist/src/analysis/adapters/__tests__/python.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.d.ts +12 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.js +676 -0
- package/dist/src/analysis/adapters/__tests__/rust.test.js.map +1 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts +14 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.d.ts.map +1 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.js +381 -0
- package/dist/src/analysis/adapters/__tests__/typescript.test.js.map +1 -0
- package/dist/src/analysis/adapters/base.d.ts +83 -0
- package/dist/src/analysis/adapters/base.d.ts.map +1 -0
- package/dist/src/analysis/adapters/base.js +40 -0
- package/dist/src/analysis/adapters/base.js.map +1 -0
- package/dist/src/analysis/adapters/factory.d.ts +150 -0
- package/dist/src/analysis/adapters/factory.d.ts.map +1 -0
- package/dist/src/analysis/adapters/factory.js +244 -0
- package/dist/src/analysis/adapters/factory.js.map +1 -0
- package/dist/src/analysis/adapters/go.d.ts +131 -0
- package/dist/src/analysis/adapters/go.d.ts.map +1 -0
- package/dist/src/analysis/adapters/go.js +414 -0
- package/dist/src/analysis/adapters/go.js.map +1 -0
- package/dist/src/analysis/adapters/index.d.ts +20 -0
- package/dist/src/analysis/adapters/index.d.ts.map +1 -0
- package/dist/src/analysis/adapters/index.js +23 -0
- package/dist/src/analysis/adapters/index.js.map +1 -0
- package/dist/src/analysis/adapters/java.d.ts +154 -0
- package/dist/src/analysis/adapters/java.d.ts.map +1 -0
- package/dist/src/analysis/adapters/java.js +407 -0
- package/dist/src/analysis/adapters/java.js.map +1 -0
- package/dist/src/analysis/adapters/python.d.ts +165 -0
- package/dist/src/analysis/adapters/python.d.ts.map +1 -0
- package/dist/src/analysis/adapters/python.js +475 -0
- package/dist/src/analysis/adapters/python.js.map +1 -0
- package/dist/src/analysis/adapters/rust.d.ts +116 -0
- package/dist/src/analysis/adapters/rust.d.ts.map +1 -0
- package/dist/src/analysis/adapters/rust.js +476 -0
- package/dist/src/analysis/adapters/rust.js.map +1 -0
- package/dist/src/analysis/adapters/typescript.d.ts +68 -0
- package/dist/src/analysis/adapters/typescript.d.ts.map +1 -0
- package/dist/src/analysis/adapters/typescript.js +79 -0
- package/dist/src/analysis/adapters/typescript.js.map +1 -0
- package/dist/src/analysis/aggregator.d.ts +193 -0
- package/dist/src/analysis/aggregator.d.ts.map +1 -0
- package/dist/src/analysis/aggregator.js +283 -0
- package/dist/src/analysis/aggregator.js.map +1 -0
- package/dist/src/analysis/cache.d.ts +180 -0
- package/dist/src/analysis/cache.d.ts.map +1 -0
- package/dist/src/analysis/cache.js +279 -0
- package/dist/src/analysis/cache.js.map +1 -0
- package/dist/src/analysis/file-streamer.d.ts +136 -0
- package/dist/src/analysis/file-streamer.d.ts.map +1 -0
- package/dist/src/analysis/file-streamer.js +291 -0
- package/dist/src/analysis/file-streamer.js.map +1 -0
- package/dist/src/analysis/incremental-parser.d.ts +186 -0
- package/dist/src/analysis/incremental-parser.d.ts.map +1 -0
- package/dist/src/analysis/incremental-parser.js +291 -0
- package/dist/src/analysis/incremental-parser.js.map +1 -0
- package/dist/src/analysis/incremental.d.ts +186 -0
- package/dist/src/analysis/incremental.d.ts.map +1 -0
- package/dist/src/analysis/incremental.js +247 -0
- package/dist/src/analysis/incremental.js.map +1 -0
- package/dist/src/analysis/index.d.ts +25 -3
- package/dist/src/analysis/index.d.ts.map +1 -1
- package/dist/src/analysis/index.js +45 -3
- package/dist/src/analysis/index.js.map +1 -1
- package/dist/src/analysis/language-detector.d.ts +92 -0
- package/dist/src/analysis/language-detector.d.ts.map +1 -0
- package/dist/src/analysis/language-detector.js +602 -0
- package/dist/src/analysis/language-detector.js.map +1 -0
- package/dist/src/analysis/memory-monitor.d.ts +199 -0
- package/dist/src/analysis/memory-monitor.d.ts.map +1 -0
- package/dist/src/analysis/memory-monitor.js +271 -0
- package/dist/src/analysis/memory-monitor.js.map +1 -0
- package/dist/src/analysis/metrics.d.ts +300 -0
- package/dist/src/analysis/metrics.d.ts.map +1 -0
- package/dist/src/analysis/metrics.js +537 -0
- package/dist/src/analysis/metrics.js.map +1 -0
- package/dist/src/analysis/router.d.ts +264 -0
- package/dist/src/analysis/router.d.ts.map +1 -0
- package/dist/src/analysis/router.js +398 -0
- package/dist/src/analysis/router.js.map +1 -0
- package/dist/src/analysis/tree-cache.d.ts +208 -0
- package/dist/src/analysis/tree-cache.d.ts.map +1 -0
- package/dist/src/analysis/tree-cache.js +288 -0
- package/dist/src/analysis/tree-cache.js.map +1 -0
- package/dist/src/analysis/tree-sitter/manager.d.ts +141 -0
- package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/manager.js +239 -0
- package/dist/src/analysis/tree-sitter/manager.js.map +1 -0
- package/dist/src/analysis/types.d.ts +69 -6
- package/dist/src/analysis/types.d.ts.map +1 -1
- package/dist/src/analysis/types.js +23 -2
- package/dist/src/analysis/types.js.map +1 -1
- package/dist/src/analysis/worker-pool.d.ts +141 -0
- package/dist/src/analysis/worker-pool.d.ts.map +1 -0
- package/dist/src/analysis/worker-pool.js +418 -0
- package/dist/src/analysis/worker-pool.js.map +1 -0
- package/dist/src/analytics/schema.d.ts +1 -1
- package/dist/src/analytics/schema.d.ts.map +1 -1
- package/dist/src/analytics/schema.js +72 -0
- package/dist/src/analytics/schema.js.map +1 -1
- package/dist/src/api/cache.d.ts +24 -1
- package/dist/src/api/cache.d.ts.map +1 -1
- package/dist/src/api/cache.js +50 -2
- package/dist/src/api/cache.js.map +1 -1
- package/dist/src/api/client.d.ts +132 -2
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +214 -18
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/api/index.d.ts +2 -0
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js +7 -0
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/types.d.ts +251 -0
- package/dist/src/api/types.d.ts.map +1 -0
- package/dist/src/api/types.js +9 -0
- package/dist/src/api/types.js.map +1 -0
- package/dist/src/benchmarks/memory/MemoryProfiler.d.ts.map +1 -1
- package/dist/src/benchmarks/memory/MemoryProfiler.js.map +1 -1
- package/dist/src/embeddings/index.d.ts.map +1 -1
- package/dist/src/embeddings/index.js.map +1 -1
- package/dist/src/errors.d.ts +1 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +1 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/repositories/IndexerRepository.d.ts.map +1 -1
- package/dist/src/repositories/IndexerRepository.js +1 -0
- package/dist/src/repositories/IndexerRepository.js.map +1 -1
- package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
- package/dist/src/repositories/SkillRepository.js +1 -0
- package/dist/src/repositories/SkillRepository.js.map +1 -1
- package/dist/src/repositories/quarantine/QuarantineRepository.d.ts.map +1 -1
- package/dist/src/repositories/quarantine/QuarantineRepository.js.map +1 -1
- package/dist/src/repositories/quarantine/query-builder.d.ts.map +1 -1
- package/dist/src/repositories/quarantine/query-builder.js +1 -1
- package/dist/src/repositories/quarantine/query-builder.js.map +1 -1
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +3 -3
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/scripts/github-import/index.js.map +1 -1
- package/dist/src/scripts/import-github-skills.js +1 -1
- package/dist/src/scripts/import-github-skills.js.map +1 -1
- package/dist/src/scripts/skill-scanner/reporter.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/reporter.js.map +1 -1
- package/dist/src/scripts/skill-scanner/scanner.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/scanner.js.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
- package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
- package/dist/src/scripts/validation/index.js +1 -2
- package/dist/src/scripts/validation/index.js.map +1 -1
- package/dist/src/scripts/validation/pipeline.d.ts.map +1 -1
- package/dist/src/scripts/validation/pipeline.js.map +1 -1
- package/dist/src/scripts/validation/types.d.ts +2 -2
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/services/SearchService.d.ts.map +1 -1
- package/dist/src/services/SearchService.js +1 -0
- package/dist/src/services/SearchService.js.map +1 -1
- package/dist/src/session/SessionHealthMonitor.d.ts +1 -1
- package/dist/src/session/SessionHealthMonitor.d.ts.map +1 -1
- package/dist/src/session/SessionHealthMonitor.js +1 -1
- package/dist/src/session/SessionHealthMonitor.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +1 -1
- package/dist/src/telemetry/index.d.ts.map +1 -1
- package/dist/src/telemetry/index.js +2 -2
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/posthog.d.ts +27 -5
- package/dist/src/telemetry/posthog.d.ts.map +1 -1
- package/dist/src/telemetry/posthog.js +20 -5
- package/dist/src/telemetry/posthog.js.map +1 -1
- package/dist/src/types/skill.d.ts +3 -0
- package/dist/src/types/skill.d.ts.map +1 -1
- package/dist/src/types.d.ts +2 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +2 -2
- package/dist/src/types.js.map +1 -1
- package/dist/tests/adapters-factory.test.d.ts +13 -0
- package/dist/tests/adapters-factory.test.d.ts.map +1 -0
- package/dist/tests/adapters-factory.test.js +308 -0
- package/dist/tests/adapters-factory.test.js.map +1 -0
- package/dist/tests/adapters-java.test.d.ts +13 -0
- package/dist/tests/adapters-java.test.d.ts.map +1 -0
- package/dist/tests/adapters-java.test.js +925 -0
- package/dist/tests/adapters-java.test.js.map +1 -0
- package/dist/tests/api/client.validation.test.d.ts +7 -0
- package/dist/tests/api/client.validation.test.d.ts.map +1 -0
- package/dist/tests/api/client.validation.test.js +183 -0
- package/dist/tests/api/client.validation.test.js.map +1 -0
- package/dist/tests/language-detector.test.d.ts +13 -0
- package/dist/tests/language-detector.test.d.ts.map +1 -0
- package/dist/tests/language-detector.test.js +674 -0
- package/dist/tests/language-detector.test.js.map +1 -0
- package/dist/tests/telemetry/posthog.test.d.ts +13 -0
- package/dist/tests/telemetry/posthog.test.d.ts.map +1 -0
- package/dist/tests/telemetry/posthog.test.js +600 -0
- package/dist/tests/telemetry/posthog.test.js.map +1 -0
- package/package.json +5 -6
- package/dist/src/security/RateLimiter.d.ts +0 -337
- package/dist/src/security/RateLimiter.d.ts.map +0 -1
- package/dist/src/security/RateLimiter.js +0 -782
- package/dist/src/security/RateLimiter.js.map +0 -1
- package/dist/src/security/scanner.d.ts +0 -151
- package/dist/src/security/scanner.d.ts.map +0 -1
- package/dist/src/security/scanner.js +0 -599
- package/dist/src/security/scanner.js.map +0 -1
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1308: Worker Thread Pool for Parallel File Parsing
|
|
3
|
+
* SMI-1337: Added metrics integration
|
|
4
|
+
*
|
|
5
|
+
* Uses Node.js worker_threads for true parallelism,
|
|
6
|
+
* bypassing the single-threaded event loop limitation.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/architecture/multi-language-analysis.md
|
|
9
|
+
* @module analysis/worker-pool
|
|
10
|
+
*/
|
|
11
|
+
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
import { EventEmitter } from 'events';
|
|
14
|
+
import { getAnalysisMetrics } from './metrics.js';
|
|
15
|
+
export class ParserWorkerPool extends EventEmitter {
|
|
16
|
+
workers = [];
|
|
17
|
+
taskQueue = [];
|
|
18
|
+
activeWorkers = 0;
|
|
19
|
+
poolSize;
|
|
20
|
+
minBatchForWorkers;
|
|
21
|
+
metrics;
|
|
22
|
+
disposed = false;
|
|
23
|
+
// SMI-1330/1331: Cache router to avoid recreation on each parseInline call
|
|
24
|
+
router = null;
|
|
25
|
+
routerPromise = null;
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
super();
|
|
28
|
+
this.poolSize = options.poolSize ?? Math.max(1, os.cpus().length - 1);
|
|
29
|
+
this.minBatchForWorkers = options.minBatchForWorkers ?? 10;
|
|
30
|
+
this.metrics = options.metrics ?? getAnalysisMetrics();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* SMI-1330/1331: Get or create shared router instance
|
|
34
|
+
* Lazily initializes the router on first use and caches it
|
|
35
|
+
*/
|
|
36
|
+
async getRouter() {
|
|
37
|
+
if (this.router)
|
|
38
|
+
return this.router;
|
|
39
|
+
if (this.routerPromise)
|
|
40
|
+
return this.routerPromise;
|
|
41
|
+
this.routerPromise = this.initializeRouter();
|
|
42
|
+
this.router = await this.routerPromise;
|
|
43
|
+
return this.router;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* SMI-1330/1331: Initialize router with all adapters
|
|
47
|
+
*/
|
|
48
|
+
async initializeRouter() {
|
|
49
|
+
const { LanguageRouter } = await import('./router.js');
|
|
50
|
+
const { TypeScriptAdapter } = await import('./adapters/typescript.js');
|
|
51
|
+
const { PythonAdapter } = await import('./adapters/python.js');
|
|
52
|
+
const { GoAdapter } = await import('./adapters/go.js');
|
|
53
|
+
const { RustAdapter } = await import('./adapters/rust.js');
|
|
54
|
+
const { JavaAdapter } = await import('./adapters/java.js');
|
|
55
|
+
const router = new LanguageRouter();
|
|
56
|
+
router.registerAdapter(new TypeScriptAdapter());
|
|
57
|
+
router.registerAdapter(new PythonAdapter());
|
|
58
|
+
router.registerAdapter(new GoAdapter());
|
|
59
|
+
router.registerAdapter(new RustAdapter());
|
|
60
|
+
router.registerAdapter(new JavaAdapter());
|
|
61
|
+
return router;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse files in parallel using worker threads
|
|
65
|
+
*
|
|
66
|
+
* SMI-1337: Records worker pool metrics.
|
|
67
|
+
*
|
|
68
|
+
* @param tasks - Array of parse tasks
|
|
69
|
+
* @returns Array of worker results
|
|
70
|
+
* @throws Error if pool has been disposed
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const results = await pool.parseFiles([
|
|
75
|
+
* { filePath: 'a.ts', content: 'export const a = 1', language: 'typescript' },
|
|
76
|
+
* { filePath: 'b.ts', content: 'export const b = 2', language: 'typescript' },
|
|
77
|
+
* ])
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
async parseFiles(tasks) {
|
|
81
|
+
if (this.disposed) {
|
|
82
|
+
throw new Error('Worker pool has been disposed');
|
|
83
|
+
}
|
|
84
|
+
if (tasks.length === 0) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
// SMI-1337: Update worker pool metrics
|
|
88
|
+
this.metrics.updateWorkerPool(this.activeWorkers, this.taskQueue.length, this.poolSize);
|
|
89
|
+
// For small batches, parse inline (worker overhead not worth it)
|
|
90
|
+
if (tasks.length < this.minBatchForWorkers) {
|
|
91
|
+
const results = await this.parseInline(tasks);
|
|
92
|
+
this.recordParseMetrics(results);
|
|
93
|
+
return results;
|
|
94
|
+
}
|
|
95
|
+
const results = await this.parseWithWorkers(tasks);
|
|
96
|
+
this.recordParseMetrics(results);
|
|
97
|
+
return results;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Record parse metrics for completed results
|
|
101
|
+
* SMI-1337: Helper to record metrics after parsing
|
|
102
|
+
*/
|
|
103
|
+
recordParseMetrics(results) {
|
|
104
|
+
for (const result of results) {
|
|
105
|
+
if (result.error) {
|
|
106
|
+
this.metrics.recordError('worker_parse_error', result.filePath.split('.').pop());
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Extract language from file extension
|
|
110
|
+
const ext = result.filePath.split('.').pop()?.toLowerCase();
|
|
111
|
+
const language = this.getLanguageFromExtension(ext);
|
|
112
|
+
if (language) {
|
|
113
|
+
this.metrics.recordFileParsed(language);
|
|
114
|
+
this.metrics.recordParseDuration(language, result.durationMs);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Update memory usage after batch processing
|
|
119
|
+
this.metrics.updateMemoryUsage();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get language from file extension
|
|
123
|
+
* SMI-1337: Helper for metrics
|
|
124
|
+
*/
|
|
125
|
+
getLanguageFromExtension(ext) {
|
|
126
|
+
if (!ext)
|
|
127
|
+
return undefined;
|
|
128
|
+
const extensionToLanguage = {
|
|
129
|
+
ts: 'typescript',
|
|
130
|
+
tsx: 'typescript',
|
|
131
|
+
mts: 'typescript',
|
|
132
|
+
cts: 'typescript',
|
|
133
|
+
js: 'javascript',
|
|
134
|
+
jsx: 'javascript',
|
|
135
|
+
mjs: 'javascript',
|
|
136
|
+
cjs: 'javascript',
|
|
137
|
+
py: 'python',
|
|
138
|
+
pyi: 'python',
|
|
139
|
+
pyw: 'python',
|
|
140
|
+
go: 'go',
|
|
141
|
+
rs: 'rust',
|
|
142
|
+
java: 'java',
|
|
143
|
+
};
|
|
144
|
+
return extensionToLanguage[ext];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Parse files inline (no workers)
|
|
148
|
+
*
|
|
149
|
+
* Used for small batches where worker overhead exceeds benefit.
|
|
150
|
+
* SMI-1330/1331: Uses cached router to avoid recreation overhead
|
|
151
|
+
*/
|
|
152
|
+
async parseInline(tasks) {
|
|
153
|
+
const results = [];
|
|
154
|
+
// SMI-1330/1331: Get shared router instance
|
|
155
|
+
const router = await this.getRouter();
|
|
156
|
+
for (const task of tasks) {
|
|
157
|
+
const start = performance.now();
|
|
158
|
+
try {
|
|
159
|
+
const adapter = router.tryGetAdapter(task.filePath);
|
|
160
|
+
if (adapter) {
|
|
161
|
+
const result = adapter.parseFile(task.content, task.filePath);
|
|
162
|
+
results.push({
|
|
163
|
+
filePath: task.filePath,
|
|
164
|
+
result,
|
|
165
|
+
durationMs: performance.now() - start,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Unsupported file type
|
|
170
|
+
results.push({
|
|
171
|
+
filePath: task.filePath,
|
|
172
|
+
result: { imports: [], exports: [], functions: [] },
|
|
173
|
+
durationMs: performance.now() - start,
|
|
174
|
+
error: `Unsupported file type: ${task.filePath}`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
results.push({
|
|
180
|
+
filePath: task.filePath,
|
|
181
|
+
result: { imports: [], exports: [], functions: [] },
|
|
182
|
+
durationMs: performance.now() - start,
|
|
183
|
+
error: error instanceof Error ? error.message : String(error),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return results;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Parse files using worker threads
|
|
191
|
+
*
|
|
192
|
+
* Chunks tasks across available workers for parallel processing.
|
|
193
|
+
*/
|
|
194
|
+
async parseWithWorkers(tasks) {
|
|
195
|
+
// Chunk tasks for workers
|
|
196
|
+
const chunkSize = Math.ceil(tasks.length / this.poolSize);
|
|
197
|
+
const chunks = this.chunkArray(tasks, chunkSize);
|
|
198
|
+
const results = await Promise.all(chunks.map((chunk) => this.dispatchToWorker(chunk)));
|
|
199
|
+
return results.flat();
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Dispatch a chunk of tasks to a worker
|
|
203
|
+
*/
|
|
204
|
+
async dispatchToWorker(tasks) {
|
|
205
|
+
return new Promise((resolve, reject) => {
|
|
206
|
+
// Create inline worker with basic regex-based parsing
|
|
207
|
+
// Full adapter-based parsing happens in main thread for accuracy
|
|
208
|
+
const workerCode = `
|
|
209
|
+
const { parentPort, workerData } = require('worker_threads');
|
|
210
|
+
|
|
211
|
+
function processTask(task) {
|
|
212
|
+
const start = Date.now();
|
|
213
|
+
try {
|
|
214
|
+
const result = {
|
|
215
|
+
imports: [],
|
|
216
|
+
exports: [],
|
|
217
|
+
functions: [],
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const lines = task.content.split('\\n');
|
|
221
|
+
for (let i = 0; i < lines.length; i++) {
|
|
222
|
+
const line = lines[i];
|
|
223
|
+
|
|
224
|
+
// Detect imports (TypeScript/JavaScript)
|
|
225
|
+
if (/^import\\s/.test(line) || /^from\\s/.test(line)) {
|
|
226
|
+
const moduleMatch = line.match(/from\\s+['"]([^'"]+)['"]/);
|
|
227
|
+
result.imports.push({
|
|
228
|
+
module: moduleMatch ? moduleMatch[1] : line.trim(),
|
|
229
|
+
namedImports: [],
|
|
230
|
+
isTypeOnly: /^import\\s+type/.test(line),
|
|
231
|
+
sourceFile: task.filePath,
|
|
232
|
+
line: i + 1,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Detect imports (Python)
|
|
237
|
+
if (/^import\\s+\\w/.test(line) || /^from\\s+\\w/.test(line)) {
|
|
238
|
+
const moduleMatch = line.match(/^(?:from\\s+)?(\\w+(?:\\.\\w+)*)/);
|
|
239
|
+
if (moduleMatch && !result.imports.some(imp => imp.line === i + 1)) {
|
|
240
|
+
result.imports.push({
|
|
241
|
+
module: moduleMatch[1],
|
|
242
|
+
namedImports: [],
|
|
243
|
+
isTypeOnly: false,
|
|
244
|
+
sourceFile: task.filePath,
|
|
245
|
+
line: i + 1,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Detect imports (Go)
|
|
251
|
+
if (/^\\s*"[^"]+"/.test(line) || /^import\\s+/.test(line)) {
|
|
252
|
+
const pathMatch = line.match(/"([^"]+)"/);
|
|
253
|
+
if (pathMatch) {
|
|
254
|
+
result.imports.push({
|
|
255
|
+
module: pathMatch[1],
|
|
256
|
+
namedImports: [],
|
|
257
|
+
isTypeOnly: false,
|
|
258
|
+
sourceFile: task.filePath,
|
|
259
|
+
line: i + 1,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Detect functions (TypeScript/JavaScript)
|
|
265
|
+
const tsFuncMatch = line.match(/^(export\\s+)?(async\\s+)?function\\s+(\\w+)/);
|
|
266
|
+
if (tsFuncMatch) {
|
|
267
|
+
result.functions.push({
|
|
268
|
+
name: tsFuncMatch[3],
|
|
269
|
+
parameterCount: 0,
|
|
270
|
+
isAsync: !!tsFuncMatch[2],
|
|
271
|
+
isExported: !!tsFuncMatch[1],
|
|
272
|
+
sourceFile: task.filePath,
|
|
273
|
+
line: i + 1,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Detect functions (Python)
|
|
278
|
+
const pyFuncMatch = line.match(/^(async\\s+)?def\\s+(\\w+)/);
|
|
279
|
+
if (pyFuncMatch) {
|
|
280
|
+
result.functions.push({
|
|
281
|
+
name: pyFuncMatch[2],
|
|
282
|
+
parameterCount: 0,
|
|
283
|
+
isAsync: !!pyFuncMatch[1],
|
|
284
|
+
isExported: !pyFuncMatch[2].startsWith('_'),
|
|
285
|
+
sourceFile: task.filePath,
|
|
286
|
+
line: i + 1,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Detect functions (Go)
|
|
291
|
+
const goFuncMatch = line.match(/^func\\s+(?:\\([^)]+\\)\\s+)?(\\w+)/);
|
|
292
|
+
if (goFuncMatch) {
|
|
293
|
+
const isExported = goFuncMatch[1][0] === goFuncMatch[1][0].toUpperCase();
|
|
294
|
+
result.functions.push({
|
|
295
|
+
name: goFuncMatch[1],
|
|
296
|
+
parameterCount: 0,
|
|
297
|
+
isAsync: false,
|
|
298
|
+
isExported: isExported,
|
|
299
|
+
sourceFile: task.filePath,
|
|
300
|
+
line: i + 1,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Detect exports (TypeScript/JavaScript)
|
|
305
|
+
if (/^export\\s+(default\\s+)?(const|let|var|class|function|interface|type|enum)/.test(line)) {
|
|
306
|
+
const exportMatch = line.match(/^export\\s+(default\\s+)?(const|let|var|class|function|interface|type|enum)\\s+(\\w+)/);
|
|
307
|
+
if (exportMatch) {
|
|
308
|
+
result.exports.push({
|
|
309
|
+
name: exportMatch[3],
|
|
310
|
+
kind: exportMatch[2] === 'function' ? 'function' :
|
|
311
|
+
exportMatch[2] === 'class' ? 'class' :
|
|
312
|
+
exportMatch[2] === 'interface' ? 'interface' :
|
|
313
|
+
exportMatch[2] === 'type' ? 'type' :
|
|
314
|
+
exportMatch[2] === 'enum' ? 'enum' : 'variable',
|
|
315
|
+
isDefault: !!exportMatch[1],
|
|
316
|
+
sourceFile: task.filePath,
|
|
317
|
+
line: i + 1,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
filePath: task.filePath,
|
|
325
|
+
result,
|
|
326
|
+
durationMs: Date.now() - start,
|
|
327
|
+
};
|
|
328
|
+
} catch (error) {
|
|
329
|
+
return {
|
|
330
|
+
filePath: task.filePath,
|
|
331
|
+
result: { imports: [], exports: [], functions: [] },
|
|
332
|
+
durationMs: Date.now() - start,
|
|
333
|
+
error: error.message || String(error),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const results = workerData.tasks.map(processTask);
|
|
339
|
+
parentPort.postMessage(results);
|
|
340
|
+
`;
|
|
341
|
+
const worker = new Worker(workerCode, {
|
|
342
|
+
eval: true,
|
|
343
|
+
workerData: { tasks },
|
|
344
|
+
});
|
|
345
|
+
const timeout = setTimeout(() => {
|
|
346
|
+
worker.terminate();
|
|
347
|
+
reject(new Error('Worker timed out after 30 seconds'));
|
|
348
|
+
}, 30000);
|
|
349
|
+
worker.on('message', (results) => {
|
|
350
|
+
clearTimeout(timeout);
|
|
351
|
+
worker.terminate();
|
|
352
|
+
resolve(results);
|
|
353
|
+
});
|
|
354
|
+
worker.on('error', (error) => {
|
|
355
|
+
clearTimeout(timeout);
|
|
356
|
+
worker.terminate();
|
|
357
|
+
reject(error);
|
|
358
|
+
});
|
|
359
|
+
worker.on('exit', (code) => {
|
|
360
|
+
clearTimeout(timeout);
|
|
361
|
+
if (code !== 0) {
|
|
362
|
+
reject(new Error(`Worker exited with code ${code}`));
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Chunk an array into smaller arrays
|
|
369
|
+
*/
|
|
370
|
+
chunkArray(array, size) {
|
|
371
|
+
const chunks = [];
|
|
372
|
+
for (let i = 0; i < array.length; i += size) {
|
|
373
|
+
chunks.push(array.slice(i, i + size));
|
|
374
|
+
}
|
|
375
|
+
return chunks;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get pool statistics
|
|
379
|
+
*
|
|
380
|
+
* SMI-1337: Also updates metrics.
|
|
381
|
+
*
|
|
382
|
+
* @returns Current pool statistics
|
|
383
|
+
*/
|
|
384
|
+
getStats() {
|
|
385
|
+
const utilization = this.poolSize > 0 ? this.activeWorkers / this.poolSize : 0;
|
|
386
|
+
// SMI-1337: Update metrics when stats are requested
|
|
387
|
+
this.metrics.updateWorkerPool(this.activeWorkers, this.taskQueue.length, this.poolSize);
|
|
388
|
+
return {
|
|
389
|
+
poolSize: this.poolSize,
|
|
390
|
+
activeWorkers: this.activeWorkers,
|
|
391
|
+
queuedTasks: this.taskQueue.length,
|
|
392
|
+
utilization,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Dispose of worker pool
|
|
397
|
+
*
|
|
398
|
+
* Terminates all workers and clears the task queue.
|
|
399
|
+
* SMI-1330/1331: Also disposes cached router
|
|
400
|
+
*/
|
|
401
|
+
dispose() {
|
|
402
|
+
this.disposed = true;
|
|
403
|
+
for (const worker of this.workers) {
|
|
404
|
+
worker.terminate();
|
|
405
|
+
}
|
|
406
|
+
this.workers = [];
|
|
407
|
+
this.taskQueue = [];
|
|
408
|
+
// SMI-1330/1331: Clean up cached router
|
|
409
|
+
if (this.router) {
|
|
410
|
+
this.router.dispose();
|
|
411
|
+
this.router = null;
|
|
412
|
+
this.routerPromise = null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// Export for worker thread context detection
|
|
417
|
+
export { isMainThread, parentPort, workerData };
|
|
418
|
+
//# sourceMappingURL=worker-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-pool.js","sourceRoot":"","sources":["../../../src/analysis/worker-pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
/**
|
|
12
12
|
* Analytics schema SQL for skill usage, A/B testing, and ROI tracking
|
|
13
13
|
*/
|
|
14
|
-
export declare const ANALYTICS_SCHEMA = "\n-- Skill usage events for attribution and value tracking\nCREATE TABLE IF NOT EXISTS skill_usage_events (\n id TEXT PRIMARY KEY,\n skill_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n session_id TEXT NOT NULL,\n event_type TEXT NOT NULL CHECK(event_type IN ('activation', 'invocation', 'success', 'failure')),\n context TEXT, -- JSON metadata about the usage context\n value_score REAL, -- Estimated value contribution (0-1)\n timestamp TEXT NOT NULL DEFAULT (datetime('now')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for usage queries\nCREATE INDEX IF NOT EXISTS idx_usage_skill_id ON skill_usage_events(skill_id);\nCREATE INDEX IF NOT EXISTS idx_usage_user_id ON skill_usage_events(user_id);\nCREATE INDEX IF NOT EXISTS idx_usage_session_id ON skill_usage_events(session_id);\nCREATE INDEX IF NOT EXISTS idx_usage_timestamp ON skill_usage_events(timestamp);\nCREATE INDEX IF NOT EXISTS idx_usage_event_type ON skill_usage_events(event_type);\n\n-- A/B testing experiments\nCREATE TABLE IF NOT EXISTS experiments (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n hypothesis TEXT,\n status TEXT NOT NULL CHECK(status IN ('draft', 'active', 'paused', 'completed')) DEFAULT 'draft',\n variant_a TEXT NOT NULL, -- JSON config for control group\n variant_b TEXT NOT NULL, -- JSON config for treatment group\n start_date TEXT,\n end_date TEXT,\n target_sample_size INTEGER DEFAULT 100,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- User assignments to experiment variants\nCREATE TABLE IF NOT EXISTS experiment_assignments (\n id TEXT PRIMARY KEY,\n experiment_id TEXT NOT NULL REFERENCES experiments(id) ON DELETE CASCADE,\n user_id TEXT NOT NULL,\n variant TEXT NOT NULL CHECK(variant IN ('control', 'treatment')),\n assigned_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(experiment_id, user_id)\n);\n\n-- Experiment outcome tracking\nCREATE TABLE IF NOT EXISTS experiment_outcomes (\n id TEXT PRIMARY KEY,\n experiment_id TEXT NOT NULL REFERENCES experiments(id) ON DELETE CASCADE,\n assignment_id TEXT NOT NULL REFERENCES experiment_assignments(id) ON DELETE CASCADE,\n outcome_type TEXT NOT NULL, -- e.g., 'activation', 'usage_count', 'value_score'\n outcome_value REAL NOT NULL,\n metadata TEXT, -- JSON additional data\n measured_at TEXT NOT NULL DEFAULT (datetime('now')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for experiment queries\nCREATE INDEX IF NOT EXISTS idx_assignments_experiment ON experiment_assignments(experiment_id);\nCREATE INDEX IF NOT EXISTS idx_assignments_user ON experiment_assignments(user_id);\nCREATE INDEX IF NOT EXISTS idx_outcomes_experiment ON experiment_outcomes(experiment_id);\nCREATE INDEX IF NOT EXISTS idx_outcomes_assignment ON experiment_outcomes(assignment_id);\n\n-- ROI metrics aggregation (materialized view equivalent)\nCREATE TABLE IF NOT EXISTS roi_metrics (\n id TEXT PRIMARY KEY,\n metric_type TEXT NOT NULL, -- 'daily', 'weekly', 'monthly', 'user', 'skill'\n entity_id TEXT, -- user_id or skill_id for entity-level metrics\n period_start TEXT NOT NULL,\n period_end TEXT NOT NULL,\n total_activations INTEGER DEFAULT 0,\n total_invocations INTEGER DEFAULT 0,\n total_successes INTEGER DEFAULT 0,\n total_failures INTEGER DEFAULT 0,\n avg_value_score REAL DEFAULT 0.0,\n estimated_time_saved REAL DEFAULT 0.0, -- in minutes\n estimated_value_usd REAL DEFAULT 0.0, -- rough ROI estimate\n metadata TEXT, -- JSON for additional metrics\n computed_at TEXT NOT NULL DEFAULT (datetime('now')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for ROI queries\nCREATE INDEX IF NOT EXISTS idx_roi_type ON roi_metrics(metric_type);\nCREATE INDEX IF NOT EXISTS idx_roi_entity ON roi_metrics(entity_id);\nCREATE INDEX IF NOT EXISTS idx_roi_period ON roi_metrics(period_start, period_end);\nCREATE INDEX IF NOT EXISTS idx_roi_computed ON roi_metrics(computed_at);\n\n-- Value attribution mappings\nCREATE TABLE IF NOT EXISTS value_attributions (\n id TEXT PRIMARY KEY,\n usage_event_id TEXT NOT NULL REFERENCES skill_usage_events(id) ON DELETE CASCADE,\n skill_id TEXT NOT NULL,\n attribution_type TEXT NOT NULL, -- 'inline', 'metadata', 'session'\n value_dimension TEXT NOT NULL, -- 'time_saved', 'quality_improved', 'error_prevented'\n value_amount REAL NOT NULL,\n confidence REAL NOT NULL CHECK(confidence >= 0 AND confidence <= 1),\n metadata TEXT, -- JSON additional context\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for attribution queries\nCREATE INDEX IF NOT EXISTS idx_attributions_event ON value_attributions(usage_event_id);\nCREATE INDEX IF NOT EXISTS idx_attributions_skill ON value_attributions(skill_id);\nCREATE INDEX IF NOT EXISTS idx_attributions_type ON value_attributions(attribution_type);\n";
|
|
14
|
+
export declare const ANALYTICS_SCHEMA = "\n-- Skill usage events for attribution and value tracking\nCREATE TABLE IF NOT EXISTS skill_usage_events (\n id TEXT PRIMARY KEY,\n skill_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n session_id TEXT NOT NULL,\n event_type TEXT NOT NULL CHECK(event_type IN ('activation', 'invocation', 'success', 'failure')),\n context TEXT, -- JSON metadata about the usage context\n value_score REAL, -- Estimated value contribution (0-1)\n timestamp TEXT NOT NULL DEFAULT (datetime('now')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for usage queries\nCREATE INDEX IF NOT EXISTS idx_usage_skill_id ON skill_usage_events(skill_id);\nCREATE INDEX IF NOT EXISTS idx_usage_user_id ON skill_usage_events(user_id);\nCREATE INDEX IF NOT EXISTS idx_usage_session_id ON skill_usage_events(session_id);\nCREATE INDEX IF NOT EXISTS idx_usage_timestamp ON skill_usage_events(timestamp);\nCREATE INDEX IF NOT EXISTS idx_usage_event_type ON skill_usage_events(event_type);\n\n-- A/B testing experiments\nCREATE TABLE IF NOT EXISTS experiments (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n description TEXT,\n hypothesis TEXT,\n status TEXT NOT NULL CHECK(status IN ('draft', 'active', 'paused', 'completed')) DEFAULT 'draft',\n variant_a TEXT NOT NULL, -- JSON config for control group\n variant_b TEXT NOT NULL, -- JSON config for treatment group\n start_date TEXT,\n end_date TEXT,\n target_sample_size INTEGER DEFAULT 100,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- User assignments to experiment variants\nCREATE TABLE IF NOT EXISTS experiment_assignments (\n id TEXT PRIMARY KEY,\n experiment_id TEXT NOT NULL REFERENCES experiments(id) ON DELETE CASCADE,\n user_id TEXT NOT NULL,\n variant TEXT NOT NULL CHECK(variant IN ('control', 'treatment')),\n assigned_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(experiment_id, user_id)\n);\n\n-- Experiment outcome tracking\nCREATE TABLE IF NOT EXISTS experiment_outcomes (\n id TEXT PRIMARY KEY,\n experiment_id TEXT NOT NULL REFERENCES experiments(id) ON DELETE CASCADE,\n assignment_id TEXT NOT NULL REFERENCES experiment_assignments(id) ON DELETE CASCADE,\n outcome_type TEXT NOT NULL, -- e.g., 'activation', 'usage_count', 'value_score'\n outcome_value REAL NOT NULL,\n metadata TEXT, -- JSON additional data\n measured_at TEXT NOT NULL DEFAULT (datetime('now')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for experiment queries\nCREATE INDEX IF NOT EXISTS idx_assignments_experiment ON experiment_assignments(experiment_id);\nCREATE INDEX IF NOT EXISTS idx_assignments_user ON experiment_assignments(user_id);\nCREATE INDEX IF NOT EXISTS idx_outcomes_experiment ON experiment_outcomes(experiment_id);\nCREATE INDEX IF NOT EXISTS idx_outcomes_assignment ON experiment_outcomes(assignment_id);\n\n-- ROI metrics aggregation (materialized view equivalent)\nCREATE TABLE IF NOT EXISTS roi_metrics (\n id TEXT PRIMARY KEY,\n metric_type TEXT NOT NULL, -- 'daily', 'weekly', 'monthly', 'user', 'skill'\n entity_id TEXT, -- user_id or skill_id for entity-level metrics\n period_start TEXT NOT NULL,\n period_end TEXT NOT NULL,\n total_activations INTEGER DEFAULT 0,\n total_invocations INTEGER DEFAULT 0,\n total_successes INTEGER DEFAULT 0,\n total_failures INTEGER DEFAULT 0,\n avg_value_score REAL DEFAULT 0.0,\n estimated_time_saved REAL DEFAULT 0.0, -- in minutes\n estimated_value_usd REAL DEFAULT 0.0, -- rough ROI estimate\n metadata TEXT, -- JSON for additional metrics\n computed_at TEXT NOT NULL DEFAULT (datetime('now')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for ROI queries\nCREATE INDEX IF NOT EXISTS idx_roi_type ON roi_metrics(metric_type);\nCREATE INDEX IF NOT EXISTS idx_roi_entity ON roi_metrics(entity_id);\nCREATE INDEX IF NOT EXISTS idx_roi_period ON roi_metrics(period_start, period_end);\nCREATE INDEX IF NOT EXISTS idx_roi_computed ON roi_metrics(computed_at);\n\n-- Value attribution mappings\nCREATE TABLE IF NOT EXISTS value_attributions (\n id TEXT PRIMARY KEY,\n usage_event_id TEXT NOT NULL REFERENCES skill_usage_events(id) ON DELETE CASCADE,\n skill_id TEXT NOT NULL,\n attribution_type TEXT NOT NULL, -- 'inline', 'metadata', 'session'\n value_dimension TEXT NOT NULL, -- 'time_saved', 'quality_improved', 'error_prevented'\n value_amount REAL NOT NULL,\n confidence REAL NOT NULL CHECK(confidence >= 0 AND confidence <= 1),\n metadata TEXT, -- JSON additional context\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for attribution queries\nCREATE INDEX IF NOT EXISTS idx_attributions_event ON value_attributions(usage_event_id);\nCREATE INDEX IF NOT EXISTS idx_attributions_skill ON value_attributions(skill_id);\nCREATE INDEX IF NOT EXISTS idx_attributions_type ON value_attributions(attribution_type);\n\n-- ============================================================================\n-- Quota Management Tables (SMI-XXXX)\n-- ============================================================================\n\n-- Monthly usage quotas per customer/license\n-- Tracks API call usage against tier limits\nCREATE TABLE IF NOT EXISTS usage_quotas (\n id TEXT PRIMARY KEY,\n customer_id TEXT NOT NULL,\n license_tier TEXT NOT NULL CHECK(license_tier IN ('community', 'individual', 'team', 'enterprise')),\n billing_period_start TEXT NOT NULL,\n billing_period_end TEXT NOT NULL,\n api_calls_limit INTEGER NOT NULL,\n api_calls_used INTEGER DEFAULT 0,\n last_warning_threshold INTEGER DEFAULT 0, -- 0, 80, 90, or 100\n last_warning_sent_at TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n UNIQUE(customer_id, billing_period_start)\n);\n\n-- Indexes for quota queries\nCREATE INDEX IF NOT EXISTS idx_quotas_customer ON usage_quotas(customer_id);\nCREATE INDEX IF NOT EXISTS idx_quotas_period ON usage_quotas(billing_period_start, billing_period_end);\nCREATE INDEX IF NOT EXISTS idx_quotas_tier ON usage_quotas(license_tier);\n\n-- Individual API call events for detailed tracking\n-- Used for quota enforcement and analytics\nCREATE TABLE IF NOT EXISTS api_call_events (\n id TEXT PRIMARY KEY,\n customer_id TEXT NOT NULL,\n license_key_hash TEXT, -- SHA256 hash of license key for lookup\n tool_name TEXT NOT NULL,\n endpoint TEXT,\n cost INTEGER DEFAULT 1, -- Some operations may cost multiple quota units\n success INTEGER DEFAULT 1, -- 1 for success, 0 for failure\n latency_ms INTEGER,\n session_id TEXT,\n metadata TEXT, -- JSON for additional context\n timestamp TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for API call queries\nCREATE INDEX IF NOT EXISTS idx_api_calls_customer ON api_call_events(customer_id);\nCREATE INDEX IF NOT EXISTS idx_api_calls_timestamp ON api_call_events(timestamp);\nCREATE INDEX IF NOT EXISTS idx_api_calls_license ON api_call_events(license_key_hash);\nCREATE INDEX IF NOT EXISTS idx_api_calls_tool ON api_call_events(tool_name);\n\n-- User subscriptions for billing integration\n-- Links customers to Stripe subscriptions\nCREATE TABLE IF NOT EXISTS user_subscriptions (\n id TEXT PRIMARY KEY,\n customer_id TEXT NOT NULL UNIQUE,\n email TEXT NOT NULL,\n tier TEXT NOT NULL CHECK(tier IN ('community', 'individual', 'team', 'enterprise')),\n stripe_customer_id TEXT,\n stripe_subscription_id TEXT,\n status TEXT NOT NULL CHECK(status IN ('active', 'past_due', 'canceled', 'trialing', 'paused')),\n current_period_start TEXT,\n current_period_end TEXT,\n last_active_at TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\n-- Indexes for subscription queries\nCREATE INDEX IF NOT EXISTS idx_subs_customer ON user_subscriptions(customer_id);\nCREATE INDEX IF NOT EXISTS idx_subs_stripe ON user_subscriptions(stripe_customer_id);\nCREATE INDEX IF NOT EXISTS idx_subs_status ON user_subscriptions(status);\nCREATE INDEX IF NOT EXISTS idx_subs_tier ON user_subscriptions(tier);\nCREATE INDEX IF NOT EXISTS idx_subs_last_active ON user_subscriptions(last_active_at);\n";
|
|
15
15
|
/**
|
|
16
16
|
* Apply analytics schema to a database
|
|
17
17
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/analytics/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/analytics/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB,4gQAmL5B,CAAA;AAED;;GAEG;AACH,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE9D,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAEhE"}
|
|
@@ -118,6 +118,78 @@ CREATE TABLE IF NOT EXISTS value_attributions (
|
|
|
118
118
|
CREATE INDEX IF NOT EXISTS idx_attributions_event ON value_attributions(usage_event_id);
|
|
119
119
|
CREATE INDEX IF NOT EXISTS idx_attributions_skill ON value_attributions(skill_id);
|
|
120
120
|
CREATE INDEX IF NOT EXISTS idx_attributions_type ON value_attributions(attribution_type);
|
|
121
|
+
|
|
122
|
+
-- ============================================================================
|
|
123
|
+
-- Quota Management Tables (SMI-XXXX)
|
|
124
|
+
-- ============================================================================
|
|
125
|
+
|
|
126
|
+
-- Monthly usage quotas per customer/license
|
|
127
|
+
-- Tracks API call usage against tier limits
|
|
128
|
+
CREATE TABLE IF NOT EXISTS usage_quotas (
|
|
129
|
+
id TEXT PRIMARY KEY,
|
|
130
|
+
customer_id TEXT NOT NULL,
|
|
131
|
+
license_tier TEXT NOT NULL CHECK(license_tier IN ('community', 'individual', 'team', 'enterprise')),
|
|
132
|
+
billing_period_start TEXT NOT NULL,
|
|
133
|
+
billing_period_end TEXT NOT NULL,
|
|
134
|
+
api_calls_limit INTEGER NOT NULL,
|
|
135
|
+
api_calls_used INTEGER DEFAULT 0,
|
|
136
|
+
last_warning_threshold INTEGER DEFAULT 0, -- 0, 80, 90, or 100
|
|
137
|
+
last_warning_sent_at TEXT,
|
|
138
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
139
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
140
|
+
UNIQUE(customer_id, billing_period_start)
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
-- Indexes for quota queries
|
|
144
|
+
CREATE INDEX IF NOT EXISTS idx_quotas_customer ON usage_quotas(customer_id);
|
|
145
|
+
CREATE INDEX IF NOT EXISTS idx_quotas_period ON usage_quotas(billing_period_start, billing_period_end);
|
|
146
|
+
CREATE INDEX IF NOT EXISTS idx_quotas_tier ON usage_quotas(license_tier);
|
|
147
|
+
|
|
148
|
+
-- Individual API call events for detailed tracking
|
|
149
|
+
-- Used for quota enforcement and analytics
|
|
150
|
+
CREATE TABLE IF NOT EXISTS api_call_events (
|
|
151
|
+
id TEXT PRIMARY KEY,
|
|
152
|
+
customer_id TEXT NOT NULL,
|
|
153
|
+
license_key_hash TEXT, -- SHA256 hash of license key for lookup
|
|
154
|
+
tool_name TEXT NOT NULL,
|
|
155
|
+
endpoint TEXT,
|
|
156
|
+
cost INTEGER DEFAULT 1, -- Some operations may cost multiple quota units
|
|
157
|
+
success INTEGER DEFAULT 1, -- 1 for success, 0 for failure
|
|
158
|
+
latency_ms INTEGER,
|
|
159
|
+
session_id TEXT,
|
|
160
|
+
metadata TEXT, -- JSON for additional context
|
|
161
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
-- Indexes for API call queries
|
|
165
|
+
CREATE INDEX IF NOT EXISTS idx_api_calls_customer ON api_call_events(customer_id);
|
|
166
|
+
CREATE INDEX IF NOT EXISTS idx_api_calls_timestamp ON api_call_events(timestamp);
|
|
167
|
+
CREATE INDEX IF NOT EXISTS idx_api_calls_license ON api_call_events(license_key_hash);
|
|
168
|
+
CREATE INDEX IF NOT EXISTS idx_api_calls_tool ON api_call_events(tool_name);
|
|
169
|
+
|
|
170
|
+
-- User subscriptions for billing integration
|
|
171
|
+
-- Links customers to Stripe subscriptions
|
|
172
|
+
CREATE TABLE IF NOT EXISTS user_subscriptions (
|
|
173
|
+
id TEXT PRIMARY KEY,
|
|
174
|
+
customer_id TEXT NOT NULL UNIQUE,
|
|
175
|
+
email TEXT NOT NULL,
|
|
176
|
+
tier TEXT NOT NULL CHECK(tier IN ('community', 'individual', 'team', 'enterprise')),
|
|
177
|
+
stripe_customer_id TEXT,
|
|
178
|
+
stripe_subscription_id TEXT,
|
|
179
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'past_due', 'canceled', 'trialing', 'paused')),
|
|
180
|
+
current_period_start TEXT,
|
|
181
|
+
current_period_end TEXT,
|
|
182
|
+
last_active_at TEXT,
|
|
183
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
184
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
-- Indexes for subscription queries
|
|
188
|
+
CREATE INDEX IF NOT EXISTS idx_subs_customer ON user_subscriptions(customer_id);
|
|
189
|
+
CREATE INDEX IF NOT EXISTS idx_subs_stripe ON user_subscriptions(stripe_customer_id);
|
|
190
|
+
CREATE INDEX IF NOT EXISTS idx_subs_status ON user_subscriptions(status);
|
|
191
|
+
CREATE INDEX IF NOT EXISTS idx_subs_tier ON user_subscriptions(tier);
|
|
192
|
+
CREATE INDEX IF NOT EXISTS idx_subs_last_active ON user_subscriptions(last_active_at);
|
|
121
193
|
`;
|
|
122
194
|
export function initializeAnalyticsSchema(db) {
|
|
123
195
|
db.exec(ANALYTICS_SCHEMA);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/analytics/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/analytics/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmL/B,CAAA;AAOD,MAAM,UAAU,yBAAyB,CAAC,EAAgB;IACxD,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;AAC3B,CAAC"}
|
package/dist/src/api/cache.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export declare class ApiCache {
|
|
|
54
54
|
private hits;
|
|
55
55
|
private misses;
|
|
56
56
|
private evictions;
|
|
57
|
+
private operationCount;
|
|
58
|
+
private readonly PRUNE_INTERVAL;
|
|
57
59
|
constructor(config?: CacheConfig);
|
|
58
60
|
/**
|
|
59
61
|
* Generate cache key from endpoint and parameters
|
|
@@ -84,13 +86,34 @@ export declare class ApiCache {
|
|
|
84
86
|
*/
|
|
85
87
|
prune(): number;
|
|
86
88
|
/**
|
|
87
|
-
* Invalidate entries matching a pattern
|
|
89
|
+
* Invalidate entries matching a pattern.
|
|
90
|
+
*
|
|
91
|
+
* When a string is provided, it is treated as a literal string and
|
|
92
|
+
* special RegExp characters are escaped to prevent RegExp injection.
|
|
93
|
+
* To use regex patterns, pass a RegExp object directly.
|
|
94
|
+
*
|
|
95
|
+
* @param pattern - Literal string to match or RegExp for pattern matching
|
|
96
|
+
* @returns Number of entries invalidated
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* // Literal string matching (special chars are escaped)
|
|
101
|
+
* cache.invalidatePattern('search:user.name') // Matches exactly 'search:user.name'
|
|
102
|
+
*
|
|
103
|
+
* // RegExp for pattern matching
|
|
104
|
+
* cache.invalidatePattern(/^search:/) // Matches all keys starting with 'search:'
|
|
105
|
+
* ```
|
|
88
106
|
*/
|
|
89
107
|
invalidatePattern(pattern: string | RegExp): number;
|
|
90
108
|
/**
|
|
91
109
|
* Get cache statistics
|
|
92
110
|
*/
|
|
93
111
|
getStats(): CacheStats;
|
|
112
|
+
/**
|
|
113
|
+
* Automatically prune expired entries every PRUNE_INTERVAL operations.
|
|
114
|
+
* SMI-1262: Prevents expired entries from accumulating until maxEntries is reached.
|
|
115
|
+
*/
|
|
116
|
+
private maybeAutoPrune;
|
|
94
117
|
/**
|
|
95
118
|
* Evict least recently used entries
|
|
96
119
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/api/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/api/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiCH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAI9C,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,WAAW,CAAS;IAG5B,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,SAAS,CAAI;IAGrB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAM;gBAEzB,MAAM,GAAE,WAAgB;IAOpC;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;IAa5E;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAuBlC;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,OAAO,WAAW,GAAG,IAAI;IAmB3E;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAUzB;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,KAAK,IAAI,MAAM;IAcf;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAcnD;;OAEG;IACH,QAAQ,IAAI,UAAU;IAWtB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAMtB;;OAEG;IACH,OAAO,CAAC,cAAc;CAwBvB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,QAAQ,CAE1D;AAOD;;GAEG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAKzC;AAED,eAAe,QAAQ,CAAA"}
|