@linklabjs/core 0.1.0 → 0.1.1
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,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphOptimizer — Analyse et rapport sur la qualité du graphe
|
|
3
|
+
*
|
|
4
|
+
* PRINCIPE : signaler, jamais détruire silencieusement.
|
|
5
|
+
*
|
|
6
|
+
* Chaque étape produit un rapport (warnings, suggestions).
|
|
7
|
+
* Le dev décide ensuite de ce qu'il fait.
|
|
8
|
+
*
|
|
9
|
+
* Seules deux opérations sont automatiques et non destructives :
|
|
10
|
+
* - Suppression des nœuds orphelins (aucune arête — objectivement inutiles)
|
|
11
|
+
* - Suppression des nœuds dead-end stricts (aucune arête entrante ET sortante)
|
|
12
|
+
*
|
|
13
|
+
* Les cycles sont DÉTECTÉS et CLASSIFIÉS, jamais supprimés :
|
|
14
|
+
* - SELF_LOOP : arête A → A (ex: Station-chatelet → Station-chatelet TRANSFER)
|
|
15
|
+
* - BIDIRECTIONAL : A → B et B → A (ex: CREATED + CREDITED — intentionnel)
|
|
16
|
+
* - STRUCTURAL_CYCLE : A → B → C → A (même type de relation — potentiellement problématique)
|
|
17
|
+
*/
|
|
18
|
+
import { PathFinder } from '../core/PathFinder.js';
|
|
19
|
+
const DEFAULT_CONFIG = {
|
|
20
|
+
intentionalBidirectional: ['physical_reverse'],
|
|
21
|
+
intentionalSelfLoops: []
|
|
22
|
+
};
|
|
23
|
+
export class GraphOptimizer {
|
|
24
|
+
graph;
|
|
25
|
+
config;
|
|
26
|
+
constructor(graph, config = {}) {
|
|
27
|
+
this.graph = graph;
|
|
28
|
+
this.config = {
|
|
29
|
+
intentionalBidirectional: config.intentionalBidirectional ?? DEFAULT_CONFIG.intentionalBidirectional,
|
|
30
|
+
intentionalSelfLoops: config.intentionalSelfLoops ?? DEFAULT_CONFIG.intentionalSelfLoops
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Pipeline complet — retourne un rapport, ne modifie pas le graphe original.
|
|
35
|
+
* Seuls orphelins et dead-ends stricts sont supprimés (safe).
|
|
36
|
+
*/
|
|
37
|
+
optimize() {
|
|
38
|
+
console.log('🔧 GraphOptimizer — analyse du graphe...');
|
|
39
|
+
const before = {
|
|
40
|
+
nodes: this.graph.nodes.length,
|
|
41
|
+
edges: this.graph.edges.length
|
|
42
|
+
};
|
|
43
|
+
// Travailler sur une copie
|
|
44
|
+
const working = {
|
|
45
|
+
nodes: [...this.graph.nodes],
|
|
46
|
+
edges: [...this.graph.edges]
|
|
47
|
+
};
|
|
48
|
+
// Opérations safe (non destructives sémantiquement)
|
|
49
|
+
const removedOrphans = this.removeOrphans(working);
|
|
50
|
+
const removedDeadEnds = this.removeStrictDeadEnds(working);
|
|
51
|
+
// Analyse — rapport uniquement, pas de suppression
|
|
52
|
+
const cycles = this.detectCycles(working);
|
|
53
|
+
const duplicatePaths = this.detectDuplicatePaths(working);
|
|
54
|
+
const after = {
|
|
55
|
+
nodes: working.nodes.length,
|
|
56
|
+
edges: working.edges.length
|
|
57
|
+
};
|
|
58
|
+
// Résumé console
|
|
59
|
+
console.log(` Nœuds : ${before.nodes} → ${after.nodes} (-${before.nodes - after.nodes})`);
|
|
60
|
+
console.log(` Arêtes : ${before.edges} → ${after.edges} (-${before.edges - after.edges})`);
|
|
61
|
+
console.log(` Cycles : ${cycles.length} détecté(s)`);
|
|
62
|
+
console.log(` Chemins dupliqués : ${duplicatePaths.length} paire(s)`);
|
|
63
|
+
const isClean = cycles.filter(c => c.severity === 'WARNING').length === 0;
|
|
64
|
+
if (isClean) {
|
|
65
|
+
console.log(' ✅ Graphe propre');
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
console.log(` ⚠️ ${cycles.filter(c => c.severity === 'WARNING').length} warning(s) à examiner`);
|
|
69
|
+
}
|
|
70
|
+
const report = {
|
|
71
|
+
graph: working,
|
|
72
|
+
summary: {
|
|
73
|
+
nodes: { before: before.nodes, after: after.nodes, removed: before.nodes - after.nodes },
|
|
74
|
+
edges: { before: before.edges, after: after.edges, removed: before.edges - after.edges }
|
|
75
|
+
},
|
|
76
|
+
cycles,
|
|
77
|
+
duplicatePaths,
|
|
78
|
+
removedOrphans,
|
|
79
|
+
removedDeadEnds,
|
|
80
|
+
isClean
|
|
81
|
+
};
|
|
82
|
+
this.printReport(report);
|
|
83
|
+
return report;
|
|
84
|
+
}
|
|
85
|
+
// ==================== CYCLES ====================
|
|
86
|
+
/**
|
|
87
|
+
* Détecte et classifie les cycles — ne supprime rien.
|
|
88
|
+
*/
|
|
89
|
+
detectCycles(graph) {
|
|
90
|
+
const warnings = [];
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
for (const edge of graph.edges) {
|
|
93
|
+
// 1. SELF_LOOP : A → A
|
|
94
|
+
if (edge.from === edge.to) {
|
|
95
|
+
const key = `SELF:${edge.name}`;
|
|
96
|
+
if (!seen.has(key)) {
|
|
97
|
+
seen.add(key);
|
|
98
|
+
const edgeType = edge.metadata?.type ?? edge.via ?? '';
|
|
99
|
+
const isIntentional = this.config.intentionalSelfLoops.includes(edgeType);
|
|
100
|
+
warnings.push({
|
|
101
|
+
type: 'SELF_LOOP',
|
|
102
|
+
severity: 'INFO',
|
|
103
|
+
edges: [edge.name ?? `${edge.from}→${edge.to}`],
|
|
104
|
+
nodes: [edge.from],
|
|
105
|
+
note: isIntentional
|
|
106
|
+
? `Self-loop intentionnel (${edgeType}) sur ${edge.from}. Géré par Dijkstra.`
|
|
107
|
+
: `Boucle sur ${edge.from}. Géré par Dijkstra (visited), inoffensif.`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
// 2. BIDIRECTIONAL : A → B et B → A
|
|
113
|
+
const reverse = graph.edges.find(e => e.from === edge.to && e.to === edge.from);
|
|
114
|
+
if (reverse) {
|
|
115
|
+
const key = [edge.from, edge.to].sort().join('↔');
|
|
116
|
+
if (!seen.has(key)) {
|
|
117
|
+
seen.add(key);
|
|
118
|
+
const typeA = edge.metadata?.type ?? '';
|
|
119
|
+
const typeB = reverse.metadata?.type ?? '';
|
|
120
|
+
const sameType = typeA === typeB;
|
|
121
|
+
const isIntentional = !sameType ||
|
|
122
|
+
this.config.intentionalBidirectional.includes(typeA) ||
|
|
123
|
+
this.config.intentionalBidirectional.includes(typeB);
|
|
124
|
+
warnings.push({
|
|
125
|
+
type: 'BIDIRECTIONAL',
|
|
126
|
+
severity: isIntentional ? 'INFO' : 'WARNING',
|
|
127
|
+
edges: [
|
|
128
|
+
edge.name ?? `${edge.from}→${edge.to}`,
|
|
129
|
+
reverse.name ?? `${reverse.from}→${reverse.to}`
|
|
130
|
+
],
|
|
131
|
+
nodes: [edge.from, edge.to],
|
|
132
|
+
note: isIntentional
|
|
133
|
+
? `Bidirectionnel intentionnel (${typeA} ↔ ${typeB}) — normal.`
|
|
134
|
+
: `Bidirectionnel de même type "${typeA}" non déclaré intentionnel — vérifier.`
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// 3. STRUCTURAL_CYCLE : A → B → C → A (même type de relation)
|
|
140
|
+
const structuralCycles = this.detectStructuralCycles(graph);
|
|
141
|
+
warnings.push(...structuralCycles);
|
|
142
|
+
return warnings;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Détecte les cycles structurels A → B → ... → A
|
|
146
|
+
* en ne suivant que les arêtes du même type.
|
|
147
|
+
*/
|
|
148
|
+
detectStructuralCycles(graph) {
|
|
149
|
+
const warnings = [];
|
|
150
|
+
const reportedCycles = new Set();
|
|
151
|
+
// Grouper les arêtes par type
|
|
152
|
+
const byType = new Map();
|
|
153
|
+
for (const edge of graph.edges) {
|
|
154
|
+
const type = edge.metadata?.type ?? edge.via ?? 'unknown';
|
|
155
|
+
if (!byType.has(type))
|
|
156
|
+
byType.set(type, []);
|
|
157
|
+
byType.get(type).push(edge);
|
|
158
|
+
}
|
|
159
|
+
for (const [type, edges] of byType) {
|
|
160
|
+
// DFS sur les arêtes de ce type uniquement
|
|
161
|
+
const visited = new Set();
|
|
162
|
+
const inPath = new Set();
|
|
163
|
+
const pathStack = [];
|
|
164
|
+
const dfs = (node) => {
|
|
165
|
+
if (inPath.has(node)) {
|
|
166
|
+
// Cycle trouvé — extraire le cycle
|
|
167
|
+
const cycleStart = pathStack.indexOf(node);
|
|
168
|
+
return pathStack.slice(cycleStart);
|
|
169
|
+
}
|
|
170
|
+
if (visited.has(node))
|
|
171
|
+
return null;
|
|
172
|
+
visited.add(node);
|
|
173
|
+
inPath.add(node);
|
|
174
|
+
pathStack.push(node);
|
|
175
|
+
const neighbors = edges.filter(e => e.from === node).map(e => e.to);
|
|
176
|
+
for (const neighbor of neighbors) {
|
|
177
|
+
const cycle = dfs(neighbor);
|
|
178
|
+
if (cycle)
|
|
179
|
+
return cycle;
|
|
180
|
+
}
|
|
181
|
+
pathStack.pop();
|
|
182
|
+
inPath.delete(node);
|
|
183
|
+
return null;
|
|
184
|
+
};
|
|
185
|
+
for (const edge of edges) {
|
|
186
|
+
const cycle = dfs(edge.from);
|
|
187
|
+
if (cycle) {
|
|
188
|
+
const key = [...cycle].sort().join(',');
|
|
189
|
+
if (!reportedCycles.has(key)) {
|
|
190
|
+
reportedCycles.add(key);
|
|
191
|
+
warnings.push({
|
|
192
|
+
type: 'STRUCTURAL_CYCLE',
|
|
193
|
+
severity: 'WARNING',
|
|
194
|
+
edges: [],
|
|
195
|
+
nodes: cycle,
|
|
196
|
+
note: `Cycle structurel sur le type "${type}" : ${cycle.join(' → ')} → ${cycle[0]}`
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
visited.clear();
|
|
201
|
+
inPath.clear();
|
|
202
|
+
pathStack.length = 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return warnings;
|
|
206
|
+
}
|
|
207
|
+
// ==================== SUPPRESSIONS SAFE ====================
|
|
208
|
+
/**
|
|
209
|
+
* Supprime les nœuds sans aucune arête (entrante ou sortante).
|
|
210
|
+
* Inoffensif — un nœud isolé ne contribue à aucune traversée.
|
|
211
|
+
*/
|
|
212
|
+
removeOrphans(graph) {
|
|
213
|
+
const connected = new Set();
|
|
214
|
+
for (const edge of graph.edges) {
|
|
215
|
+
connected.add(edge.from);
|
|
216
|
+
connected.add(edge.to);
|
|
217
|
+
}
|
|
218
|
+
const orphans = graph.nodes
|
|
219
|
+
.filter(n => !connected.has(n.id))
|
|
220
|
+
.map(n => n.id);
|
|
221
|
+
graph.nodes = graph.nodes.filter(n => connected.has(n.id));
|
|
222
|
+
if (orphans.length > 0) {
|
|
223
|
+
console.log(` 🗑️ Orphelins supprimés : ${orphans.join(', ')}`);
|
|
224
|
+
}
|
|
225
|
+
return orphans;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Supprime les nœuds sans arête entrante ET sans arête sortante
|
|
229
|
+
* après suppression des orphelins.
|
|
230
|
+
* Différent de removeOrphans — cible les nœuds stricts.
|
|
231
|
+
*/
|
|
232
|
+
removeStrictDeadEnds(graph) {
|
|
233
|
+
const hasIncoming = new Set();
|
|
234
|
+
const hasOutgoing = new Set();
|
|
235
|
+
for (const edge of graph.edges) {
|
|
236
|
+
hasOutgoing.add(edge.from);
|
|
237
|
+
hasIncoming.add(edge.to);
|
|
238
|
+
}
|
|
239
|
+
const deadEnds = graph.nodes
|
|
240
|
+
.filter(n => !hasIncoming.has(n.id) && !hasOutgoing.has(n.id))
|
|
241
|
+
.map(n => n.id);
|
|
242
|
+
// Déjà couverts par removeOrphans — cette passe est redondante
|
|
243
|
+
// mais explicite pour la lisibilité
|
|
244
|
+
graph.nodes = graph.nodes.filter(n => !deadEnds.includes(n.id));
|
|
245
|
+
return deadEnds;
|
|
246
|
+
}
|
|
247
|
+
// ==================== DUPLICATES ====================
|
|
248
|
+
/**
|
|
249
|
+
* Détecte les paires de nœuds avec plusieurs chemins possibles.
|
|
250
|
+
* Informatif — les chemins multiples sont souvent intentionnels (fallbacks).
|
|
251
|
+
*/
|
|
252
|
+
detectDuplicatePaths(graph) {
|
|
253
|
+
const warnings = [];
|
|
254
|
+
const finder = new PathFinder(graph);
|
|
255
|
+
for (const from of graph.nodes) {
|
|
256
|
+
for (const to of graph.nodes) {
|
|
257
|
+
if (from.id === to.id)
|
|
258
|
+
continue;
|
|
259
|
+
try {
|
|
260
|
+
const paths = finder.findAllPaths(from.id, to.id, 5);
|
|
261
|
+
if (paths.length > 1) {
|
|
262
|
+
warnings.push({
|
|
263
|
+
from: from.id,
|
|
264
|
+
to: to.id,
|
|
265
|
+
paths: paths.map(p => p),
|
|
266
|
+
note: `${paths.length} chemins entre ${from.id} et ${to.id} — le plus court sera utilisé par défaut.`
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// Ignorer les erreurs de traversée
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return warnings;
|
|
276
|
+
}
|
|
277
|
+
// ==================== RAPPORT ====================
|
|
278
|
+
printReport(report) {
|
|
279
|
+
if (report.cycles.length === 0 && report.duplicatePaths.length === 0)
|
|
280
|
+
return;
|
|
281
|
+
console.log('\n📋 RAPPORT GraphOptimizer\n');
|
|
282
|
+
if (report.removedOrphans.length > 0) {
|
|
283
|
+
console.log(`🗑️ Orphelins supprimés (${report.removedOrphans.length}) :`);
|
|
284
|
+
report.removedOrphans.forEach(n => console.log(` - ${n}`));
|
|
285
|
+
}
|
|
286
|
+
// Cycles WARNING uniquement (les INFO sont attendus)
|
|
287
|
+
const cycleWarnings = report.cycles.filter(c => c.severity === 'WARNING');
|
|
288
|
+
if (cycleWarnings.length > 0) {
|
|
289
|
+
console.log(`\n⚠️ Cycles à examiner (${cycleWarnings.length}) :`);
|
|
290
|
+
cycleWarnings.forEach(c => {
|
|
291
|
+
console.log(` [${c.type}] ${c.note}`);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
const cycleInfos = report.cycles.filter(c => c.severity === 'INFO');
|
|
295
|
+
if (cycleInfos.length > 0) {
|
|
296
|
+
console.log(`\nℹ️ Cycles intentionnels (${cycleInfos.length}) :`);
|
|
297
|
+
cycleInfos.forEach(c => {
|
|
298
|
+
console.log(` [${c.type}] ${c.note}`);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (report.duplicatePaths.length > 0) {
|
|
302
|
+
console.log(`\nℹ️ Chemins multiples (${report.duplicatePaths.length} paires) — fallbacks disponibles`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=GraphOptimizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphOptimizer.js","sourceRoot":"","sources":["../../src/graph/GraphOptimizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphTrainer - Trains graph with real use cases
|
|
3
|
+
*
|
|
4
|
+
* Benchmarks all paths and assigns weights based on actual performance
|
|
5
|
+
*/
|
|
6
|
+
import type { Graph, UseCase, MetricsMap, TrainingMetrics, Provider } from '../types/index.js';
|
|
7
|
+
export declare class GraphTrainer {
|
|
8
|
+
private graph;
|
|
9
|
+
private provider;
|
|
10
|
+
private metrics;
|
|
11
|
+
constructor(graph: Graph, provider: Provider);
|
|
12
|
+
/**
|
|
13
|
+
* Train graph with use cases
|
|
14
|
+
*/
|
|
15
|
+
train(useCases: UseCase[]): Promise<MetricsMap>;
|
|
16
|
+
/**
|
|
17
|
+
* Train single use case
|
|
18
|
+
*/
|
|
19
|
+
private trainUseCase;
|
|
20
|
+
/**
|
|
21
|
+
* Benchmark a specific path
|
|
22
|
+
*/
|
|
23
|
+
private benchmarkPath;
|
|
24
|
+
/**
|
|
25
|
+
* Build SQL query for a path
|
|
26
|
+
*/
|
|
27
|
+
private buildQuery;
|
|
28
|
+
/**
|
|
29
|
+
* Update graph weights based on metrics
|
|
30
|
+
*/
|
|
31
|
+
updateWeights(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Check if path uses edge
|
|
34
|
+
*/
|
|
35
|
+
private pathUsesEdge;
|
|
36
|
+
/**
|
|
37
|
+
* Get training statistics
|
|
38
|
+
*/
|
|
39
|
+
getStats(): {
|
|
40
|
+
total: number;
|
|
41
|
+
successful: number;
|
|
42
|
+
failed: number;
|
|
43
|
+
avgTime: number;
|
|
44
|
+
fastest: TrainingMetrics | undefined;
|
|
45
|
+
slowest: TrainingMetrics | undefined;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Get metrics map
|
|
49
|
+
*/
|
|
50
|
+
getMetrics(): MetricsMap;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=GraphTrainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphTrainer.d.ts","sourceRoot":"","sources":["../../src/graph/GraphTrainer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAG9F,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAO;IACpB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAY;gBAEf,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ;IAM5C;;OAEG;IACG,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAcrD;;OAEG;YACW,YAAY;IAe1B;;OAEG;YACW,aAAa;IAgE3B;;OAEG;IACH,OAAO,CAAC,UAAU;IAiClB;;OAEG;IACH,aAAa,IAAI,IAAI;IA4BrB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;OAEG;IACH,QAAQ,IAAI;QACV,KAAK,EAAE,MAAM,CAAA;QACb,UAAU,EAAE,MAAM,CAAA;QAClB,MAAM,EAAE,MAAM,CAAA;QACd,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;QACpC,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;KACrC;IA6BD;;OAEG;IACH,UAAU,IAAI,UAAU;CAGzB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphTrainer - Trains graph with real use cases
|
|
3
|
+
*
|
|
4
|
+
* Benchmarks all paths and assigns weights based on actual performance
|
|
5
|
+
*/
|
|
6
|
+
import { PathFinder } from '../core/PathFinder.js';
|
|
7
|
+
export class GraphTrainer {
|
|
8
|
+
graph;
|
|
9
|
+
provider;
|
|
10
|
+
metrics;
|
|
11
|
+
constructor(graph, provider) {
|
|
12
|
+
this.graph = graph;
|
|
13
|
+
this.provider = provider;
|
|
14
|
+
this.metrics = new Map();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Train graph with use cases
|
|
18
|
+
*/
|
|
19
|
+
async train(useCases) {
|
|
20
|
+
console.log(`🎓 Training graph with ${useCases.length} use cases...\n`);
|
|
21
|
+
for (const [index, useCase] of useCases.entries()) {
|
|
22
|
+
console.log(` [${index + 1}/${useCases.length}] ${useCase.description}`);
|
|
23
|
+
await this.trainUseCase(useCase);
|
|
24
|
+
}
|
|
25
|
+
console.log('\n✅ Training complete');
|
|
26
|
+
console.log(` Tested ${this.metrics.size} unique paths`);
|
|
27
|
+
return this.metrics;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Train single use case
|
|
31
|
+
*/
|
|
32
|
+
async trainUseCase(useCase) {
|
|
33
|
+
const { from, to, sampleData } = useCase;
|
|
34
|
+
// Find all paths
|
|
35
|
+
const finder = new PathFinder(this.graph);
|
|
36
|
+
const paths = finder.findAllPaths(from, to);
|
|
37
|
+
console.log(` Found ${paths.length} possible paths`);
|
|
38
|
+
// Benchmark each path
|
|
39
|
+
for (const path of paths) {
|
|
40
|
+
await this.benchmarkPath(path, sampleData);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Benchmark a specific path
|
|
45
|
+
*/
|
|
46
|
+
async benchmarkPath(path, sampleData) {
|
|
47
|
+
const pathKey = path.join('→');
|
|
48
|
+
try {
|
|
49
|
+
// Build SQL query
|
|
50
|
+
const query = this.buildQuery(path, sampleData);
|
|
51
|
+
// Execute multiple times for average
|
|
52
|
+
const iterations = 3;
|
|
53
|
+
const times = [];
|
|
54
|
+
for (let i = 0; i < iterations; i++) {
|
|
55
|
+
const start = performance.now();
|
|
56
|
+
await this.provider.query(query.sql, query.params);
|
|
57
|
+
const duration = performance.now() - start;
|
|
58
|
+
times.push(duration);
|
|
59
|
+
}
|
|
60
|
+
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
61
|
+
const minTime = Math.min(...times);
|
|
62
|
+
const maxTime = Math.max(...times);
|
|
63
|
+
// Store metrics
|
|
64
|
+
if (!this.metrics.has(pathKey)) {
|
|
65
|
+
this.metrics.set(pathKey, {
|
|
66
|
+
path,
|
|
67
|
+
executions: 0,
|
|
68
|
+
successes: 0,
|
|
69
|
+
failures: 0,
|
|
70
|
+
totalTime: 0,
|
|
71
|
+
avgTime: 0,
|
|
72
|
+
minTime: Infinity,
|
|
73
|
+
maxTime: 0,
|
|
74
|
+
used: true
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const metric = this.metrics.get(pathKey);
|
|
78
|
+
metric.executions += iterations;
|
|
79
|
+
metric.successes = (metric.successes || 0) + iterations;
|
|
80
|
+
metric.totalTime += avgTime * iterations;
|
|
81
|
+
metric.avgTime = metric.totalTime / metric.executions;
|
|
82
|
+
metric.minTime = Math.min(metric.minTime, minTime);
|
|
83
|
+
metric.maxTime = Math.max(metric.maxTime, maxTime);
|
|
84
|
+
console.log(` ✓ ${pathKey}: ${avgTime.toFixed(2)}ms avg`);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.log(` ✗ ${pathKey}: Failed - ${err.message}`);
|
|
88
|
+
// Mark as failed
|
|
89
|
+
this.metrics.set(pathKey, {
|
|
90
|
+
path,
|
|
91
|
+
executions: 0,
|
|
92
|
+
totalTime: 0,
|
|
93
|
+
avgTime: 0,
|
|
94
|
+
minTime: 0,
|
|
95
|
+
maxTime: 0,
|
|
96
|
+
used: false,
|
|
97
|
+
failed: true,
|
|
98
|
+
error: err.message
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Build SQL query for a path
|
|
104
|
+
*/
|
|
105
|
+
buildQuery(path, sampleData) {
|
|
106
|
+
// Start with first table
|
|
107
|
+
let sql = `SELECT * FROM ${path[0]}`;
|
|
108
|
+
const params = [];
|
|
109
|
+
// Add JOINs
|
|
110
|
+
for (let i = 1; i < path.length; i++) {
|
|
111
|
+
const from = path[i - 1];
|
|
112
|
+
const to = path[i];
|
|
113
|
+
// Find edge
|
|
114
|
+
const edge = this.graph.edges.find(e => e.from === from && e.to === to);
|
|
115
|
+
if (edge) {
|
|
116
|
+
sql += ` JOIN ${to} ON ${from}.${edge.via} = ${to}.id`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Add WHERE if sample data provided
|
|
120
|
+
if (sampleData?.id) {
|
|
121
|
+
sql += ` WHERE ${path[0]}.id = $${params.length + 1}`;
|
|
122
|
+
params.push(sampleData.id);
|
|
123
|
+
}
|
|
124
|
+
// Limit for safety
|
|
125
|
+
sql += ' LIMIT 100';
|
|
126
|
+
return { sql, params };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Update graph weights based on metrics
|
|
130
|
+
*/
|
|
131
|
+
updateWeights() {
|
|
132
|
+
console.log('📊 Updating graph weights based on metrics...');
|
|
133
|
+
let updated = 0;
|
|
134
|
+
for (const edge of this.graph.edges) {
|
|
135
|
+
// Find all paths using this edge
|
|
136
|
+
const pathsWithEdge = Array.from(this.metrics.values()).filter(m => !m.failed && this.pathUsesEdge(m.path, edge));
|
|
137
|
+
if (pathsWithEdge.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
// Calculate new weight (average time)
|
|
140
|
+
const avgTime = pathsWithEdge.reduce((sum, m) => sum + m.avgTime, 0) / pathsWithEdge.length;
|
|
141
|
+
// Normalize to 0-100 scale
|
|
142
|
+
const newWeight = Math.min(100, avgTime);
|
|
143
|
+
if (edge.weight !== newWeight) {
|
|
144
|
+
edge.weight = newWeight;
|
|
145
|
+
updated++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
console.log(` Updated ${updated} edge weights`);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if path uses edge
|
|
152
|
+
*/
|
|
153
|
+
pathUsesEdge(path, edge) {
|
|
154
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
155
|
+
if (path[i] === edge.from && path[i + 1] === edge.to) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get training statistics
|
|
163
|
+
*/
|
|
164
|
+
getStats() {
|
|
165
|
+
const successful = Array.from(this.metrics.values()).filter(m => !m.failed);
|
|
166
|
+
const failed = Array.from(this.metrics.values()).filter(m => m.failed);
|
|
167
|
+
const avgTime = successful.length > 0
|
|
168
|
+
? successful.reduce((sum, m) => sum + m.avgTime, 0) / successful.length
|
|
169
|
+
: 0;
|
|
170
|
+
const fastest = successful.reduce((min, m) => (!min || m.avgTime < min.avgTime ? m : min), undefined);
|
|
171
|
+
const slowest = successful.reduce((max, m) => (!max || m.avgTime > max.avgTime ? m : max), undefined);
|
|
172
|
+
return {
|
|
173
|
+
total: this.metrics.size,
|
|
174
|
+
successful: successful.length,
|
|
175
|
+
failed: failed.length,
|
|
176
|
+
avgTime,
|
|
177
|
+
fastest,
|
|
178
|
+
slowest
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get metrics map
|
|
183
|
+
*/
|
|
184
|
+
getMetrics() {
|
|
185
|
+
return this.metrics;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=GraphTrainer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GraphTrainer.js","sourceRoot":"","sources":["../../src/graph/GraphTrainer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LinkBuilder — Génère les liens HATEOAS depuis le graphe
|
|
3
|
+
*
|
|
4
|
+
* Logique pure, sans dépendance à Fastify.
|
|
5
|
+
* Prend un Trail + un Graph, retourne des liens navigables.
|
|
6
|
+
*
|
|
7
|
+
* Trois catégories de liens générés automatiquement :
|
|
8
|
+
*
|
|
9
|
+
* self — l'URL courante (Trail sérialisé)
|
|
10
|
+
* up — le parent (Trail sans la dernière frame)
|
|
11
|
+
* relations — toutes les arêtes sortantes du nœud courant
|
|
12
|
+
*
|
|
13
|
+
* Les liens émergent du graphe — le dev ne configure rien.
|
|
14
|
+
*/
|
|
15
|
+
import type { Graph } from '../types/index.js';
|
|
16
|
+
import { Trail } from '../navigation/Trail.js';
|
|
17
|
+
export interface HateoasLink {
|
|
18
|
+
href: string;
|
|
19
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
20
|
+
templated?: boolean;
|
|
21
|
+
title?: string;
|
|
22
|
+
rel?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface HateoasLinks {
|
|
25
|
+
self: HateoasLink;
|
|
26
|
+
up?: HateoasLink;
|
|
27
|
+
[relation: string]: HateoasLink | undefined;
|
|
28
|
+
}
|
|
29
|
+
export interface LinkBuilderOptions {
|
|
30
|
+
/** Préfixe ajouté à toutes les URLs générées — ex: '/api/v1' */
|
|
31
|
+
prefix?: string;
|
|
32
|
+
/** Inclure les arêtes inverses (retour vers le parent) */
|
|
33
|
+
includeReverse?: boolean;
|
|
34
|
+
/** Exclure certaines relations — ex: ['internal', 'debug'] */
|
|
35
|
+
exclude?: string[];
|
|
36
|
+
}
|
|
37
|
+
export declare class LinkBuilder {
|
|
38
|
+
private graph;
|
|
39
|
+
private options;
|
|
40
|
+
constructor(graph: Graph, options?: LinkBuilderOptions);
|
|
41
|
+
/**
|
|
42
|
+
* Génère les liens HATEOAS pour un Trail donné.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const builder = new LinkBuilder(graph, { prefix: '/api' })
|
|
47
|
+
* const links = builder.from(trail)
|
|
48
|
+
* // {
|
|
49
|
+
* // self: { href: '/api/people/Nolan/movies' },
|
|
50
|
+
* // up: { href: '/api/people/Nolan' },
|
|
51
|
+
* // actors: { href: '/api/people/Nolan/movies/{id}/actors', templated: true },
|
|
52
|
+
* // ratings: { href: '/api/people/Nolan/movies/{id}/ratings', templated: true }
|
|
53
|
+
* // }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
from(trail: Trail): HateoasLinks;
|
|
57
|
+
/**
|
|
58
|
+
* Génère les liens pour une collection de résultats.
|
|
59
|
+
* Chaque item reçoit ses propres liens self + relations.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // GET /people/Nolan/movies → liste de films
|
|
64
|
+
* const itemLinks = builder.forItems(trail, movies, 'id')
|
|
65
|
+
* // itemLinks[0] = { self: { href: '/people/Nolan/movies/1' }, actors: {...} }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
forItems(trail: Trail, items: any[], idField?: string): HateoasLinks[];
|
|
69
|
+
/**
|
|
70
|
+
* Vérifie si une relation existe depuis un nœud donné.
|
|
71
|
+
* Utile pour les hooks d'access.check.
|
|
72
|
+
*/
|
|
73
|
+
hasRelation(fromEntity: string, relation: string): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Retourne toutes les entités accessibles depuis un nœud.
|
|
76
|
+
*/
|
|
77
|
+
reachableFrom(entity: string): string[];
|
|
78
|
+
private getOutgoingEdges;
|
|
79
|
+
private prefix;
|
|
80
|
+
private buildTitle;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=LinkBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LinkBuilder.d.ts","sourceRoot":"","sources":["../../src/http/LinkBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAa,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAAE,KAAK,EAAE,MAAY,wBAAwB,CAAA;AAKpD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAM,MAAM,CAAA;IAChB,MAAM,EAAI,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAA;IACrD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,KAAK,CAAC,EAAI,MAAM,CAAA;IAChB,GAAG,CAAC,EAAM,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAG,WAAW,CAAA;IAClB,EAAE,CAAC,EAAI,WAAW,CAAA;IAClB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAA;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0DAA0D;IAC1D,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAID,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAA8B;gBAEjC,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,kBAAuB;IAS1D;;;;;;;;;;;;;;OAcG;IACH,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,YAAY;IAwFhC;;;;;;;;;;OAUG;IACH,QAAQ,CACN,KAAK,EAAI,KAAK,EACd,KAAK,EAAI,GAAG,EAAE,EACd,OAAO,GAAE,MAAa,GACrB,YAAY,EAAE;IAkBjB;;;OAGG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAM1D;;OAEG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAMvC,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,UAAU;CAkBnB"}
|