@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.
Files changed (206) hide show
  1. package/dist/api/DomainNode.d.ts +154 -0
  2. package/dist/api/DomainNode.d.ts.map +1 -0
  3. package/dist/api/DomainNode.js +1157 -0
  4. package/dist/api/DomainNode.js.map +1 -0
  5. package/dist/api/Graph.d.ts +117 -0
  6. package/dist/api/Graph.d.ts.map +1 -0
  7. package/dist/api/Graph.js +212 -0
  8. package/dist/api/Graph.js.map +1 -0
  9. package/dist/api/PathBuilder.d.ts +76 -0
  10. package/dist/api/PathBuilder.d.ts.map +1 -0
  11. package/dist/api/PathBuilder.js +182 -0
  12. package/dist/api/PathBuilder.js.map +1 -0
  13. package/dist/api/index.d.ts +8 -0
  14. package/dist/api/index.d.ts.map +1 -0
  15. package/dist/api/index.js +7 -0
  16. package/dist/api/index.js.map +1 -0
  17. package/dist/api/loadGraph.d.ts +57 -0
  18. package/dist/api/loadGraph.d.ts.map +1 -0
  19. package/dist/api/loadGraph.js +153 -0
  20. package/dist/api/loadGraph.js.map +1 -0
  21. package/dist/api/test-api.d.ts +9 -0
  22. package/dist/api/test-api.d.ts.map +1 -0
  23. package/dist/api/test-api.js +133 -0
  24. package/dist/api/test-api.js.map +1 -0
  25. package/dist/api/test-domain.d.ts +13 -0
  26. package/dist/api/test-domain.d.ts.map +1 -0
  27. package/dist/api/test-domain.js +105 -0
  28. package/dist/api/test-domain.js.map +1 -0
  29. package/dist/api/types.d.ts +69 -0
  30. package/dist/api/types.d.ts.map +1 -0
  31. package/dist/api/types.js +22 -0
  32. package/dist/api/types.js.map +1 -0
  33. package/dist/config/synonyms.json +25 -0
  34. package/dist/core/EventBus.d.ts +56 -0
  35. package/dist/core/EventBus.d.ts.map +1 -0
  36. package/dist/core/EventBus.js +147 -0
  37. package/dist/core/EventBus.js.map +1 -0
  38. package/dist/core/GraphEvents.d.ts +118 -0
  39. package/dist/core/GraphEvents.d.ts.map +1 -0
  40. package/dist/core/GraphEvents.js +23 -0
  41. package/dist/core/GraphEvents.js.map +1 -0
  42. package/dist/core/PathFinder.d.ts +43 -0
  43. package/dist/core/PathFinder.d.ts.map +1 -0
  44. package/dist/core/PathFinder.js +264 -0
  45. package/dist/core/PathFinder.js.map +1 -0
  46. package/dist/formatters/BaseFormatter.d.ts +15 -0
  47. package/dist/formatters/BaseFormatter.d.ts.map +1 -0
  48. package/dist/formatters/BaseFormatter.js +9 -0
  49. package/dist/formatters/BaseFormatter.js.map +1 -0
  50. package/dist/graph/GraphAssembler.d.ts +14 -0
  51. package/dist/graph/GraphAssembler.d.ts.map +1 -0
  52. package/dist/graph/GraphAssembler.js +44 -0
  53. package/dist/graph/GraphAssembler.js.map +1 -0
  54. package/dist/graph/GraphCompiler.d.ts +37 -0
  55. package/dist/graph/GraphCompiler.d.ts.map +1 -0
  56. package/dist/graph/GraphCompiler.js +355 -0
  57. package/dist/graph/GraphCompiler.js.map +1 -0
  58. package/dist/graph/GraphExtractor.d.ts +21 -0
  59. package/dist/graph/GraphExtractor.d.ts.map +1 -0
  60. package/dist/graph/GraphExtractor.js +145 -0
  61. package/dist/graph/GraphExtractor.js.map +1 -0
  62. package/dist/graph/GraphOptimizer.d.ts +104 -0
  63. package/dist/graph/GraphOptimizer.d.ts.map +1 -0
  64. package/dist/graph/GraphOptimizer.js +306 -0
  65. package/dist/graph/GraphOptimizer.js.map +1 -0
  66. package/dist/graph/GraphTrainer.d.ts +52 -0
  67. package/dist/graph/GraphTrainer.d.ts.map +1 -0
  68. package/dist/graph/GraphTrainer.js +188 -0
  69. package/dist/graph/GraphTrainer.js.map +1 -0
  70. package/dist/http/LinkBuilder.d.ts +82 -0
  71. package/dist/http/LinkBuilder.d.ts.map +1 -0
  72. package/dist/http/LinkBuilder.js +190 -0
  73. package/dist/http/LinkBuilder.js.map +1 -0
  74. package/dist/http/TrailRequest.d.ts +39 -0
  75. package/dist/http/TrailRequest.d.ts.map +1 -0
  76. package/dist/http/TrailRequest.js +22 -0
  77. package/dist/http/TrailRequest.js.map +1 -0
  78. package/dist/http/example-netflix.d.ts +6 -0
  79. package/dist/http/example-netflix.d.ts.map +1 -0
  80. package/dist/http/example-netflix.js +52 -0
  81. package/dist/http/example-netflix.js.map +1 -0
  82. package/dist/http/index.d.ts +32 -0
  83. package/dist/http/index.d.ts.map +1 -0
  84. package/dist/http/index.js +27 -0
  85. package/dist/http/index.js.map +1 -0
  86. package/dist/http/plugin.d.ts +110 -0
  87. package/dist/http/plugin.d.ts.map +1 -0
  88. package/dist/http/plugin.js +217 -0
  89. package/dist/http/plugin.js.map +1 -0
  90. package/dist/index.d.ts +55 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +71 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/instrumentation/TelemetryShim.d.ts +114 -0
  95. package/dist/instrumentation/TelemetryShim.d.ts.map +1 -0
  96. package/dist/instrumentation/TelemetryShim.js +107 -0
  97. package/dist/instrumentation/TelemetryShim.js.map +1 -0
  98. package/dist/navigation/NavigationEngine.d.ts +69 -0
  99. package/dist/navigation/NavigationEngine.d.ts.map +1 -0
  100. package/dist/navigation/NavigationEngine.js +361 -0
  101. package/dist/navigation/NavigationEngine.js.map +1 -0
  102. package/dist/navigation/Resolver.d.ts +35 -0
  103. package/dist/navigation/Resolver.d.ts.map +1 -0
  104. package/dist/navigation/Resolver.js +113 -0
  105. package/dist/navigation/Resolver.js.map +1 -0
  106. package/dist/navigation/Scheduler.d.ts +36 -0
  107. package/dist/navigation/Scheduler.d.ts.map +1 -0
  108. package/dist/navigation/Scheduler.js +107 -0
  109. package/dist/navigation/Scheduler.js.map +1 -0
  110. package/dist/navigation/Trail.d.ts +129 -0
  111. package/dist/navigation/Trail.d.ts.map +1 -0
  112. package/dist/navigation/Trail.js +202 -0
  113. package/dist/navigation/Trail.js.map +1 -0
  114. package/dist/navigation/TrailParser.d.ts +96 -0
  115. package/dist/navigation/TrailParser.d.ts.map +1 -0
  116. package/dist/navigation/TrailParser.js +180 -0
  117. package/dist/navigation/TrailParser.js.map +1 -0
  118. package/dist/navigation/index.d.ts +10 -0
  119. package/dist/navigation/index.d.ts.map +1 -0
  120. package/dist/navigation/index.js +9 -0
  121. package/dist/navigation/index.js.map +1 -0
  122. package/dist/providers/MockProvider.d.ts +29 -0
  123. package/dist/providers/MockProvider.d.ts.map +1 -0
  124. package/dist/providers/MockProvider.js +55 -0
  125. package/dist/providers/MockProvider.js.map +1 -0
  126. package/dist/providers/PostgresProvider.d.ts +46 -0
  127. package/dist/providers/PostgresProvider.d.ts.map +1 -0
  128. package/dist/providers/PostgresProvider.js +152 -0
  129. package/dist/providers/PostgresProvider.js.map +1 -0
  130. package/dist/runtime/CompiledGraphEngine.d.ts +74 -0
  131. package/dist/runtime/CompiledGraphEngine.d.ts.map +1 -0
  132. package/dist/runtime/CompiledGraphEngine.js +211 -0
  133. package/dist/runtime/CompiledGraphEngine.js.map +1 -0
  134. package/dist/runtime/DataLoader.d.ts +90 -0
  135. package/dist/runtime/DataLoader.d.ts.map +1 -0
  136. package/dist/runtime/DataLoader.js +178 -0
  137. package/dist/runtime/DataLoader.js.map +1 -0
  138. package/dist/runtime/Engine.d.ts +36 -0
  139. package/dist/runtime/Engine.d.ts.map +1 -0
  140. package/dist/runtime/Engine.js +128 -0
  141. package/dist/runtime/Engine.js.map +1 -0
  142. package/dist/runtime/QueryEngine.d.ts +80 -0
  143. package/dist/runtime/QueryEngine.d.ts.map +1 -0
  144. package/dist/runtime/QueryEngine.js +188 -0
  145. package/dist/runtime/QueryEngine.js.map +1 -0
  146. package/dist/scenarios/test-metro-paris/config.json +6 -0
  147. package/dist/scenarios/test-metro-paris/graph.json +16325 -0
  148. package/dist/scenarios/test-metro-paris/queries.d.ts +22 -0
  149. package/dist/scenarios/test-metro-paris/queries.d.ts.map +1 -0
  150. package/dist/scenarios/test-metro-paris/queries.js +128 -0
  151. package/dist/scenarios/test-metro-paris/queries.js.map +1 -0
  152. package/dist/scenarios/test-metro-paris/stack.json +1 -0
  153. package/dist/scenarios/test-musicians/config.json +10 -0
  154. package/dist/scenarios/test-musicians/graph.json +20 -0
  155. package/dist/scenarios/test-musicians/stack.json +1 -0
  156. package/dist/scenarios/test-netflix/actions.d.ts +14 -0
  157. package/dist/scenarios/test-netflix/actions.d.ts.map +1 -0
  158. package/dist/scenarios/test-netflix/actions.js +86 -0
  159. package/dist/scenarios/test-netflix/actions.js.map +1 -0
  160. package/dist/scenarios/test-netflix/config.json +6 -0
  161. package/dist/scenarios/test-netflix/data/categories.json +1 -0
  162. package/dist/scenarios/test-netflix/data/companies.json +1 -0
  163. package/dist/scenarios/test-netflix/data/credits.json +19797 -0
  164. package/dist/scenarios/test-netflix/data/departments.json +18 -0
  165. package/dist/scenarios/test-netflix/data/jobs.json +142 -0
  166. package/dist/scenarios/test-netflix/data/movies.json +3497 -0
  167. package/dist/scenarios/test-netflix/data/people.json +1 -0
  168. package/dist/scenarios/test-netflix/data/synonyms.json +7 -0
  169. package/dist/scenarios/test-netflix/data/users.json +70 -0
  170. package/dist/scenarios/test-netflix/graph.json +1017 -0
  171. package/dist/scenarios/test-netflix/queries.d.ts +29 -0
  172. package/dist/scenarios/test-netflix/queries.d.ts.map +1 -0
  173. package/dist/scenarios/test-netflix/queries.js +134 -0
  174. package/dist/scenarios/test-netflix/queries.js.map +1 -0
  175. package/dist/scenarios/test-netflix/stack.json +14 -0
  176. package/dist/schema/GraphBuilder.d.ts +9 -0
  177. package/dist/schema/GraphBuilder.d.ts.map +1 -0
  178. package/dist/schema/GraphBuilder.js +90 -0
  179. package/dist/schema/GraphBuilder.js.map +1 -0
  180. package/dist/schema/JsonSchemaExtractor.d.ts +21 -0
  181. package/dist/schema/JsonSchemaExtractor.d.ts.map +1 -0
  182. package/dist/schema/JsonSchemaExtractor.js +88 -0
  183. package/dist/schema/JsonSchemaExtractor.js.map +1 -0
  184. package/dist/schema/SchemaAnalyzer.d.ts +41 -0
  185. package/dist/schema/SchemaAnalyzer.d.ts.map +1 -0
  186. package/dist/schema/SchemaAnalyzer.js +144 -0
  187. package/dist/schema/SchemaAnalyzer.js.map +1 -0
  188. package/dist/schema/SchemaExtractor.d.ts +10 -0
  189. package/dist/schema/SchemaExtractor.d.ts.map +1 -0
  190. package/dist/schema/SchemaExtractor.js +90 -0
  191. package/dist/schema/SchemaExtractor.js.map +1 -0
  192. package/dist/schema/SynonymResolver.d.ts +55 -0
  193. package/dist/schema/SynonymResolver.d.ts.map +1 -0
  194. package/dist/schema/SynonymResolver.js +121 -0
  195. package/dist/schema/SynonymResolver.js.map +1 -0
  196. package/dist/scripts/dictionary.json +796 -0
  197. package/dist/scripts/graph.json +664 -0
  198. package/dist/scripts/regenerate.d.ts +23 -0
  199. package/dist/scripts/regenerate.d.ts.map +1 -0
  200. package/dist/scripts/regenerate.js +206 -0
  201. package/dist/scripts/regenerate.js.map +1 -0
  202. package/dist/types/index.d.ts +394 -0
  203. package/dist/types/index.d.ts.map +1 -0
  204. package/dist/types/index.js +21 -0
  205. package/dist/types/index.js.map +1 -0
  206. package/package.json +1 -1
@@ -0,0 +1,190 @@
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 { TrailParser } from '../navigation/TrailParser.js';
16
+ // ── LinkBuilder ───────────────────────────────────────────────
17
+ export class LinkBuilder {
18
+ graph;
19
+ options;
20
+ constructor(graph, options = {}) {
21
+ this.graph = graph;
22
+ this.options = {
23
+ prefix: options.prefix ?? '',
24
+ includeReverse: options.includeReverse ?? false,
25
+ exclude: options.exclude ?? [],
26
+ };
27
+ }
28
+ /**
29
+ * Génère les liens HATEOAS pour un Trail donné.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const builder = new LinkBuilder(graph, { prefix: '/api' })
34
+ * const links = builder.from(trail)
35
+ * // {
36
+ * // self: { href: '/api/people/Nolan/movies' },
37
+ * // up: { href: '/api/people/Nolan' },
38
+ * // actors: { href: '/api/people/Nolan/movies/{id}/actors', templated: true },
39
+ * // ratings: { href: '/api/people/Nolan/movies/{id}/ratings', templated: true }
40
+ * // }
41
+ * ```
42
+ */
43
+ from(trail) {
44
+ const currentPath = this.prefix(TrailParser.toPath(trail));
45
+ const links = {
46
+ self: {
47
+ href: currentPath,
48
+ method: 'GET',
49
+ rel: 'self',
50
+ }
51
+ };
52
+ // ── Frame courante ────────────────────────────────────────
53
+ const current = trail.current;
54
+ // ── Lien "up" — parent dans le Trail ou collection ─────────
55
+ // depth = 1 avec id : up vers la collection (ex: /movies/278 → up: /movies)
56
+ // depth > 1 : up vers l'entité parente (ex: /movies/278/people → up: /movies/278)
57
+ if (trail.depth === 1 && current?.id !== undefined) {
58
+ links.up = {
59
+ href: this.prefix('/' + current.entity),
60
+ method: 'GET',
61
+ rel: 'up',
62
+ title: `Collection ${current.entity}`,
63
+ };
64
+ }
65
+ else if (trail.depth > 1) {
66
+ const parentTrail = trail.slice(trail.depth - 1);
67
+ links.up = {
68
+ href: this.prefix(TrailParser.toPath(parentTrail)),
69
+ method: 'GET',
70
+ rel: 'up',
71
+ title: `Retour vers ${parentTrail.current?.entity}`,
72
+ };
73
+ }
74
+ // ── Liens sortants — arêtes depuis le nœud courant ────────
75
+ if (!current)
76
+ return links;
77
+ const outgoing = this.getOutgoingEdges(current.entity);
78
+ // Grouper les arêtes par entité cible (.to)
79
+ // Plusieurs arêtes peuvent pointer vers la même entité (ex: actor, director, writer → people)
80
+ // On génère un seul lien par entité cible, avec l'arête de poids minimal
81
+ const byTarget = new Map();
82
+ for (const edge of outgoing) {
83
+ // Ignorer les relations exclues
84
+ if (this.options.exclude.includes(edge.name ?? ''))
85
+ continue;
86
+ if (this.options.exclude.includes(edge.to))
87
+ continue;
88
+ const edgeLabel = edge.label ?? edge.name ?? '';
89
+ // Préférer une edge avec un label significatif (pas 'unknow', pas vide)
90
+ const isSignificant = edgeLabel && edgeLabel !== 'unknow';
91
+ const existing = byTarget.get(edge.to);
92
+ if (!existing) {
93
+ byTarget.set(edge.to, edge);
94
+ }
95
+ else {
96
+ const existingLabel = existing.label ?? existing.name ?? '';
97
+ const existingSignificant = existingLabel && existingLabel !== 'unknow';
98
+ // Remplacer si : l'actuelle est insignifiante ET la nouvelle est significative,
99
+ // ou les deux sont significatives et la nouvelle est moins lourde
100
+ if ((!existingSignificant && isSignificant) ||
101
+ (existingSignificant && isSignificant && edge.weight < existing.weight)) {
102
+ byTarget.set(edge.to, edge);
103
+ }
104
+ }
105
+ }
106
+ for (const [targetEntity, edge] of byTarget) {
107
+ // L'URL utilise l'entité cible comme segment — pas le nom de l'arête
108
+ // /api/movies/278/people (pas /api/movies/278/actor)
109
+ const href = current.id !== undefined
110
+ ? this.prefix(TrailParser.toPath(trail) + '/' + targetEntity)
111
+ : this.prefix(TrailParser.toPath(trail) + '/{id}/' + targetEntity);
112
+ const templated = current.id === undefined || href.includes('{id}');
113
+ links[targetEntity] = {
114
+ href,
115
+ method: 'GET',
116
+ templated: templated || undefined,
117
+ rel: targetEntity,
118
+ title: this.buildTitle(trail, edge),
119
+ };
120
+ }
121
+ return links;
122
+ }
123
+ /**
124
+ * Génère les liens pour une collection de résultats.
125
+ * Chaque item reçoit ses propres liens self + relations.
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * // GET /people/Nolan/movies → liste de films
130
+ * const itemLinks = builder.forItems(trail, movies, 'id')
131
+ * // itemLinks[0] = { self: { href: '/people/Nolan/movies/1' }, actors: {...} }
132
+ * ```
133
+ */
134
+ forItems(trail, items, idField = 'id') {
135
+ return items.map(item => {
136
+ const id = item[idField];
137
+ if (id === undefined)
138
+ return this.from(trail);
139
+ // Construire un Trail avec l'id de l'item
140
+ const itemTrail = trail.clone();
141
+ const last = itemTrail.current;
142
+ if (last) {
143
+ // Remplacer la dernière frame avec l'id
144
+ itemTrail.pop();
145
+ itemTrail.push({ ...last, id, state: 'RESOLVED' });
146
+ }
147
+ return this.from(itemTrail);
148
+ });
149
+ }
150
+ /**
151
+ * Vérifie si une relation existe depuis un nœud donné.
152
+ * Utile pour les hooks d'access.check.
153
+ */
154
+ hasRelation(fromEntity, relation) {
155
+ return this.graph.edges.some(e => e.from === fromEntity && (e.name === relation || e.to === relation));
156
+ }
157
+ /**
158
+ * Retourne toutes les entités accessibles depuis un nœud.
159
+ */
160
+ reachableFrom(entity) {
161
+ return this.getOutgoingEdges(entity).map(e => e.name ?? e.to);
162
+ }
163
+ // ── Privé ──────────────────────────────────────────────────
164
+ getOutgoingEdges(entity) {
165
+ return this.graph.edges
166
+ .filter(e => e.from === entity)
167
+ .sort((a, b) => b.weight - a.weight); // les plus utilisées en premier
168
+ }
169
+ prefix(path) {
170
+ if (!this.options.prefix)
171
+ return path;
172
+ return this.options.prefix.replace(/\/$/, '') + path;
173
+ }
174
+ buildTitle(trail, edge) {
175
+ const current = trail.current;
176
+ // Résoudre un label lisible pour l'edge :
177
+ // 1. edge.name explicite et non générique (ex: 'LIST_OF_CREDITS', 'actor')
178
+ // 2. edge.to comme fallback neutre (ex: 'people', 'movies')
179
+ const rawName = edge.name ?? '';
180
+ const isGeneric = !rawName || rawName === 'unknow' || rawName === 'unknow_in';
181
+ const edgeLabel = isGeneric ? edge.to : rawName;
182
+ if (!current)
183
+ return edgeLabel;
184
+ const from = current.id !== undefined
185
+ ? `${current.entity}(${current.id})`
186
+ : current.entity;
187
+ return `${edgeLabel} de ${from}`;
188
+ }
189
+ }
190
+ //# sourceMappingURL=LinkBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LinkBuilder.js","sourceRoot":"","sources":["../../src/http/LinkBuilder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * TrailRequest — Augmentation du type Request Fastify
3
+ *
4
+ * Ajoute `request.trail` et `request.linkBuilder`
5
+ * sur chaque requête décorée par le plugin LinkLab.
6
+ *
7
+ * Usage :
8
+ * ```typescript
9
+ * fastify.get('/*', async (req, reply) => {
10
+ * const trail = req.trail // Trail parsé depuis l'URL
11
+ * const links = req.linkBuilder // LinkBuilder prêt à l'emploi
12
+ * })
13
+ * ```
14
+ */
15
+ import type { FastifyRequest } from 'fastify';
16
+ import type { Trail } from '../navigation/Trail.js';
17
+ import type { LinkBuilder } from './LinkBuilder.js';
18
+ /**
19
+ * Déclaration d'augmentation du module Fastify.
20
+ * TypeScript merge automatiquement avec FastifyRequest.
21
+ */
22
+ declare module 'fastify' {
23
+ interface FastifyRequest {
24
+ /** Trail parsé depuis l'URL de la requête */
25
+ trail: Trail;
26
+ /** LinkBuilder configuré avec le graphe de l'instance */
27
+ linkBuilder: LinkBuilder;
28
+ }
29
+ }
30
+ /**
31
+ * Extrait le contexte utilisateur depuis une requête Fastify.
32
+ * Extensible par le dev via les options du plugin.
33
+ */
34
+ export type UserContextExtractor = (req: FastifyRequest) => Promise<Record<string, any>> | Record<string, any>;
35
+ /**
36
+ * Extracteur par défaut — lit req.user si présent (JWT/session)
37
+ */
38
+ export declare const defaultUserExtractor: UserContextExtractor;
39
+ //# sourceMappingURL=TrailRequest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TrailRequest.d.ts","sourceRoot":"","sources":["../../src/http/TrailRequest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAe,wBAAwB,CAAA;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAS,kBAAkB,CAAA;AAEtD;;;GAGG;AACH,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,6CAA6C;QAC7C,KAAK,EAAE,KAAK,CAAA;QAEZ,yDAAyD;QACzD,WAAW,EAAE,WAAW,CAAA;KACzB;CACF;AAED;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,GAAG,EAAE,cAAc,KAChB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAEvD;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,oBAGlC,CAAA"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * TrailRequest — Augmentation du type Request Fastify
3
+ *
4
+ * Ajoute `request.trail` et `request.linkBuilder`
5
+ * sur chaque requête décorée par le plugin LinkLab.
6
+ *
7
+ * Usage :
8
+ * ```typescript
9
+ * fastify.get('/*', async (req, reply) => {
10
+ * const trail = req.trail // Trail parsé depuis l'URL
11
+ * const links = req.linkBuilder // LinkBuilder prêt à l'emploi
12
+ * })
13
+ * ```
14
+ */
15
+ /**
16
+ * Extracteur par défaut — lit req.user si présent (JWT/session)
17
+ */
18
+ export const defaultUserExtractor = (req) => {
19
+ const r = req;
20
+ return r.user ?? r.session?.user ?? {};
21
+ };
22
+ //# sourceMappingURL=TrailRequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TrailRequest.js","sourceRoot":"","sources":["../../src/http/TrailRequest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA4BH;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAyB,CAAC,GAAG,EAAE,EAAE;IAChE,MAAM,CAAC,GAAG,GAAU,CAAA;IACpB,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAA;AACxC,CAAC,CAAA"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Exemple d'intégration Netflix
3
+ * Fichier à titre d'exemple uniquement, non exporté.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=example-netflix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example-netflix.d.ts","sourceRoot":"","sources":["../../src/http/example-netflix.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Exemple d'intégration Netflix
3
+ * Fichier à titre d'exemple uniquement, non exporté.
4
+ */
5
+ import { createRequire } from 'module';
6
+ import { fileURLToPath } from 'url';
7
+ import path from 'path';
8
+ import Fastify from 'fastify';
9
+ import { linklabPlugin } from './index.js';
10
+ const require = createRequire(import.meta.url);
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const dataDir = path.join(__dirname, '../examples/netflix/data');
13
+ const compiledGraph = require('../examples/netflix/compiled-graph.json');
14
+ const movies = require(path.join(dataDir, 'movies.json'));
15
+ const people = require(path.join(dataDir, 'people.json'));
16
+ const credits = require(path.join(dataDir, 'credits.json'));
17
+ const categories = require(path.join(dataDir, 'categories.json'));
18
+ const departments = require(path.join(dataDir, 'departments.json'));
19
+ const jobs = require(path.join(dataDir, 'jobs.json'));
20
+ const fastify = Fastify({ logger: true });
21
+ await fastify.register(linklabPlugin, {
22
+ graph: compiledGraph,
23
+ prefix: '/api',
24
+ global: { domain: 'netflix', version: 'v1' },
25
+ dataLoader: {
26
+ dataset: { movies, people, credits, categories, departments, jobs }
27
+ },
28
+ extractUser: async (req) => {
29
+ const auth = req.headers.authorization;
30
+ if (!auth)
31
+ return {};
32
+ return { userId: 'u_123', subscription: 'premium', locale: 'fr-FR' };
33
+ },
34
+ onEngine: (engine, req) => {
35
+ const accessHandler = async (ctx) => {
36
+ const protected_ = ['movies', 'series', 'episodes'];
37
+ if (protected_.includes(ctx.node) && !ctx.trail?.user?.subscription) {
38
+ return { cancelled: true, reason: 'subscription_required' };
39
+ }
40
+ return undefined;
41
+ };
42
+ engine.hooks.on('access.check', accessHandler);
43
+ engine.events.on('traversal.complete', ({ routeUsed, durationMs }) => {
44
+ fastify.log.info({ routeUsed, durationMs }, 'traversal');
45
+ });
46
+ engine.errors.on('route.notfound', ({ from, to }) => {
47
+ fastify.log.warn({ from, to }, 'route not found');
48
+ });
49
+ },
50
+ });
51
+ await fastify.listen({ port: 3000 });
52
+ //# sourceMappingURL=example-netflix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example-netflix.js","sourceRoot":"","sources":["../../src/http/example-netflix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * http — Exports du module HTTP LinkLab
3
+ *
4
+ * Point d'entrée unique pour le plugin Fastify et
5
+ * les utilitaires HTTP associés.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { linklabPlugin, LinkBuilder, TrailParser } from '@linklab/http'
10
+ *
11
+ * await fastify.register(linklabPlugin, {
12
+ * graph: compiledGraph,
13
+ * prefix: '/api',
14
+ * onEngine: (engine, req) => {
15
+ * engine.hooks.on('access.check', async (ctx) => {
16
+ * if (!ctx.trail.user.userId) {
17
+ * return { cancelled: true, reason: 'unauthenticated' }
18
+ * }
19
+ * })
20
+ * }
21
+ * })
22
+ * ```
23
+ */
24
+ export { linklabPlugin } from './plugin.js';
25
+ export { LinkBuilder } from './LinkBuilder.js';
26
+ export { DataLoader } from '../runtime/DataLoader.js';
27
+ export type { LinklabPluginOptions } from './plugin.js';
28
+ export type { TrailResponse, ResponseMeta } from './plugin.js';
29
+ export type { HateoasLink, HateoasLinks, LinkBuilderOptions } from './LinkBuilder.js';
30
+ export type { UserContextExtractor } from './TrailRequest.js';
31
+ export type { DataLoaderOptions } from '../runtime/DataLoader.js';
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAgB,aAAa,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAkB,kBAAkB,CAAA;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAmB,0BAA0B,CAAA;AAElE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,YAAY,EAAE,iBAAiB,EAAE,MAAO,0BAA0B,CAAA"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * http — Exports du module HTTP LinkLab
3
+ *
4
+ * Point d'entrée unique pour le plugin Fastify et
5
+ * les utilitaires HTTP associés.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { linklabPlugin, LinkBuilder, TrailParser } from '@linklab/http'
10
+ *
11
+ * await fastify.register(linklabPlugin, {
12
+ * graph: compiledGraph,
13
+ * prefix: '/api',
14
+ * onEngine: (engine, req) => {
15
+ * engine.hooks.on('access.check', async (ctx) => {
16
+ * if (!ctx.trail.user.userId) {
17
+ * return { cancelled: true, reason: 'unauthenticated' }
18
+ * }
19
+ * })
20
+ * }
21
+ * })
22
+ * ```
23
+ */
24
+ export { linklabPlugin } from './plugin.js';
25
+ export { LinkBuilder } from './LinkBuilder.js';
26
+ export { DataLoader } from '../runtime/DataLoader.js';
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAgB,aAAa,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAkB,kBAAkB,CAAA;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAmB,0BAA0B,CAAA"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * LinkLab Fastify Plugin
3
+ *
4
+ * Transforme automatiquement chaque requête HTTP en Trail,
5
+ * résout la navigation via le graphe, et retourne une réponse
6
+ * HATEOAS Level 3 — liens générés automatiquement depuis le graphe.
7
+ *
8
+ * Usage minimal :
9
+ * ```typescript
10
+ * import Fastify from 'fastify'
11
+ * import { linklabPlugin } from '@linklab/http'
12
+ *
13
+ * const fastify = Fastify()
14
+ * await fastify.register(linklabPlugin, {
15
+ * graph: compiledGraph,
16
+ * prefix: '/api'
17
+ * })
18
+ * await fastify.listen({ port: 3000 })
19
+ * ```
20
+ *
21
+ * Toutes ces routes fonctionnent sans configuration supplémentaire :
22
+ * GET /api/people
23
+ * GET /api/people/Nolan
24
+ * GET /api/people/Nolan/movies
25
+ * GET /api/people/Nolan/movies/1/actors
26
+ *
27
+ * Hooks disponibles sur chaque requête :
28
+ * ```typescript
29
+ * fastify.register(linklabPlugin, {
30
+ * graph,
31
+ * onEngine: (engine, req) => {
32
+ * engine.hooks.on('access.check', async (ctx) => {
33
+ * if (!ctx.trail.user.userId) {
34
+ * return { cancelled: true, reason: 'unauthenticated' }
35
+ * }
36
+ * })
37
+ * }
38
+ * })
39
+ * ```
40
+ */
41
+ import type { FastifyPluginAsync, FastifyRequest } from 'fastify';
42
+ import type { Graph, CompiledGraph } from '../types/index.js';
43
+ import { Trail } from '../navigation/Trail.js';
44
+ import { NavigationEngine } from '../navigation/NavigationEngine.js';
45
+ import { type DataLoaderOptions } from '../runtime/DataLoader.js';
46
+ import { type UserContextExtractor } from './TrailRequest.js';
47
+ export interface LinklabPluginOptions {
48
+ /** Le graphe sémantique — navigation, LinkBuilder, Resolver */
49
+ graph: Graph;
50
+ /** Le graphe compilé — routes SQL optimales pour DataLoader/QueryEngine */
51
+ compiledGraph?: CompiledGraph;
52
+ /** Préfixe URL — ex: '/api' ou '/api/v1' */
53
+ prefix?: string;
54
+ /** Contexte global injecté dans chaque Trail */
55
+ global?: Record<string, any>;
56
+ /**
57
+ * Extracteur de contexte utilisateur.
58
+ * Par défaut : lit req.user ou req.session.user
59
+ */
60
+ extractUser?: UserContextExtractor;
61
+ /**
62
+ * Hook appelé après création du moteur, avant résolution.
63
+ * C'est ici que Netflix branche sa logique métier.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * onEngine: (engine, req) => {
68
+ * engine.hooks.on('access.check', async (ctx) => {
69
+ * if (!ctx.trail.user.subscription) {
70
+ * return { cancelled: true, reason: 'subscription_required' }
71
+ * }
72
+ * })
73
+ * }
74
+ * ```
75
+ */
76
+ onEngine?: (engine: NavigationEngine, req: FastifyRequest) => void | Promise<void>;
77
+ /**
78
+ * Options du DataLoader — source de données réelles.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // Mode JSON (Netflix mock / tests)
83
+ * dataLoader: { dataset: { movies, people, credits } }
84
+ *
85
+ * // Mode SQL (PostgreSQL)
86
+ * dataLoader: { provider: postgresProvider }
87
+ * ```
88
+ */
89
+ dataLoader?: DataLoaderOptions;
90
+ /**
91
+ * Transforme les données avant envoi.
92
+ * Utile pour la pagination, la sérialisation custom, etc.
93
+ */
94
+ transformData?: (data: any, trail: Trail) => any;
95
+ }
96
+ export interface TrailResponse {
97
+ data: any;
98
+ _links: Record<string, any>;
99
+ _trail: string;
100
+ _meta: ResponseMeta;
101
+ }
102
+ export interface ResponseMeta {
103
+ entity: string;
104
+ depth: number;
105
+ resolved: number;
106
+ timing: number;
107
+ count?: number;
108
+ }
109
+ export declare const linklabPlugin: FastifyPluginAsync<LinklabPluginOptions>;
110
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/http/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EAEf,MAAM,SAAS,CAAA;AAGhB,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAuB,wBAAwB,CAAA;AAE/D,OAAO,EAAE,gBAAgB,EAAE,MAAY,mCAAmC,CAAA;AAE1E,OAAO,EAAc,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC7E,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,mBAAmB,CAAA;AAI1B,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,KAAK,EAAE,KAAK,CAAA;IAEZ,2EAA2E;IAC3E,aAAa,CAAC,EAAE,aAAa,CAAA;IAE7B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAE5B;;;OAGG;IACH,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAElC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAElF;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAE9B;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,KAAK,GAAG,CAAA;CACjD;AAID,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAK,GAAG,CAAA;IACZ,MAAM,EAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,MAAM,EAAG,MAAM,CAAA;IACf,KAAK,EAAI,YAAY,CAAA;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAI,MAAM,CAAA;IAChB,KAAK,EAAK,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAI,MAAM,CAAA;IAChB,KAAK,CAAC,EAAI,MAAM,CAAA;CACjB;AA+ND,eAAO,MAAM,aAAa,0CAGxB,CAAA"}