@linklabjs/core 0.1.0 → 0.1.2
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/dist/api/DomainNode.d.ts +154 -0
- package/dist/api/DomainNode.d.ts.map +1 -0
- package/dist/api/DomainNode.js +1157 -0
- package/dist/api/DomainNode.js.map +1 -0
- package/dist/api/Graph.d.ts +117 -0
- package/dist/api/Graph.d.ts.map +1 -0
- package/dist/api/Graph.js +212 -0
- package/dist/api/Graph.js.map +1 -0
- package/dist/api/PathBuilder.d.ts +76 -0
- package/dist/api/PathBuilder.d.ts.map +1 -0
- package/dist/api/PathBuilder.js +182 -0
- package/dist/api/PathBuilder.js.map +1 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +7 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/loadGraph.d.ts +57 -0
- package/dist/api/loadGraph.d.ts.map +1 -0
- package/dist/api/loadGraph.js +153 -0
- package/dist/api/loadGraph.js.map +1 -0
- package/dist/api/test-api.d.ts +9 -0
- package/dist/api/test-api.d.ts.map +1 -0
- package/dist/api/test-api.js +133 -0
- package/dist/api/test-api.js.map +1 -0
- package/dist/api/test-domain.d.ts +13 -0
- package/dist/api/test-domain.d.ts.map +1 -0
- package/dist/api/test-domain.js +105 -0
- package/dist/api/test-domain.js.map +1 -0
- package/dist/api/types.d.ts +69 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +22 -0
- package/dist/api/types.js.map +1 -0
- package/dist/config/synonyms.json +25 -0
- package/dist/core/EventBus.d.ts +56 -0
- package/dist/core/EventBus.d.ts.map +1 -0
- package/dist/core/EventBus.js +147 -0
- package/dist/core/EventBus.js.map +1 -0
- package/dist/core/GraphEvents.d.ts +118 -0
- package/dist/core/GraphEvents.d.ts.map +1 -0
- package/dist/core/GraphEvents.js +23 -0
- package/dist/core/GraphEvents.js.map +1 -0
- package/dist/core/PathFinder.d.ts +43 -0
- package/dist/core/PathFinder.d.ts.map +1 -0
- package/dist/core/PathFinder.js +264 -0
- package/dist/core/PathFinder.js.map +1 -0
- package/dist/formatters/BaseFormatter.d.ts +15 -0
- package/dist/formatters/BaseFormatter.d.ts.map +1 -0
- package/dist/formatters/BaseFormatter.js +9 -0
- package/dist/formatters/BaseFormatter.js.map +1 -0
- package/dist/graph/GraphAssembler.d.ts +14 -0
- package/dist/graph/GraphAssembler.d.ts.map +1 -0
- package/dist/graph/GraphAssembler.js +44 -0
- package/dist/graph/GraphAssembler.js.map +1 -0
- package/dist/graph/GraphCompiler.d.ts +37 -0
- package/dist/graph/GraphCompiler.d.ts.map +1 -0
- package/dist/graph/GraphCompiler.js +355 -0
- package/dist/graph/GraphCompiler.js.map +1 -0
- package/dist/graph/GraphExtractor.d.ts +21 -0
- package/dist/graph/GraphExtractor.d.ts.map +1 -0
- package/dist/graph/GraphExtractor.js +145 -0
- package/dist/graph/GraphExtractor.js.map +1 -0
- package/dist/graph/GraphOptimizer.d.ts +104 -0
- package/dist/graph/GraphOptimizer.d.ts.map +1 -0
- package/dist/graph/GraphOptimizer.js +306 -0
- package/dist/graph/GraphOptimizer.js.map +1 -0
- package/dist/graph/GraphTrainer.d.ts +52 -0
- package/dist/graph/GraphTrainer.d.ts.map +1 -0
- package/dist/graph/GraphTrainer.js +188 -0
- package/dist/graph/GraphTrainer.js.map +1 -0
- package/dist/http/LinkBuilder.d.ts +82 -0
- package/dist/http/LinkBuilder.d.ts.map +1 -0
- package/dist/http/LinkBuilder.js +190 -0
- package/dist/http/LinkBuilder.js.map +1 -0
- package/dist/http/TrailRequest.d.ts +39 -0
- package/dist/http/TrailRequest.d.ts.map +1 -0
- package/dist/http/TrailRequest.js +22 -0
- package/dist/http/TrailRequest.js.map +1 -0
- package/dist/http/example-netflix.d.ts +6 -0
- package/dist/http/example-netflix.d.ts.map +1 -0
- package/dist/http/example-netflix.js +52 -0
- package/dist/http/example-netflix.js.map +1 -0
- package/dist/http/index.d.ts +32 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +27 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/plugin.d.ts +110 -0
- package/dist/http/plugin.d.ts.map +1 -0
- package/dist/http/plugin.js +217 -0
- package/dist/http/plugin.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation/TelemetryShim.d.ts +114 -0
- package/dist/instrumentation/TelemetryShim.d.ts.map +1 -0
- package/dist/instrumentation/TelemetryShim.js +107 -0
- package/dist/instrumentation/TelemetryShim.js.map +1 -0
- package/dist/navigation/NavigationEngine.d.ts +69 -0
- package/dist/navigation/NavigationEngine.d.ts.map +1 -0
- package/dist/navigation/NavigationEngine.js +361 -0
- package/dist/navigation/NavigationEngine.js.map +1 -0
- package/dist/navigation/Resolver.d.ts +35 -0
- package/dist/navigation/Resolver.d.ts.map +1 -0
- package/dist/navigation/Resolver.js +113 -0
- package/dist/navigation/Resolver.js.map +1 -0
- package/dist/navigation/Scheduler.d.ts +36 -0
- package/dist/navigation/Scheduler.d.ts.map +1 -0
- package/dist/navigation/Scheduler.js +107 -0
- package/dist/navigation/Scheduler.js.map +1 -0
- package/dist/navigation/Trail.d.ts +129 -0
- package/dist/navigation/Trail.d.ts.map +1 -0
- package/dist/navigation/Trail.js +202 -0
- package/dist/navigation/Trail.js.map +1 -0
- package/dist/navigation/TrailParser.d.ts +96 -0
- package/dist/navigation/TrailParser.d.ts.map +1 -0
- package/dist/navigation/TrailParser.js +180 -0
- package/dist/navigation/TrailParser.js.map +1 -0
- package/dist/navigation/index.d.ts +10 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +9 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/providers/MockProvider.d.ts +29 -0
- package/dist/providers/MockProvider.d.ts.map +1 -0
- package/dist/providers/MockProvider.js +55 -0
- package/dist/providers/MockProvider.js.map +1 -0
- package/dist/providers/PostgresProvider.d.ts +46 -0
- package/dist/providers/PostgresProvider.d.ts.map +1 -0
- package/dist/providers/PostgresProvider.js +152 -0
- package/dist/providers/PostgresProvider.js.map +1 -0
- package/dist/runtime/CompiledGraphEngine.d.ts +74 -0
- package/dist/runtime/CompiledGraphEngine.d.ts.map +1 -0
- package/dist/runtime/CompiledGraphEngine.js +211 -0
- package/dist/runtime/CompiledGraphEngine.js.map +1 -0
- package/dist/runtime/DataLoader.d.ts +90 -0
- package/dist/runtime/DataLoader.d.ts.map +1 -0
- package/dist/runtime/DataLoader.js +178 -0
- package/dist/runtime/DataLoader.js.map +1 -0
- package/dist/runtime/Engine.d.ts +36 -0
- package/dist/runtime/Engine.d.ts.map +1 -0
- package/dist/runtime/Engine.js +128 -0
- package/dist/runtime/Engine.js.map +1 -0
- package/dist/runtime/QueryEngine.d.ts +80 -0
- package/dist/runtime/QueryEngine.d.ts.map +1 -0
- package/dist/runtime/QueryEngine.js +188 -0
- package/dist/runtime/QueryEngine.js.map +1 -0
- package/dist/scenarios/test-metro-paris/config.json +6 -0
- package/dist/scenarios/test-metro-paris/graph.json +16325 -0
- package/dist/scenarios/test-metro-paris/queries.d.ts +22 -0
- package/dist/scenarios/test-metro-paris/queries.d.ts.map +1 -0
- package/dist/scenarios/test-metro-paris/queries.js +128 -0
- package/dist/scenarios/test-metro-paris/queries.js.map +1 -0
- package/dist/scenarios/test-metro-paris/stack.json +1 -0
- package/dist/scenarios/test-musicians/config.json +10 -0
- package/dist/scenarios/test-musicians/graph.json +20 -0
- package/dist/scenarios/test-musicians/stack.json +1 -0
- package/dist/scenarios/test-netflix/actions.d.ts +14 -0
- package/dist/scenarios/test-netflix/actions.d.ts.map +1 -0
- package/dist/scenarios/test-netflix/actions.js +86 -0
- package/dist/scenarios/test-netflix/actions.js.map +1 -0
- package/dist/scenarios/test-netflix/config.json +6 -0
- package/dist/scenarios/test-netflix/data/categories.json +1 -0
- package/dist/scenarios/test-netflix/data/companies.json +1 -0
- package/dist/scenarios/test-netflix/data/credits.json +19797 -0
- package/dist/scenarios/test-netflix/data/departments.json +18 -0
- package/dist/scenarios/test-netflix/data/jobs.json +142 -0
- package/dist/scenarios/test-netflix/data/movies.json +3497 -0
- package/dist/scenarios/test-netflix/data/people.json +1 -0
- package/dist/scenarios/test-netflix/data/synonyms.json +7 -0
- package/dist/scenarios/test-netflix/data/users.json +70 -0
- package/dist/scenarios/test-netflix/graph.json +1017 -0
- package/dist/scenarios/test-netflix/queries.d.ts +29 -0
- package/dist/scenarios/test-netflix/queries.d.ts.map +1 -0
- package/dist/scenarios/test-netflix/queries.js +134 -0
- package/dist/scenarios/test-netflix/queries.js.map +1 -0
- package/dist/scenarios/test-netflix/stack.json +14 -0
- package/dist/schema/GraphBuilder.d.ts +9 -0
- package/dist/schema/GraphBuilder.d.ts.map +1 -0
- package/dist/schema/GraphBuilder.js +90 -0
- package/dist/schema/GraphBuilder.js.map +1 -0
- package/dist/schema/JsonSchemaExtractor.d.ts +21 -0
- package/dist/schema/JsonSchemaExtractor.d.ts.map +1 -0
- package/dist/schema/JsonSchemaExtractor.js +88 -0
- package/dist/schema/JsonSchemaExtractor.js.map +1 -0
- package/dist/schema/SchemaAnalyzer.d.ts +41 -0
- package/dist/schema/SchemaAnalyzer.d.ts.map +1 -0
- package/dist/schema/SchemaAnalyzer.js +144 -0
- package/dist/schema/SchemaAnalyzer.js.map +1 -0
- package/dist/schema/SchemaExtractor.d.ts +10 -0
- package/dist/schema/SchemaExtractor.d.ts.map +1 -0
- package/dist/schema/SchemaExtractor.js +90 -0
- package/dist/schema/SchemaExtractor.js.map +1 -0
- package/dist/schema/SynonymResolver.d.ts +55 -0
- package/dist/schema/SynonymResolver.d.ts.map +1 -0
- package/dist/schema/SynonymResolver.js +121 -0
- package/dist/schema/SynonymResolver.js.map +1 -0
- package/dist/scripts/dictionary.json +796 -0
- package/dist/scripts/graph.json +664 -0
- package/dist/scripts/regenerate.d.ts +23 -0
- package/dist/scripts/regenerate.d.ts.map +1 -0
- package/dist/scripts/regenerate.js +206 -0
- package/dist/scripts/regenerate.js.map +1 -0
- package/dist/types/index.d.ts +394 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +21 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompiledGraphEngine - Production engine using precompiled graph
|
|
3
|
+
*
|
|
4
|
+
* - O(1) route lookup
|
|
5
|
+
* - Automatic fallback
|
|
6
|
+
* - Live metrics
|
|
7
|
+
* - Hot reload support
|
|
8
|
+
*/
|
|
9
|
+
export class CompiledGraphEngine {
|
|
10
|
+
compiled;
|
|
11
|
+
provider;
|
|
12
|
+
liveMetrics;
|
|
13
|
+
routeCache;
|
|
14
|
+
constructor(compiled, provider) {
|
|
15
|
+
this.compiled = compiled;
|
|
16
|
+
this.provider = provider;
|
|
17
|
+
this.liveMetrics = new Map();
|
|
18
|
+
this.routeCache = this.buildRouteCache(compiled);
|
|
19
|
+
console.log('🚀 Compiled Graph Engine initialized');
|
|
20
|
+
console.log(` Routes loaded: ${compiled.routes.length}`);
|
|
21
|
+
console.log(` Nodes: ${compiled.nodes.length}`);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build fast lookup cache
|
|
25
|
+
*/
|
|
26
|
+
buildRouteCache(compiled) {
|
|
27
|
+
const cache = new Map();
|
|
28
|
+
for (const route of compiled.routes) {
|
|
29
|
+
const key = `${route.from}→${route.to}`;
|
|
30
|
+
cache.set(key, route);
|
|
31
|
+
}
|
|
32
|
+
return cache;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Execute query from -> to
|
|
36
|
+
*/
|
|
37
|
+
async query(from, to, data = {}) {
|
|
38
|
+
const key = `${from}→${to}`;
|
|
39
|
+
// O(1) lookup!
|
|
40
|
+
const route = this.routeCache.get(key);
|
|
41
|
+
if (!route) {
|
|
42
|
+
throw new Error(`No compiled route from ${from} to ${to}`);
|
|
43
|
+
}
|
|
44
|
+
// Try primary path
|
|
45
|
+
try {
|
|
46
|
+
const start = performance.now();
|
|
47
|
+
const result = await this.executePath(route.primary.path, data);
|
|
48
|
+
const duration = performance.now() - start;
|
|
49
|
+
// Update metrics
|
|
50
|
+
this.updateMetrics(route.primary.path, duration, true);
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.warn(`⚠️ Primary path failed: ${err.message}`);
|
|
55
|
+
// Try fallbacks
|
|
56
|
+
return await this.fallback(route, data);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Execute path with JOINs
|
|
61
|
+
*/
|
|
62
|
+
async executePath(path, data) {
|
|
63
|
+
// Build SQL with JOINs
|
|
64
|
+
let sql = `SELECT * FROM ${path[0]}`;
|
|
65
|
+
const params = [];
|
|
66
|
+
// Add JOINs
|
|
67
|
+
for (let i = 1; i < path.length; i++) {
|
|
68
|
+
const from = path[i - 1];
|
|
69
|
+
const to = path[i];
|
|
70
|
+
// Find edge in compiled graph
|
|
71
|
+
const edge = this.findEdge(from, to);
|
|
72
|
+
if (edge) {
|
|
73
|
+
sql += ` JOIN ${to} ON ${from}.${edge.via} = ${to}.id`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Add WHERE if ID provided
|
|
77
|
+
if (data.id) {
|
|
78
|
+
sql += ` WHERE ${path[0]}.id = $${params.length + 1}`;
|
|
79
|
+
params.push(data.id);
|
|
80
|
+
}
|
|
81
|
+
// Execute
|
|
82
|
+
return await this.provider.query(sql, params);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Find edge between nodes
|
|
86
|
+
*/
|
|
87
|
+
findEdge(from, to) {
|
|
88
|
+
// Simplified - would look in compiled graph edges
|
|
89
|
+
return { via: 'id' };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Fallback to alternative paths
|
|
93
|
+
*/
|
|
94
|
+
async fallback(route, data) {
|
|
95
|
+
for (const [index, fallback] of route.fallbacks.entries()) {
|
|
96
|
+
try {
|
|
97
|
+
console.log(` Trying fallback ${index + 1}/${route.fallbacks.length}...`);
|
|
98
|
+
const start = performance.now();
|
|
99
|
+
const result = await this.executePath(fallback.path, data);
|
|
100
|
+
const duration = performance.now() - start;
|
|
101
|
+
// Success!
|
|
102
|
+
console.log(` ✅ Fallback worked: ${fallback.path.join('→')}`);
|
|
103
|
+
this.updateMetrics(fallback.path, duration, true);
|
|
104
|
+
// Maybe promote this fallback?
|
|
105
|
+
this.considerPromotion(route, index);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.warn(` ✗ Fallback ${index + 1} failed: ${err.message}`);
|
|
110
|
+
this.updateMetrics(fallback.path, 0, false);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw new Error('All paths failed');
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Update live metrics
|
|
118
|
+
*/
|
|
119
|
+
updateMetrics(path, duration, success) {
|
|
120
|
+
const key = path.join('→');
|
|
121
|
+
if (!this.liveMetrics.has(key)) {
|
|
122
|
+
this.liveMetrics.set(key, {
|
|
123
|
+
path,
|
|
124
|
+
executions: 0,
|
|
125
|
+
successes: 0,
|
|
126
|
+
failures: 0,
|
|
127
|
+
totalTime: 0,
|
|
128
|
+
avgTime: 0
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const metric = this.liveMetrics.get(key);
|
|
132
|
+
metric.executions++;
|
|
133
|
+
if (success) {
|
|
134
|
+
metric.successes++;
|
|
135
|
+
metric.totalTime += duration;
|
|
136
|
+
metric.avgTime = metric.totalTime / metric.successes;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
metric.failures++;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Consider promoting a fallback to primary
|
|
144
|
+
*/
|
|
145
|
+
considerPromotion(route, fallbackIndex) {
|
|
146
|
+
const fallback = route.fallbacks[fallbackIndex];
|
|
147
|
+
const fallbackKey = fallback.path.join('→');
|
|
148
|
+
const primaryKey = route.primary.path.join('→');
|
|
149
|
+
const fallbackMetric = this.liveMetrics.get(fallbackKey);
|
|
150
|
+
const primaryMetric = this.liveMetrics.get(primaryKey);
|
|
151
|
+
if (!fallbackMetric || !primaryMetric)
|
|
152
|
+
return;
|
|
153
|
+
// Promote if fallback is faster AND more reliable
|
|
154
|
+
const fallbackBetter = fallbackMetric.avgTime < primaryMetric.avgTime && fallbackMetric.successes > 5; // Min sample size
|
|
155
|
+
if (fallbackBetter) {
|
|
156
|
+
console.log(`🔄 Promoting fallback to primary: ${fallbackKey}`);
|
|
157
|
+
// Swap
|
|
158
|
+
const temp = route.primary;
|
|
159
|
+
route.primary = fallback;
|
|
160
|
+
route.fallbacks[fallbackIndex] = temp;
|
|
161
|
+
// Update cache
|
|
162
|
+
const key = `${route.from}→${route.to}`;
|
|
163
|
+
this.routeCache.set(key, route);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get live statistics
|
|
168
|
+
*/
|
|
169
|
+
getStats() {
|
|
170
|
+
const metrics = Array.from(this.liveMetrics.values());
|
|
171
|
+
if (metrics.length === 0) {
|
|
172
|
+
return {
|
|
173
|
+
totalExecutions: 0,
|
|
174
|
+
totalSuccesses: 0,
|
|
175
|
+
successRate: '0%',
|
|
176
|
+
avgTime: '0ms',
|
|
177
|
+
uniquePaths: 0
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const totalExecutions = metrics.reduce((sum, m) => sum + m.executions, 0);
|
|
181
|
+
const totalSuccesses = metrics.reduce((sum, m) => sum + m.successes, 0);
|
|
182
|
+
const avgTime = metrics.reduce((sum, m) => sum + m.avgTime * m.successes, 0) / totalSuccesses;
|
|
183
|
+
const fastest = metrics.reduce((min, m) => (m.avgTime < min.avgTime ? m : min));
|
|
184
|
+
const slowest = metrics.reduce((max, m) => (m.avgTime > max.avgTime ? m : max));
|
|
185
|
+
return {
|
|
186
|
+
totalExecutions,
|
|
187
|
+
totalSuccesses,
|
|
188
|
+
successRate: ((totalSuccesses / totalExecutions) * 100).toFixed(1) + '%',
|
|
189
|
+
avgTime: avgTime.toFixed(2) + 'ms',
|
|
190
|
+
uniquePaths: metrics.length,
|
|
191
|
+
fastest,
|
|
192
|
+
slowest
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Export metrics for recompilation
|
|
197
|
+
*/
|
|
198
|
+
exportMetrics() {
|
|
199
|
+
return new Map(this.liveMetrics);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Hot reload compiled graph
|
|
203
|
+
*/
|
|
204
|
+
reload(compiled) {
|
|
205
|
+
console.log('🔄 Hot reloading compiled graph...');
|
|
206
|
+
this.compiled = compiled;
|
|
207
|
+
this.routeCache = this.buildRouteCache(compiled);
|
|
208
|
+
console.log(`✅ Reloaded: ${compiled.routes.length} routes`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=CompiledGraphEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CompiledGraphEngine.js","sourceRoot":"","sources":["../../src/runtime/CompiledGraphEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataLoader — Fetch les données pour un Trail résolu
|
|
3
|
+
*
|
|
4
|
+
* Fait le pont entre :
|
|
5
|
+
* Trail (sémantique — où on est, d'où on vient)
|
|
6
|
+
* QueryEngine (technique — comment fetcher les données)
|
|
7
|
+
* Provider (physique — SQL ou JSON en mémoire)
|
|
8
|
+
*
|
|
9
|
+
* Principe :
|
|
10
|
+
* Pour chaque frame RESOLVED dans le Trail, DataLoader
|
|
11
|
+
* construit la requête optimale depuis le graphe compilé
|
|
12
|
+
* et remplit frame.data avec les résultats.
|
|
13
|
+
*
|
|
14
|
+
* Deux modes de fetch :
|
|
15
|
+
* SQL — via Provider (PostgreSQL, MySQL...)
|
|
16
|
+
* JSON — via dataset en mémoire (mock, tests, Netflix JSON)
|
|
17
|
+
*
|
|
18
|
+
* Usage :
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const loader = new DataLoader(compiledGraph, { dataset })
|
|
21
|
+
* await loader.load(trail)
|
|
22
|
+
* // trail.current.data contient maintenant les données
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import type { CompiledGraph, Frame } from '../types/index.js';
|
|
26
|
+
import type { Trail } from '../navigation/Trail.js';
|
|
27
|
+
export interface DataLoaderOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Dataset JSON en mémoire — pour les providers mock ou Netflix JSON.
|
|
30
|
+
* Clé = nom de l'entité, valeur = tableau de rows.
|
|
31
|
+
*/
|
|
32
|
+
dataset?: Record<string, any[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Provider SQL — pour PostgreSQL, MySQL, etc.
|
|
35
|
+
* Si fourni, prend la priorité sur dataset.
|
|
36
|
+
*/
|
|
37
|
+
provider?: {
|
|
38
|
+
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Transforme les filtres d'une frame en paramètres SQL.
|
|
42
|
+
* Par défaut : { field: 'id', value: 1 } → WHERE entity.id = 1
|
|
43
|
+
*/
|
|
44
|
+
buildFilters?: (frame: Frame) => Record<string, any>;
|
|
45
|
+
}
|
|
46
|
+
export declare class DataLoader {
|
|
47
|
+
private queryEngine;
|
|
48
|
+
private options;
|
|
49
|
+
constructor(compiledGraph: CompiledGraph, options?: DataLoaderOptions);
|
|
50
|
+
/**
|
|
51
|
+
* Charge les données pour la frame courante du Trail.
|
|
52
|
+
*
|
|
53
|
+
* Stratégie :
|
|
54
|
+
* 1. Si la frame courante est UNRESOLVED → rien à fetcher
|
|
55
|
+
* 2. Si depth === 1 → fetch direct de l'entité (avec id si présent)
|
|
56
|
+
* 3. Si depth > 1 → traverse depuis le dernier ancêtre résolu
|
|
57
|
+
*
|
|
58
|
+
* Mutate trail.current.data avec les résultats.
|
|
59
|
+
*/
|
|
60
|
+
load(trail: Trail): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Charge les données pour toutes les frames RESOLVED du Trail.
|
|
63
|
+
* Utile pour les réponses enrichies (chaque frame a ses données).
|
|
64
|
+
*/
|
|
65
|
+
loadAll(trail: Trail): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Trouve le premier ancêtre résolu avec un id dans le Trail.
|
|
68
|
+
* C'est le point de départ de la traversée.
|
|
69
|
+
*/
|
|
70
|
+
private findAnchor;
|
|
71
|
+
/**
|
|
72
|
+
* Construit les filtres depuis le Trail.
|
|
73
|
+
* Combine les filtres de resolvedBy + l'id de l'ancêtre.
|
|
74
|
+
*/
|
|
75
|
+
private buildFilters;
|
|
76
|
+
/**
|
|
77
|
+
* Résout la clé primaire d'une entité depuis le graphe compilé.
|
|
78
|
+
* Fallback : {entity}_id (convention dvdrental, PostgreSQL standard).
|
|
79
|
+
*/
|
|
80
|
+
private pkOf;
|
|
81
|
+
/**
|
|
82
|
+
* Fetch direct — une seule entité, sans traversée.
|
|
83
|
+
*/
|
|
84
|
+
private fetchDirect;
|
|
85
|
+
/**
|
|
86
|
+
* Fetch via route compilée — traverse from → to.
|
|
87
|
+
*/
|
|
88
|
+
private fetchViaRoute;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=DataLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataLoader.d.ts","sourceRoot":"","sources":["../../src/runtime/DataLoader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAO,mBAAmB,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAuB,wBAAwB,CAAA;AAKpE,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAE/B;;;OAGG;IACH,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;KAC1D,CAAA;IAED;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACrD;AAID,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,OAAO,CAAuB;gBAE1B,aAAa,EAAE,aAAa,EAAE,OAAO,GAAE,iBAAsB;IAKzE;;;;;;;;;OASG;IACG,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvC;;;OAGG;IACG,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1C;;;OAGG;IACH,OAAO,CAAC,UAAU;IAWlB;;;OAGG;IACH,OAAO,CAAC,YAAY;IA0BpB;;;OAGG;IACH,OAAO,CAAC,IAAI;IAMZ;;OAEG;YACW,WAAW;IAoCzB;;OAEG;YACW,aAAa;CA4B5B"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataLoader — Fetch les données pour un Trail résolu
|
|
3
|
+
*
|
|
4
|
+
* Fait le pont entre :
|
|
5
|
+
* Trail (sémantique — où on est, d'où on vient)
|
|
6
|
+
* QueryEngine (technique — comment fetcher les données)
|
|
7
|
+
* Provider (physique — SQL ou JSON en mémoire)
|
|
8
|
+
*
|
|
9
|
+
* Principe :
|
|
10
|
+
* Pour chaque frame RESOLVED dans le Trail, DataLoader
|
|
11
|
+
* construit la requête optimale depuis le graphe compilé
|
|
12
|
+
* et remplit frame.data avec les résultats.
|
|
13
|
+
*
|
|
14
|
+
* Deux modes de fetch :
|
|
15
|
+
* SQL — via Provider (PostgreSQL, MySQL...)
|
|
16
|
+
* JSON — via dataset en mémoire (mock, tests, Netflix JSON)
|
|
17
|
+
*
|
|
18
|
+
* Usage :
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const loader = new DataLoader(compiledGraph, { dataset })
|
|
21
|
+
* await loader.load(trail)
|
|
22
|
+
* // trail.current.data contient maintenant les données
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import { QueryEngine } from './QueryEngine.js';
|
|
26
|
+
// ── DataLoader ────────────────────────────────────────────────
|
|
27
|
+
export class DataLoader {
|
|
28
|
+
queryEngine;
|
|
29
|
+
options;
|
|
30
|
+
constructor(compiledGraph, options = {}) {
|
|
31
|
+
this.queryEngine = new QueryEngine(compiledGraph);
|
|
32
|
+
this.options = options;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Charge les données pour la frame courante du Trail.
|
|
36
|
+
*
|
|
37
|
+
* Stratégie :
|
|
38
|
+
* 1. Si la frame courante est UNRESOLVED → rien à fetcher
|
|
39
|
+
* 2. Si depth === 1 → fetch direct de l'entité (avec id si présent)
|
|
40
|
+
* 3. Si depth > 1 → traverse depuis le dernier ancêtre résolu
|
|
41
|
+
*
|
|
42
|
+
* Mutate trail.current.data avec les résultats.
|
|
43
|
+
*/
|
|
44
|
+
async load(trail) {
|
|
45
|
+
const current = trail.current;
|
|
46
|
+
if (!current)
|
|
47
|
+
return;
|
|
48
|
+
if (current.state === 'UNRESOLVED')
|
|
49
|
+
return;
|
|
50
|
+
// Trouver l'ancêtre — point d'entrée de la traversée
|
|
51
|
+
const anchor = this.findAnchor(trail);
|
|
52
|
+
let data;
|
|
53
|
+
if (!anchor || anchor.entity === current.entity) {
|
|
54
|
+
const filters = current.id !== undefined ? { id: current.id } : {};
|
|
55
|
+
data = await this.fetchDirect(current.entity, filters);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const filters = anchor.id !== undefined ? { id: anchor.id } : {};
|
|
59
|
+
data = await this.fetchViaRoute(anchor.entity, current.entity, filters);
|
|
60
|
+
}
|
|
61
|
+
;
|
|
62
|
+
current.data = data;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Charge les données pour toutes les frames RESOLVED du Trail.
|
|
66
|
+
* Utile pour les réponses enrichies (chaque frame a ses données).
|
|
67
|
+
*/
|
|
68
|
+
async loadAll(trail) {
|
|
69
|
+
for (let i = 0; i < trail.depth; i++) {
|
|
70
|
+
const frame = trail.at(i);
|
|
71
|
+
if (!frame || frame.state !== 'RESOLVED')
|
|
72
|
+
continue;
|
|
73
|
+
const subTrail = trail.slice(i + 1);
|
|
74
|
+
await this.load(subTrail);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ── Privé ──────────────────────────────────────────────────
|
|
78
|
+
/**
|
|
79
|
+
* Trouve le premier ancêtre résolu avec un id dans le Trail.
|
|
80
|
+
* C'est le point de départ de la traversée.
|
|
81
|
+
*/
|
|
82
|
+
findAnchor(trail) {
|
|
83
|
+
// Remonter le Trail depuis l'avant-dernière frame
|
|
84
|
+
for (let i = trail.depth - 2; i >= 0; i--) {
|
|
85
|
+
const frame = trail.at(i);
|
|
86
|
+
if (frame?.state === 'RESOLVED' && frame.id !== undefined) {
|
|
87
|
+
return frame;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Construit les filtres depuis le Trail.
|
|
94
|
+
* Combine les filtres de resolvedBy + l'id de l'ancêtre.
|
|
95
|
+
*/
|
|
96
|
+
buildFilters(trail) {
|
|
97
|
+
const current = trail.current;
|
|
98
|
+
const filters = {};
|
|
99
|
+
// Filtre sur l'id de la frame courante si présent
|
|
100
|
+
if (current.id !== undefined) {
|
|
101
|
+
filters['id'] = current.id;
|
|
102
|
+
}
|
|
103
|
+
// Filtres portés par resolvedBy (conditions sémantiques)
|
|
104
|
+
if (current.resolvedBy?.filters) {
|
|
105
|
+
for (const f of current.resolvedBy.filters) {
|
|
106
|
+
if (f.operator === 'equals') {
|
|
107
|
+
filters[f.field] = f.value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Override par buildFilters custom si fourni
|
|
112
|
+
if (this.options.buildFilters) {
|
|
113
|
+
return { ...filters, ...this.options.buildFilters(current) };
|
|
114
|
+
}
|
|
115
|
+
return filters;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Résout la clé primaire d'une entité depuis le graphe compilé.
|
|
119
|
+
* Fallback : {entity}_id (convention dvdrental, PostgreSQL standard).
|
|
120
|
+
*/
|
|
121
|
+
pkOf(entity) {
|
|
122
|
+
const node = this.queryEngine.compiledGraph.nodes.find((n) => n.id === entity);
|
|
123
|
+
const pk = node?.primaryKey;
|
|
124
|
+
return Array.isArray(pk) ? pk[0] : (pk ?? `${entity}_id`);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Fetch direct — une seule entité, sans traversée.
|
|
128
|
+
*/
|
|
129
|
+
async fetchDirect(entity, filters) {
|
|
130
|
+
if (this.options.provider) {
|
|
131
|
+
// SQL via provider — résoudre la PK réelle au lieu de supposer 'id'
|
|
132
|
+
const conditions = Object.entries(filters)
|
|
133
|
+
.map(([k, v], i) => {
|
|
134
|
+
const col = k === 'id' ? this.pkOf(entity) : k;
|
|
135
|
+
return `${entity}.${col} = $${i + 1}`;
|
|
136
|
+
})
|
|
137
|
+
.join(' AND ');
|
|
138
|
+
const sql = conditions
|
|
139
|
+
? `SELECT * FROM ${entity} WHERE ${conditions}`
|
|
140
|
+
: `SELECT * FROM ${entity}`;
|
|
141
|
+
const params = Object.values(filters);
|
|
142
|
+
return this.options.provider.query(sql, params);
|
|
143
|
+
}
|
|
144
|
+
if (this.options.dataset) {
|
|
145
|
+
// JSON en mémoire — chercher sur la PK réelle ou 'id'
|
|
146
|
+
const pk = this.pkOf(entity);
|
|
147
|
+
const rows = this.options.dataset[entity] ?? [];
|
|
148
|
+
return rows.filter(row => Object.entries(filters).every(([k, v]) => {
|
|
149
|
+
const col = k === 'id' ? pk : k;
|
|
150
|
+
return row[col] === v;
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Fetch via route compilée — traverse from → to.
|
|
157
|
+
*/
|
|
158
|
+
async fetchViaRoute(from, to, filters) {
|
|
159
|
+
// Vérifier que la route existe
|
|
160
|
+
let route;
|
|
161
|
+
try {
|
|
162
|
+
route = this.queryEngine.getRoute(from, to);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return this.fetchDirect(to, filters);
|
|
166
|
+
}
|
|
167
|
+
if (this.options.provider) {
|
|
168
|
+
const sql = this.queryEngine.generateSQL({ from, to, filters });
|
|
169
|
+
return this.options.provider.query(sql);
|
|
170
|
+
}
|
|
171
|
+
if (this.options.dataset) {
|
|
172
|
+
const result = this.queryEngine.executeInMemory({ from, to, filters }, this.options.dataset);
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=DataLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataLoader.js","sourceRoot":"","sources":["../../src/runtime/DataLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine - Core runtime engine with LRU cache
|
|
3
|
+
*
|
|
4
|
+
* Bus exposés :
|
|
5
|
+
* engine.events — cache.hit, cache.miss
|
|
6
|
+
* engine.errors — (extensible)
|
|
7
|
+
*/
|
|
8
|
+
import type { Provider, EngineConfig, CacheStats } from '../types/index.js';
|
|
9
|
+
import { EventBus, ErrorBus } from '../core/EventBus.js';
|
|
10
|
+
import type { CacheHitPayload, CacheMissPayload, GraphErrors } from '../core/GraphEvents.js';
|
|
11
|
+
interface EngineEventMap {
|
|
12
|
+
'cache.hit': CacheHitPayload;
|
|
13
|
+
'cache.miss': CacheMissPayload;
|
|
14
|
+
}
|
|
15
|
+
export declare class Engine {
|
|
16
|
+
private provider;
|
|
17
|
+
maxSize: number;
|
|
18
|
+
private cache;
|
|
19
|
+
private hits;
|
|
20
|
+
private misses;
|
|
21
|
+
readonly events: EventBus<EngineEventMap>;
|
|
22
|
+
readonly errors: ErrorBus<GraphErrors>;
|
|
23
|
+
constructor(provider: Provider, config?: EngineConfig);
|
|
24
|
+
get<T = any>(key: string, fetcher: () => Promise<T>): Promise<T>;
|
|
25
|
+
set<T = any>(key: string, value: T): void;
|
|
26
|
+
private evictIfNeeded;
|
|
27
|
+
clearCache(): void;
|
|
28
|
+
getStats(): CacheStats;
|
|
29
|
+
private getCurrentSize;
|
|
30
|
+
private estimateSize;
|
|
31
|
+
private formatSize;
|
|
32
|
+
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
|
33
|
+
close(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=Engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/runtime/Engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAW5F,UAAU,cAAc;IACtB,WAAW,EAAG,eAAe,CAAA;IAC7B,YAAY,EAAE,gBAAgB,CAAA;CAC/B;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAU;IACnB,OAAO,EAAI,MAAM,CAAA;IACxB,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,MAAM,CAAU;IAGxB,SAAgB,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAA;IAChD,SAAgB,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAA;gBAEjC,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAE,YAAiB;IAUnD,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA8BtE,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAiBzC,OAAO,CAAC,aAAa;IAmBrB,UAAU,IAAI,IAAI;IAOlB,QAAQ,IAAI,UAAU;IAmBtB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,UAAU;IAMZ,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAI7D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine - Core runtime engine with LRU cache
|
|
3
|
+
*
|
|
4
|
+
* Bus exposés :
|
|
5
|
+
* engine.events — cache.hit, cache.miss
|
|
6
|
+
* engine.errors — (extensible)
|
|
7
|
+
*/
|
|
8
|
+
import { EventBus, ErrorBus } from '../core/EventBus.js';
|
|
9
|
+
export class Engine {
|
|
10
|
+
provider;
|
|
11
|
+
maxSize;
|
|
12
|
+
cache;
|
|
13
|
+
hits;
|
|
14
|
+
misses;
|
|
15
|
+
// ── Bus ──────────────────────────────────────────────────────
|
|
16
|
+
events;
|
|
17
|
+
errors;
|
|
18
|
+
constructor(provider, config = {}) {
|
|
19
|
+
this.provider = provider;
|
|
20
|
+
this.maxSize = config.cache?.maxSize ?? 10 * 1024 * 1024;
|
|
21
|
+
this.cache = new Map();
|
|
22
|
+
this.hits = 0;
|
|
23
|
+
this.misses = 0;
|
|
24
|
+
this.events = new EventBus();
|
|
25
|
+
this.errors = new ErrorBus();
|
|
26
|
+
}
|
|
27
|
+
async get(key, fetcher) {
|
|
28
|
+
const cached = this.cache.get(key);
|
|
29
|
+
if (cached) {
|
|
30
|
+
this.hits++;
|
|
31
|
+
cached.accessCount++;
|
|
32
|
+
cached.lastAccess = Date.now();
|
|
33
|
+
// ── Event : cache.hit ──────────────────────────────────
|
|
34
|
+
this.events.emit('cache.hit', {
|
|
35
|
+
key,
|
|
36
|
+
accessCount: cached.accessCount,
|
|
37
|
+
});
|
|
38
|
+
return cached.value;
|
|
39
|
+
}
|
|
40
|
+
this.misses++;
|
|
41
|
+
// ── Event : cache.miss ─────────────────────────────────
|
|
42
|
+
this.events.emit('cache.miss', {
|
|
43
|
+
key,
|
|
44
|
+
requestedAt: Date.now(),
|
|
45
|
+
});
|
|
46
|
+
const value = await fetcher();
|
|
47
|
+
this.set(key, value);
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
set(key, value) {
|
|
51
|
+
const size = this.estimateSize(value);
|
|
52
|
+
this.evictIfNeeded(size);
|
|
53
|
+
this.cache.set(key, {
|
|
54
|
+
key,
|
|
55
|
+
value,
|
|
56
|
+
size,
|
|
57
|
+
accessCount: 1,
|
|
58
|
+
lastAccess: Date.now(),
|
|
59
|
+
});
|
|
60
|
+
const formatted = this.formatSize(size);
|
|
61
|
+
const maxFormatted = this.formatSize(this.maxSize);
|
|
62
|
+
console.log(`💾 RAM CACHED: ${key} (${formatted}/${maxFormatted})`);
|
|
63
|
+
}
|
|
64
|
+
evictIfNeeded(neededSize) {
|
|
65
|
+
const currentSize = this.getCurrentSize();
|
|
66
|
+
if (currentSize + neededSize <= this.maxSize)
|
|
67
|
+
return;
|
|
68
|
+
const entries = Array.from(this.cache.values()).sort((a, b) => {
|
|
69
|
+
const scoreA = a.accessCount * 1000 + a.lastAccess;
|
|
70
|
+
const scoreB = b.accessCount * 1000 + b.lastAccess;
|
|
71
|
+
return scoreA - scoreB;
|
|
72
|
+
});
|
|
73
|
+
let freedSize = 0;
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
if (currentSize - freedSize + neededSize <= this.maxSize)
|
|
76
|
+
break;
|
|
77
|
+
this.cache.delete(entry.key);
|
|
78
|
+
freedSize += entry.size;
|
|
79
|
+
console.log(`🗑️ RAM EVICTED: ${entry.key}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
clearCache() {
|
|
83
|
+
this.cache.clear();
|
|
84
|
+
this.hits = 0;
|
|
85
|
+
this.misses = 0;
|
|
86
|
+
console.log('💾 RAM CLEARED');
|
|
87
|
+
}
|
|
88
|
+
getStats() {
|
|
89
|
+
const size = this.getCurrentSize();
|
|
90
|
+
const totalAccesses = this.hits + this.misses;
|
|
91
|
+
const hitRate = totalAccesses > 0
|
|
92
|
+
? ((this.hits / totalAccesses) * 100).toFixed(1) + '%'
|
|
93
|
+
: '0%';
|
|
94
|
+
return {
|
|
95
|
+
entries: this.cache.size,
|
|
96
|
+
size,
|
|
97
|
+
sizeFormatted: this.formatSize(size),
|
|
98
|
+
maxSize: this.maxSize,
|
|
99
|
+
usage: ((size / this.maxSize) * 100).toFixed(1) + '%',
|
|
100
|
+
hits: this.hits,
|
|
101
|
+
misses: this.misses,
|
|
102
|
+
hitRate,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
getCurrentSize() {
|
|
106
|
+
let total = 0;
|
|
107
|
+
for (const entry of this.cache.values())
|
|
108
|
+
total += entry.size;
|
|
109
|
+
return total;
|
|
110
|
+
}
|
|
111
|
+
estimateSize(obj) {
|
|
112
|
+
return JSON.stringify(obj).length;
|
|
113
|
+
}
|
|
114
|
+
formatSize(bytes) {
|
|
115
|
+
if (bytes < 1024)
|
|
116
|
+
return `${bytes} B`;
|
|
117
|
+
if (bytes < 1024 * 1024)
|
|
118
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
119
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
120
|
+
}
|
|
121
|
+
async query(sql, params = []) {
|
|
122
|
+
return this.provider.query(sql, params);
|
|
123
|
+
}
|
|
124
|
+
async close() {
|
|
125
|
+
await this.provider.close();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=Engine.js.map
|