@timmeck/brain-core 2.2.0 → 2.4.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.
@@ -1,3 +1,4 @@
1
+ // ── Rate Limiter ────────────────────────────────────────
1
2
  export class RateLimiter {
2
3
  store = new Map();
3
4
  windowMs;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/api/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/api/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface BackupConfig {
3
+ backupDir?: string;
4
+ maxBackups?: number;
5
+ compress?: boolean;
6
+ }
7
+ export interface BackupRecord {
8
+ filename: string;
9
+ path: string;
10
+ size: number;
11
+ created: string;
12
+ }
13
+ export interface RestoreResult {
14
+ success: boolean;
15
+ filename: string;
16
+ integrityOk: boolean;
17
+ }
18
+ export declare class BackupService {
19
+ private db;
20
+ private dbPath;
21
+ private logger;
22
+ private backupDir;
23
+ private maxBackups;
24
+ constructor(db: Database.Database, dbPath: string, config?: BackupConfig);
25
+ /** Create a backup of the current database. */
26
+ create(label?: string): BackupRecord;
27
+ /** List all available backups (newest first). */
28
+ list(): BackupRecord[];
29
+ /**
30
+ * Restore a database from a backup.
31
+ * CAUTION: This replaces the current database. The caller must close and reopen the DB connection.
32
+ */
33
+ restore(filename: string): RestoreResult;
34
+ /** Check integrity of a database file. */
35
+ checkIntegrity(dbFilePath?: string): boolean;
36
+ /** Get current database size info. */
37
+ getInfo(): {
38
+ dbSize: number;
39
+ walSize: number;
40
+ backupCount: number;
41
+ backupTotalSize: number;
42
+ };
43
+ /** Delete a specific backup. */
44
+ delete(filename: string): boolean;
45
+ /** Auto-cleanup oldest backups beyond maxBackups. */
46
+ private autoCleanup;
47
+ private formatSize;
48
+ }
@@ -0,0 +1,153 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { getLogger } from '../utils/logger.js';
5
+ const require = createRequire(import.meta.url);
6
+ // ── Service ─────────────────────────────────────────────
7
+ export class BackupService {
8
+ db;
9
+ dbPath;
10
+ logger = getLogger();
11
+ backupDir;
12
+ maxBackups;
13
+ constructor(db, dbPath, config) {
14
+ this.db = db;
15
+ this.dbPath = dbPath;
16
+ this.backupDir = config?.backupDir ?? path.join(path.dirname(dbPath), 'backups');
17
+ this.maxBackups = config?.maxBackups ?? 10;
18
+ fs.mkdirSync(this.backupDir, { recursive: true });
19
+ }
20
+ /** Create a backup of the current database. */
21
+ create(label) {
22
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
23
+ const suffix = label ? `-${label.replace(/[^a-zA-Z0-9-_]/g, '')}` : '';
24
+ const filename = `backup-${timestamp}${suffix}.db`;
25
+ const backupPath = path.join(this.backupDir, filename);
26
+ // Checkpoint WAL before copying for a consistent snapshot
27
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
28
+ // Copy DB file synchronously
29
+ fs.copyFileSync(this.dbPath, backupPath);
30
+ // Verify integrity
31
+ const integrityOk = this.checkIntegrity(backupPath);
32
+ if (!integrityOk) {
33
+ this.logger.warn(`Backup integrity check failed: ${filename}`);
34
+ fs.unlinkSync(backupPath);
35
+ throw new Error('Backup integrity check failed');
36
+ }
37
+ this.logger.info(`Backup created: ${filename} (${this.formatSize(fs.statSync(backupPath).size)})`);
38
+ // Auto-cleanup old backups
39
+ this.autoCleanup();
40
+ const stat = fs.statSync(backupPath);
41
+ return {
42
+ filename,
43
+ path: backupPath,
44
+ size: stat.size,
45
+ created: stat.mtime.toISOString(),
46
+ };
47
+ }
48
+ /** List all available backups (newest first). */
49
+ list() {
50
+ if (!fs.existsSync(this.backupDir))
51
+ return [];
52
+ const files = fs.readdirSync(this.backupDir)
53
+ .filter(f => f.startsWith('backup-') && f.endsWith('.db'))
54
+ .sort()
55
+ .reverse();
56
+ return files.map(filename => {
57
+ const filePath = path.join(this.backupDir, filename);
58
+ const stat = fs.statSync(filePath);
59
+ return {
60
+ filename,
61
+ path: filePath,
62
+ size: stat.size,
63
+ created: stat.mtime.toISOString(),
64
+ };
65
+ });
66
+ }
67
+ /**
68
+ * Restore a database from a backup.
69
+ * CAUTION: This replaces the current database. The caller must close and reopen the DB connection.
70
+ */
71
+ restore(filename) {
72
+ const backupPath = path.join(this.backupDir, filename);
73
+ if (!fs.existsSync(backupPath)) {
74
+ throw new Error(`Backup not found: ${filename}`);
75
+ }
76
+ // Verify backup integrity before restore
77
+ const integrityOk = this.checkIntegrity(backupPath);
78
+ if (!integrityOk) {
79
+ throw new Error(`Backup integrity check failed: ${filename}`);
80
+ }
81
+ // Create a safety backup of current DB before overwriting
82
+ const safetyName = `pre-restore-${new Date().toISOString().replace(/[:.]/g, '-')}.db`;
83
+ const safetyPath = path.join(this.backupDir, safetyName);
84
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
85
+ fs.copyFileSync(this.dbPath, safetyPath);
86
+ this.logger.info(`Safety backup created: ${safetyName}`);
87
+ // Close current DB, copy backup over, caller must reopen
88
+ this.db.close();
89
+ fs.copyFileSync(backupPath, this.dbPath);
90
+ // Clean up WAL/SHM files if they exist
91
+ try {
92
+ fs.unlinkSync(this.dbPath + '-wal');
93
+ }
94
+ catch { /* may not exist */ }
95
+ try {
96
+ fs.unlinkSync(this.dbPath + '-shm');
97
+ }
98
+ catch { /* may not exist */ }
99
+ this.logger.info(`Database restored from: ${filename}`);
100
+ return { success: true, filename, integrityOk };
101
+ }
102
+ /** Check integrity of a database file. */
103
+ checkIntegrity(dbFilePath) {
104
+ const filePath = dbFilePath ?? this.dbPath;
105
+ try {
106
+ const BetterSqlite3 = require('better-sqlite3');
107
+ const testDb = new BetterSqlite3(filePath, { readonly: true });
108
+ const result = testDb.pragma('integrity_check');
109
+ testDb.close();
110
+ return result.length === 1 && result[0].integrity_check === 'ok';
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
116
+ /** Get current database size info. */
117
+ getInfo() {
118
+ const dbSize = fs.existsSync(this.dbPath) ? fs.statSync(this.dbPath).size : 0;
119
+ const walPath = this.dbPath + '-wal';
120
+ const walSize = fs.existsSync(walPath) ? fs.statSync(walPath).size : 0;
121
+ const backups = this.list();
122
+ const backupTotalSize = backups.reduce((sum, b) => sum + b.size, 0);
123
+ return { dbSize, walSize, backupCount: backups.length, backupTotalSize };
124
+ }
125
+ /** Delete a specific backup. */
126
+ delete(filename) {
127
+ const filePath = path.join(this.backupDir, filename);
128
+ if (!fs.existsSync(filePath))
129
+ return false;
130
+ fs.unlinkSync(filePath);
131
+ this.logger.info(`Backup deleted: ${filename}`);
132
+ return true;
133
+ }
134
+ /** Auto-cleanup oldest backups beyond maxBackups. */
135
+ autoCleanup() {
136
+ const backups = this.list(); // sorted newest first
137
+ if (backups.length <= this.maxBackups)
138
+ return;
139
+ const toDelete = backups.slice(this.maxBackups);
140
+ for (const backup of toDelete) {
141
+ this.delete(backup.filename);
142
+ }
143
+ this.logger.info(`Auto-cleaned ${toDelete.length} old backup(s)`);
144
+ }
145
+ formatSize(bytes) {
146
+ if (bytes < 1024)
147
+ return `${bytes}B`;
148
+ if (bytes < 1024 * 1024)
149
+ return `${(bytes / 1024).toFixed(1)}KB`;
150
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
151
+ }
152
+ }
153
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/backup/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface CausalEvent {
3
+ source: string;
4
+ type: string;
5
+ timestamp: number;
6
+ data?: unknown;
7
+ }
8
+ export interface CausalEdge {
9
+ id?: number;
10
+ cause: string;
11
+ effect: string;
12
+ strength: number;
13
+ confidence: number;
14
+ lag_ms: number;
15
+ sample_size: number;
16
+ direction: number;
17
+ }
18
+ export interface CausalPath {
19
+ chain: string[];
20
+ totalStrength: number;
21
+ totalLag: number;
22
+ }
23
+ export interface CausalAnalysis {
24
+ edges: CausalEdge[];
25
+ roots: string[];
26
+ leaves: string[];
27
+ strongestChain: CausalPath | null;
28
+ }
29
+ export declare function runCausalMigration(db: Database.Database): void;
30
+ /**
31
+ * Causal Inference Engine: detects cause→effect relationships between events.
32
+ *
33
+ * Research approach: Simplified Granger Causality.
34
+ *
35
+ * Granger causality asks: "Does knowing that event A happened improve
36
+ * our ability to predict event B?" If yes, A Granger-causes B.
37
+ *
38
+ * Algorithm:
39
+ * 1. Record all events with timestamps
40
+ * 2. For each pair of event types (A, B):
41
+ * a. Count how often B occurs within a time window after A
42
+ * b. Compare this to the baseline rate of B
43
+ * c. If B is significantly more likely after A → A causes B
44
+ * 3. Build a directed graph of causal relationships
45
+ * 4. Detect causal chains (A → B → C)
46
+ */
47
+ export declare class CausalGraph {
48
+ private db;
49
+ private logger;
50
+ private maxWindowMs;
51
+ private minSamples;
52
+ private significanceThreshold;
53
+ constructor(db: Database.Database, config?: {
54
+ maxWindowMs?: number;
55
+ minSamples?: number;
56
+ significanceThreshold?: number;
57
+ });
58
+ /** Record an event for causal analysis. */
59
+ recordEvent(source: string, type: string, data?: unknown): void;
60
+ /**
61
+ * Run Granger causality analysis on all event pairs.
62
+ * This is the core research algorithm.
63
+ */
64
+ analyze(): CausalEdge[];
65
+ /**
66
+ * Test if event type A Granger-causes event type B.
67
+ *
68
+ * Algorithm:
69
+ * 1. Get all timestamps for A and B
70
+ * 2. For each occurrence of A, check if B occurs within the window after
71
+ * 3. Calculate: P(B after A) vs P(B in any random window)
72
+ * 4. If ratio > threshold → A Granger-causes B
73
+ */
74
+ private testGrangerCausality;
75
+ /** Get all detected causal edges (strongest first). */
76
+ getEdges(minStrength?: number): CausalEdge[];
77
+ /** Get causes of a specific event type. */
78
+ getCauses(eventType: string): CausalEdge[];
79
+ /** Get effects of a specific event type. */
80
+ getEffects(eventType: string): CausalEdge[];
81
+ /** Find causal chains (A → B → C). */
82
+ findChains(maxDepth?: number): CausalPath[];
83
+ /** Full causal analysis including graph structure. */
84
+ getAnalysis(): CausalAnalysis;
85
+ /** Get event type statistics. */
86
+ getEventStats(): Array<{
87
+ type: string;
88
+ count: number;
89
+ first_seen: number;
90
+ last_seen: number;
91
+ }>;
92
+ private getEventTypes;
93
+ private getTimeRange;
94
+ private upsertEdge;
95
+ private findRoots;
96
+ private findLeaves;
97
+ private dfs;
98
+ }
@@ -0,0 +1,265 @@
1
+ import { getLogger } from '../utils/logger.js';
2
+ // ── Migration ───────────────────────────────────────────
3
+ export function runCausalMigration(db) {
4
+ db.exec(`
5
+ CREATE TABLE IF NOT EXISTS causal_events (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ source TEXT NOT NULL,
8
+ type TEXT NOT NULL,
9
+ timestamp INTEGER NOT NULL,
10
+ data TEXT,
11
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
12
+ );
13
+
14
+ CREATE TABLE IF NOT EXISTS causal_edges (
15
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
16
+ cause TEXT NOT NULL,
17
+ effect TEXT NOT NULL,
18
+ strength REAL NOT NULL DEFAULT 0,
19
+ confidence REAL NOT NULL DEFAULT 0,
20
+ lag_ms REAL NOT NULL DEFAULT 0,
21
+ sample_size INTEGER NOT NULL DEFAULT 0,
22
+ direction REAL NOT NULL DEFAULT 1,
23
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
24
+ UNIQUE(cause, effect)
25
+ );
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_causal_events_type ON causal_events(type);
28
+ CREATE INDEX IF NOT EXISTS idx_causal_events_timestamp ON causal_events(timestamp);
29
+ CREATE INDEX IF NOT EXISTS idx_causal_edges_strength ON causal_edges(strength DESC);
30
+ `);
31
+ }
32
+ // ── Engine ───────────────────────────────────────────────
33
+ /**
34
+ * Causal Inference Engine: detects cause→effect relationships between events.
35
+ *
36
+ * Research approach: Simplified Granger Causality.
37
+ *
38
+ * Granger causality asks: "Does knowing that event A happened improve
39
+ * our ability to predict event B?" If yes, A Granger-causes B.
40
+ *
41
+ * Algorithm:
42
+ * 1. Record all events with timestamps
43
+ * 2. For each pair of event types (A, B):
44
+ * a. Count how often B occurs within a time window after A
45
+ * b. Compare this to the baseline rate of B
46
+ * c. If B is significantly more likely after A → A causes B
47
+ * 3. Build a directed graph of causal relationships
48
+ * 4. Detect causal chains (A → B → C)
49
+ */
50
+ export class CausalGraph {
51
+ db;
52
+ logger = getLogger();
53
+ maxWindowMs; // max time lag to consider
54
+ minSamples; // minimum observations for significance
55
+ significanceThreshold; // minimum ratio to baseline for significance
56
+ constructor(db, config) {
57
+ this.db = db;
58
+ runCausalMigration(db);
59
+ this.maxWindowMs = config?.maxWindowMs ?? 300_000; // 5 minutes
60
+ this.minSamples = config?.minSamples ?? 5;
61
+ this.significanceThreshold = config?.significanceThreshold ?? 1.5; // 50% above baseline
62
+ }
63
+ /** Record an event for causal analysis. */
64
+ recordEvent(source, type, data) {
65
+ this.db.prepare(`
66
+ INSERT INTO causal_events (source, type, timestamp, data)
67
+ VALUES (?, ?, ?, ?)
68
+ `).run(source, type, Date.now(), data ? JSON.stringify(data) : null);
69
+ }
70
+ /**
71
+ * Run Granger causality analysis on all event pairs.
72
+ * This is the core research algorithm.
73
+ */
74
+ analyze() {
75
+ const eventTypes = this.getEventTypes();
76
+ if (eventTypes.length < 2)
77
+ return [];
78
+ const edges = [];
79
+ // For each pair of event types, test for Granger causality
80
+ for (const cause of eventTypes) {
81
+ for (const effect of eventTypes) {
82
+ if (cause === effect)
83
+ continue;
84
+ const edge = this.testGrangerCausality(cause, effect);
85
+ if (edge) {
86
+ this.upsertEdge(edge);
87
+ edges.push(edge);
88
+ }
89
+ }
90
+ }
91
+ this.logger.info(`Causal analysis complete: ${edges.length} causal relationships detected`);
92
+ return edges;
93
+ }
94
+ /**
95
+ * Test if event type A Granger-causes event type B.
96
+ *
97
+ * Algorithm:
98
+ * 1. Get all timestamps for A and B
99
+ * 2. For each occurrence of A, check if B occurs within the window after
100
+ * 3. Calculate: P(B after A) vs P(B in any random window)
101
+ * 4. If ratio > threshold → A Granger-causes B
102
+ */
103
+ testGrangerCausality(causeType, effectType) {
104
+ const causeEvents = this.db.prepare('SELECT timestamp FROM causal_events WHERE type = ? ORDER BY timestamp').all(causeType);
105
+ const effectEvents = this.db.prepare('SELECT timestamp FROM causal_events WHERE type = ? ORDER BY timestamp').all(effectType);
106
+ if (causeEvents.length < this.minSamples || effectEvents.length < this.minSamples) {
107
+ return null;
108
+ }
109
+ // Calculate: how often does effectType occur within maxWindowMs after causeType?
110
+ let followCount = 0;
111
+ let totalLag = 0;
112
+ for (const cause of causeEvents) {
113
+ // Find the first effect event within the window
114
+ for (const effect of effectEvents) {
115
+ const lag = effect.timestamp - cause.timestamp;
116
+ if (lag > 0 && lag <= this.maxWindowMs) {
117
+ followCount++;
118
+ totalLag += lag;
119
+ break; // only count first occurrence per cause event
120
+ }
121
+ if (lag > this.maxWindowMs)
122
+ break; // past window, stop looking
123
+ }
124
+ }
125
+ if (followCount < this.minSamples)
126
+ return null;
127
+ // P(effect follows cause) = followCount / causeEvents.length
128
+ const pFollows = followCount / causeEvents.length;
129
+ // Baseline: P(effect in any random window of same size)
130
+ // Estimate: total effect events / total time * window size
131
+ const timeRange = this.getTimeRange();
132
+ if (timeRange <= 0)
133
+ return null;
134
+ const baselineRate = (effectEvents.length / timeRange) * this.maxWindowMs;
135
+ const pBaseline = Math.min(1, baselineRate);
136
+ // Significance: is the conditional probability significantly higher than baseline?
137
+ if (pBaseline <= 0)
138
+ return null;
139
+ const ratio = pFollows / pBaseline;
140
+ if (ratio < this.significanceThreshold)
141
+ return null;
142
+ // Calculate strength (0-1 normalized)
143
+ const strength = Math.min(1, (ratio - 1) / (this.significanceThreshold * 2));
144
+ // Confidence based on sample size (logistic curve, plateaus around 20 samples)
145
+ const confidence = 1 - 1 / (1 + followCount / 10);
146
+ // Average lag
147
+ const avgLag = followCount > 0 ? totalLag / followCount : 0;
148
+ return {
149
+ cause: causeType,
150
+ effect: effectType,
151
+ strength,
152
+ confidence,
153
+ lag_ms: avgLag,
154
+ sample_size: followCount,
155
+ direction: 1, // cause increases likelihood of effect
156
+ };
157
+ }
158
+ /** Get all detected causal edges (strongest first). */
159
+ getEdges(minStrength = 0) {
160
+ return this.db.prepare('SELECT * FROM causal_edges WHERE strength >= ? ORDER BY strength DESC').all(minStrength);
161
+ }
162
+ /** Get causes of a specific event type. */
163
+ getCauses(eventType) {
164
+ return this.db.prepare('SELECT * FROM causal_edges WHERE effect = ? ORDER BY strength DESC').all(eventType);
165
+ }
166
+ /** Get effects of a specific event type. */
167
+ getEffects(eventType) {
168
+ return this.db.prepare('SELECT * FROM causal_edges WHERE cause = ? ORDER BY strength DESC').all(eventType);
169
+ }
170
+ /** Find causal chains (A → B → C). */
171
+ findChains(maxDepth = 4) {
172
+ const edges = this.getEdges(0.1);
173
+ const adjacency = new Map();
174
+ for (const edge of edges) {
175
+ if (!adjacency.has(edge.cause))
176
+ adjacency.set(edge.cause, []);
177
+ adjacency.get(edge.cause).push(edge);
178
+ }
179
+ const chains = [];
180
+ const roots = this.findRoots(edges);
181
+ for (const root of roots) {
182
+ this.dfs(root, [root], 0, 0, adjacency, maxDepth, new Set(), chains);
183
+ }
184
+ // Sort by total strength descending
185
+ chains.sort((a, b) => b.totalStrength - a.totalStrength);
186
+ return chains.slice(0, 20); // top 20 chains
187
+ }
188
+ /** Full causal analysis including graph structure. */
189
+ getAnalysis() {
190
+ const edges = this.getEdges();
191
+ const roots = this.findRoots(edges);
192
+ const leaves = this.findLeaves(edges);
193
+ const chains = this.findChains();
194
+ return {
195
+ edges,
196
+ roots,
197
+ leaves,
198
+ strongestChain: chains.length > 0 ? chains[0] : null,
199
+ };
200
+ }
201
+ /** Get event type statistics. */
202
+ getEventStats() {
203
+ return this.db.prepare(`
204
+ SELECT type, COUNT(*) as count,
205
+ MIN(timestamp) as first_seen,
206
+ MAX(timestamp) as last_seen
207
+ FROM causal_events
208
+ GROUP BY type
209
+ ORDER BY count DESC
210
+ `).all();
211
+ }
212
+ // ── Private helpers ─────────────────────────────────
213
+ getEventTypes() {
214
+ const rows = this.db.prepare('SELECT DISTINCT type FROM causal_events').all();
215
+ return rows.map(r => r.type);
216
+ }
217
+ getTimeRange() {
218
+ const row = this.db.prepare('SELECT MIN(timestamp) as min_ts, MAX(timestamp) as max_ts FROM causal_events').get();
219
+ if (!row.min_ts || !row.max_ts)
220
+ return 0;
221
+ return row.max_ts - row.min_ts;
222
+ }
223
+ upsertEdge(edge) {
224
+ this.db.prepare(`
225
+ INSERT INTO causal_edges (cause, effect, strength, confidence, lag_ms, sample_size, direction)
226
+ VALUES (?, ?, ?, ?, ?, ?, ?)
227
+ ON CONFLICT(cause, effect) DO UPDATE SET
228
+ strength = ?, confidence = ?, lag_ms = ?, sample_size = ?, direction = ?,
229
+ updated_at = datetime('now')
230
+ `).run(edge.cause, edge.effect, edge.strength, edge.confidence, edge.lag_ms, edge.sample_size, edge.direction, edge.strength, edge.confidence, edge.lag_ms, edge.sample_size, edge.direction);
231
+ }
232
+ findRoots(edges) {
233
+ const causes = new Set(edges.map(e => e.cause));
234
+ const effects = new Set(edges.map(e => e.effect));
235
+ return [...causes].filter(c => !effects.has(c));
236
+ }
237
+ findLeaves(edges) {
238
+ const causes = new Set(edges.map(e => e.cause));
239
+ const effects = new Set(edges.map(e => e.effect));
240
+ return [...effects].filter(e => !causes.has(e));
241
+ }
242
+ dfs(node, path, totalStrength, totalLag, adjacency, maxDepth, visited, results) {
243
+ if (path.length >= 3) {
244
+ // Record any path of length 3+ as a chain
245
+ results.push({
246
+ chain: [...path],
247
+ totalStrength: totalStrength / (path.length - 1), // average strength
248
+ totalLag,
249
+ });
250
+ }
251
+ if (path.length >= maxDepth)
252
+ return;
253
+ const neighbors = adjacency.get(node) ?? [];
254
+ for (const edge of neighbors) {
255
+ if (visited.has(edge.effect))
256
+ continue;
257
+ visited.add(edge.effect);
258
+ path.push(edge.effect);
259
+ this.dfs(edge.effect, path, totalStrength + edge.strength, totalLag + edge.lag_ms, adjacency, maxDepth, visited, results);
260
+ path.pop();
261
+ visited.delete(edge.effect);
262
+ }
263
+ }
264
+ }
265
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/causal/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type Database from 'better-sqlite3';
2
+ export interface ExportOptions {
3
+ table: string;
4
+ format: 'json' | 'csv';
5
+ columns?: string[];
6
+ where?: string;
7
+ orderBy?: string;
8
+ limit?: number;
9
+ dateColumn?: string;
10
+ dateFrom?: string;
11
+ dateTo?: string;
12
+ }
13
+ export interface ExportResult {
14
+ table: string;
15
+ format: 'json' | 'csv';
16
+ rowCount: number;
17
+ data: string;
18
+ }
19
+ export declare class ExportService {
20
+ private db;
21
+ private logger;
22
+ constructor(db: Database.Database);
23
+ /** Get list of all tables in the database. */
24
+ listTables(): string[];
25
+ /** Get column info for a table. */
26
+ getColumns(table: string): {
27
+ name: string;
28
+ type: string;
29
+ }[];
30
+ /** Export data from a table. */
31
+ export(options: ExportOptions): ExportResult;
32
+ /** Export multiple tables at once (JSON only). */
33
+ exportAll(tables?: string[], format?: 'json' | 'csv'): Record<string, ExportResult>;
34
+ /** Get row counts for all tables. */
35
+ getStats(): Record<string, number>;
36
+ /** Convert array of objects to CSV string. */
37
+ private toCsv;
38
+ /** Validate table name to prevent SQL injection. */
39
+ private validateTableName;
40
+ /** Validate column name. */
41
+ private validateColumnName;
42
+ }