@living-architecture/riviere-query 0.2.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.
Files changed (50) hide show
  1. package/README.md +11 -0
  2. package/dist/RiviereQuery.d.ts +486 -0
  3. package/dist/RiviereQuery.d.ts.map +1 -0
  4. package/dist/RiviereQuery.js +553 -0
  5. package/dist/component-queries.d.ts +8 -0
  6. package/dist/component-queries.d.ts.map +1 -0
  7. package/dist/component-queries.js +24 -0
  8. package/dist/cross-domain-queries.d.ts +5 -0
  9. package/dist/cross-domain-queries.d.ts.map +1 -0
  10. package/dist/cross-domain-queries.js +92 -0
  11. package/dist/depth-queries.d.ts +4 -0
  12. package/dist/depth-queries.d.ts.map +1 -0
  13. package/dist/depth-queries.js +54 -0
  14. package/dist/domain-queries.d.ts +10 -0
  15. package/dist/domain-queries.d.ts.map +1 -0
  16. package/dist/domain-queries.js +101 -0
  17. package/dist/domain-types.d.ts +307 -0
  18. package/dist/domain-types.d.ts.map +1 -0
  19. package/dist/domain-types.js +111 -0
  20. package/dist/errors.d.ts +6 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +10 -0
  23. package/dist/event-queries.d.ts +5 -0
  24. package/dist/event-queries.d.ts.map +1 -0
  25. package/dist/event-queries.js +32 -0
  26. package/dist/event-types.d.ts +88 -0
  27. package/dist/event-types.d.ts.map +1 -0
  28. package/dist/event-types.js +1 -0
  29. package/dist/external-system-queries.d.ts +13 -0
  30. package/dist/external-system-queries.d.ts.map +1 -0
  31. package/dist/external-system-queries.js +54 -0
  32. package/dist/flow-queries.d.ts +17 -0
  33. package/dist/flow-queries.d.ts.map +1 -0
  34. package/dist/flow-queries.js +127 -0
  35. package/dist/graph-diff.d.ts +4 -0
  36. package/dist/graph-diff.d.ts.map +1 -0
  37. package/dist/graph-diff.js +53 -0
  38. package/dist/graph-validation.d.ts +5 -0
  39. package/dist/graph-validation.d.ts.map +1 -0
  40. package/dist/graph-validation.js +72 -0
  41. package/dist/index.d.ts +5 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +4 -0
  44. package/dist/riviere-graph-fixtures.d.ts +37 -0
  45. package/dist/riviere-graph-fixtures.d.ts.map +1 -0
  46. package/dist/riviere-graph-fixtures.js +73 -0
  47. package/dist/stats-queries.d.ts +4 -0
  48. package/dist/stats-queries.d.ts.map +1 -0
  49. package/dist/stats-queries.js +14 -0
  50. package/package.json +29 -0
@@ -0,0 +1,553 @@
1
+ import { parseRiviereGraph } from '@living-architecture/riviere-schema';
2
+ import { findComponent, findAllComponents, componentById as lookupComponentById, searchComponents, componentsInDomain as filterByDomain, componentsByType as filterByType } from './component-queries';
3
+ import { queryDomains, operationsForEntity, queryEntities, businessRulesForEntity, transitionsForEntity, statesForEntity } from './domain-queries';
4
+ import { queryExternalDomains } from './external-system-queries';
5
+ import { findEntryPoints, traceFlowFrom, queryFlows, searchWithFlowContext } from './flow-queries';
6
+ import { queryCrossDomainLinks, queryDomainConnections } from './cross-domain-queries';
7
+ import { queryPublishedEvents, queryEventHandlers } from './event-queries';
8
+ import { validateGraph, detectOrphanComponents } from './graph-validation';
9
+ import { diffGraphs } from './graph-diff';
10
+ import { queryStats } from './stats-queries';
11
+ import { queryNodeDepths } from './depth-queries';
12
+ export { parseComponentId } from './domain-types';
13
+ export { ComponentNotFoundError } from './errors';
14
+ function assertValidGraph(graph) {
15
+ parseRiviereGraph(graph);
16
+ }
17
+ /**
18
+ * Query and analyze Riviere architecture graphs.
19
+ *
20
+ * RiviereQuery provides methods to explore components, trace execution flows,
21
+ * analyze domain models, and compare graph versions.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { RiviereQuery } from '@living-architecture/riviere-query'
26
+ *
27
+ * // From JSON
28
+ * const query = RiviereQuery.fromJSON(graphData)
29
+ *
30
+ * // Query components
31
+ * const apis = query.componentsByType('API')
32
+ * const orderDomain = query.componentsInDomain('orders')
33
+ *
34
+ * // Trace flows
35
+ * const flow = query.traceFlow('orders:checkout:api:post-orders')
36
+ * ```
37
+ */
38
+ export class RiviereQuery {
39
+ graph;
40
+ /**
41
+ * Creates a new RiviereQuery instance.
42
+ *
43
+ * @param graph - A valid RiviereGraph object
44
+ * @throws If the graph fails schema validation
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const graph: RiviereGraph = JSON.parse(jsonString)
49
+ * const query = new RiviereQuery(graph)
50
+ * ```
51
+ */
52
+ constructor(graph) {
53
+ assertValidGraph(graph);
54
+ this.graph = graph;
55
+ }
56
+ /**
57
+ * Creates a RiviereQuery from raw JSON data.
58
+ *
59
+ * @param json - Raw JSON data to parse as a RiviereGraph
60
+ * @returns A new RiviereQuery instance
61
+ * @throws If the JSON fails schema validation
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const jsonData = await fetch('/graph.json').then(r => r.json())
66
+ * const query = RiviereQuery.fromJSON(jsonData)
67
+ * ```
68
+ */
69
+ static fromJSON(json) {
70
+ assertValidGraph(json);
71
+ return new RiviereQuery(json);
72
+ }
73
+ /**
74
+ * Returns all components in the graph.
75
+ *
76
+ * @returns Array of all components
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const allComponents = query.components()
81
+ * console.log(`Total: ${allComponents.length}`)
82
+ * ```
83
+ */
84
+ components() {
85
+ return this.graph.components;
86
+ }
87
+ /**
88
+ * Returns all links in the graph.
89
+ *
90
+ * @returns Array of all links
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * const allLinks = query.links()
95
+ * console.log(`Total links: ${allLinks.length}`)
96
+ * ```
97
+ */
98
+ links() {
99
+ return this.graph.links;
100
+ }
101
+ /**
102
+ * Validates the graph structure beyond schema validation.
103
+ *
104
+ * Checks for structural issues like invalid link references.
105
+ *
106
+ * @returns Validation result with any errors found
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const result = query.validate()
111
+ * if (!result.valid) {
112
+ * console.error('Validation errors:', result.errors)
113
+ * }
114
+ * ```
115
+ */
116
+ validate() {
117
+ return validateGraph(this.graph);
118
+ }
119
+ /**
120
+ * Detects orphan components with no incoming or outgoing links.
121
+ *
122
+ * @returns Array of component IDs that are disconnected from the graph
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const orphanIds = query.detectOrphans()
127
+ * if (orphanIds.length > 0) {
128
+ * console.warn(`Found ${orphanIds.length} orphan nodes`)
129
+ * }
130
+ * ```
131
+ */
132
+ detectOrphans() {
133
+ return detectOrphanComponents(this.graph);
134
+ }
135
+ /**
136
+ * Finds the first component matching a predicate.
137
+ *
138
+ * @param predicate - Function that returns true for matching components
139
+ * @returns The first matching component, or undefined if none found
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const checkout = query.find(c => c.name.includes('checkout'))
144
+ * ```
145
+ */
146
+ find(predicate) {
147
+ return findComponent(this.graph, predicate);
148
+ }
149
+ /**
150
+ * Finds all components matching a predicate.
151
+ *
152
+ * @param predicate - Function that returns true for matching components
153
+ * @returns Array of all matching components
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * const orderHandlers = query.findAll(c =>
158
+ * c.type === 'EventHandler' && c.domain === 'orders'
159
+ * )
160
+ * ```
161
+ */
162
+ findAll(predicate) {
163
+ return findAllComponents(this.graph, predicate);
164
+ }
165
+ /**
166
+ * Finds a component by its ID.
167
+ *
168
+ * @param id - The component ID to look up
169
+ * @returns The component, or undefined if not found
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * const component = query.componentById('orders:checkout:api:post-orders')
174
+ * ```
175
+ */
176
+ componentById(id) {
177
+ return lookupComponentById(this.graph, id);
178
+ }
179
+ /**
180
+ * Searches components by name, domain, or type.
181
+ *
182
+ * Case-insensitive search across component name, domain, and type fields.
183
+ *
184
+ * @param query - Search term
185
+ * @returns Array of matching components
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const results = query.search('order')
190
+ * // Matches: "PlaceOrder", "orders" domain, etc.
191
+ * ```
192
+ */
193
+ search(query) {
194
+ return searchComponents(this.graph, query);
195
+ }
196
+ /**
197
+ * Returns all components in a specific domain.
198
+ *
199
+ * @param domainName - The domain name to filter by
200
+ * @returns Array of components in the domain
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * const orderComponents = query.componentsInDomain('orders')
205
+ * ```
206
+ */
207
+ componentsInDomain(domainName) {
208
+ return filterByDomain(this.graph, domainName);
209
+ }
210
+ /**
211
+ * Returns all components of a specific type.
212
+ *
213
+ * @param type - The component type to filter by
214
+ * @returns Array of components of that type
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * const apis = query.componentsByType('API')
219
+ * const events = query.componentsByType('Event')
220
+ * ```
221
+ */
222
+ componentsByType(type) {
223
+ return filterByType(this.graph, type);
224
+ }
225
+ /**
226
+ * Returns domain information with component counts.
227
+ *
228
+ * @returns Array of Domain objects sorted by name
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * const domains = query.domains()
233
+ * for (const domain of domains) {
234
+ * console.log(`${domain.name}: ${domain.componentCounts.total} components`)
235
+ * }
236
+ * ```
237
+ */
238
+ domains() {
239
+ return queryDomains(this.graph);
240
+ }
241
+ /**
242
+ * Returns all domain operations for a specific entity.
243
+ *
244
+ * @param entityName - The entity name to get operations for
245
+ * @returns Array of DomainOp components targeting the entity
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * const orderOps = query.operationsFor('Order')
250
+ * ```
251
+ */
252
+ operationsFor(entityName) {
253
+ return operationsForEntity(this.graph, entityName);
254
+ }
255
+ /**
256
+ * Returns entities with their domain operations.
257
+ *
258
+ * @param domainName - Optional domain to filter by
259
+ * @returns Array of Entity objects with their operations
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * const allEntities = query.entities()
264
+ * const orderEntities = query.entities('orders')
265
+ *
266
+ * for (const entity of orderEntities) {
267
+ * console.log(`${entity.name} has ${entity.operations.length} operations`)
268
+ * }
269
+ * ```
270
+ */
271
+ entities(domainName) {
272
+ return queryEntities(this.graph, domainName);
273
+ }
274
+ /**
275
+ * Returns all business rules for an entity's operations.
276
+ *
277
+ * @param entityName - The entity name to get rules for
278
+ * @returns Array of business rule strings
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const rules = query.businessRulesFor('Order')
283
+ * ```
284
+ */
285
+ businessRulesFor(entityName) {
286
+ return businessRulesForEntity(this.graph, entityName);
287
+ }
288
+ /**
289
+ * Returns state transitions for an entity.
290
+ *
291
+ * @param entityName - The entity name to get transitions for
292
+ * @returns Array of EntityTransition objects
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * const transitions = query.transitionsFor('Order')
297
+ * ```
298
+ */
299
+ transitionsFor(entityName) {
300
+ return transitionsForEntity(this.graph, entityName);
301
+ }
302
+ /**
303
+ * Returns ordered states for an entity based on transitions.
304
+ *
305
+ * States are ordered by transition flow from initial to final states.
306
+ *
307
+ * @param entityName - The entity name to get states for
308
+ * @returns Array of state names in transition order
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * const orderStates = query.statesFor('Order')
313
+ * // ['pending', 'confirmed', 'shipped', 'delivered']
314
+ * ```
315
+ */
316
+ statesFor(entityName) {
317
+ return statesForEntity(this.graph, entityName);
318
+ }
319
+ /**
320
+ * Returns components that are entry points to the system.
321
+ *
322
+ * Entry points are UI, API, EventHandler, or Custom components
323
+ * with no incoming links.
324
+ *
325
+ * @returns Array of entry point components
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * const entryPoints = query.entryPoints()
330
+ * ```
331
+ */
332
+ entryPoints() {
333
+ return findEntryPoints(this.graph);
334
+ }
335
+ /**
336
+ * Traces the complete flow bidirectionally from a starting component.
337
+ *
338
+ * Returns all nodes and links connected to the starting point,
339
+ * following links in both directions.
340
+ *
341
+ * @param startComponentId - ID of the component to start tracing from
342
+ * @returns Object with componentIds and linkIds in the flow
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * const flow = query.traceFlow('orders:checkout:api:post-orders')
347
+ * console.log(`Flow includes ${flow.componentIds.length} nodes`)
348
+ * ```
349
+ */
350
+ traceFlow(startComponentId) {
351
+ return traceFlowFrom(this.graph, startComponentId);
352
+ }
353
+ /**
354
+ * Compares this graph with another and returns the differences.
355
+ *
356
+ * @param other - The graph to compare against
357
+ * @returns GraphDiff with added, removed, and modified items
358
+ *
359
+ * @example
360
+ * ```typescript
361
+ * const oldGraph = RiviereQuery.fromJSON(oldData)
362
+ * const newGraph = RiviereQuery.fromJSON(newData)
363
+ * const diff = newGraph.diff(oldGraph.graph)
364
+ *
365
+ * console.log(`Added: ${diff.stats.componentsAdded}`)
366
+ * console.log(`Removed: ${diff.stats.componentsRemoved}`)
367
+ * ```
368
+ */
369
+ diff(other) {
370
+ return diffGraphs(this.graph, other);
371
+ }
372
+ /**
373
+ * Returns published events with their handlers.
374
+ *
375
+ * @param domainName - Optional domain to filter by
376
+ * @returns Array of PublishedEvent objects sorted by event name
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * const allEvents = query.publishedEvents()
381
+ * const orderEvents = query.publishedEvents('orders')
382
+ *
383
+ * for (const event of orderEvents) {
384
+ * console.log(`${event.eventName} has ${event.handlers.length} handlers`)
385
+ * }
386
+ * ```
387
+ */
388
+ publishedEvents(domainName) {
389
+ return queryPublishedEvents(this.graph, domainName);
390
+ }
391
+ /**
392
+ * Returns event handlers with their subscriptions.
393
+ *
394
+ * @param eventName - Optional event name to filter handlers by
395
+ * @returns Array of EventHandlerInfo objects sorted by handler name
396
+ *
397
+ * @example
398
+ * ```typescript
399
+ * const allHandlers = query.eventHandlers()
400
+ * const orderPlacedHandlers = query.eventHandlers('order-placed')
401
+ * ```
402
+ */
403
+ eventHandlers(eventName) {
404
+ return queryEventHandlers(this.graph, eventName);
405
+ }
406
+ /**
407
+ * Returns all flows in the graph.
408
+ *
409
+ * Each flow starts from an entry point (UI, API, or Custom with no
410
+ * incoming links) and traces forward through the graph.
411
+ *
412
+ * @returns Array of Flow objects with entry point and steps
413
+ *
414
+ * @example
415
+ * ```typescript
416
+ * const flows = query.flows()
417
+ *
418
+ * for (const flow of flows) {
419
+ * console.log(`Flow: ${flow.entryPoint.name}`)
420
+ * for (const step of flow.steps) {
421
+ * console.log(` ${step.component.name} (depth: ${step.depth})`)
422
+ * }
423
+ * }
424
+ * ```
425
+ */
426
+ flows() {
427
+ return queryFlows(this.graph);
428
+ }
429
+ /**
430
+ * Searches for components and returns their flow context.
431
+ *
432
+ * Returns both matching component IDs and all visible IDs in their flows.
433
+ *
434
+ * @param query - Search term
435
+ * @param options - Search options including returnAllOnEmptyQuery
436
+ * @returns Object with matchingIds and visibleIds arrays
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * const result = query.searchWithFlow('checkout', { returnAllOnEmptyQuery: true })
441
+ * console.log(`Found ${result.matchingIds.length} matches`)
442
+ * console.log(`Showing ${result.visibleIds.length} nodes in context`)
443
+ * ```
444
+ */
445
+ searchWithFlow(query, options) {
446
+ return searchWithFlowContext(this.graph, query, options);
447
+ }
448
+ /**
449
+ * Returns links from a domain to other domains.
450
+ *
451
+ * @param domainName - The source domain name
452
+ * @returns Array of CrossDomainLink objects (deduplicated by target domain and type)
453
+ *
454
+ * @example
455
+ * ```typescript
456
+ * const outgoing = query.crossDomainLinks('orders')
457
+ * ```
458
+ */
459
+ crossDomainLinks(domainName) {
460
+ return queryCrossDomainLinks(this.graph, domainName);
461
+ }
462
+ /**
463
+ * Returns cross-domain connections with API and event counts.
464
+ *
465
+ * Shows both incoming and outgoing connections for a domain.
466
+ *
467
+ * @param domainName - The domain to analyze
468
+ * @returns Array of DomainConnection objects
469
+ *
470
+ * @example
471
+ * ```typescript
472
+ * const connections = query.domainConnections('orders')
473
+ * for (const conn of connections) {
474
+ * console.log(`${conn.direction} to ${conn.targetDomain}: ${conn.apiCount} API, ${conn.eventCount} event`)
475
+ * }
476
+ * ```
477
+ */
478
+ domainConnections(domainName) {
479
+ return queryDomainConnections(this.graph, domainName);
480
+ }
481
+ /**
482
+ * Returns aggregate statistics about the graph.
483
+ *
484
+ * @returns GraphStats with counts for components, links, domains, APIs, entities, and events
485
+ *
486
+ * @example
487
+ * ```typescript
488
+ * const stats = query.stats()
489
+ * console.log(`Components: ${stats.componentCount}`)
490
+ * console.log(`Links: ${stats.linkCount}`)
491
+ * console.log(`Domains: ${stats.domainCount}`)
492
+ * ```
493
+ */
494
+ stats() {
495
+ return queryStats(this.graph);
496
+ }
497
+ /**
498
+ * Calculates depth from entry points for each component.
499
+ *
500
+ * Components unreachable from entry points will not be in the map.
501
+ *
502
+ * @returns Map of component ID to depth (0 = entry point)
503
+ *
504
+ * @example
505
+ * ```typescript
506
+ * const depths = query.nodeDepths()
507
+ * for (const [id, depth] of depths) {
508
+ * console.log(`${id}: depth ${depth}`)
509
+ * }
510
+ * ```
511
+ */
512
+ nodeDepths() {
513
+ return queryNodeDepths(this.graph);
514
+ }
515
+ /**
516
+ * Returns all external links in the graph.
517
+ *
518
+ * External links represent connections from components to external
519
+ * systems that are not part of the graph (e.g., third-party APIs).
520
+ *
521
+ * @returns Array of all external links, or empty array if none exist
522
+ *
523
+ * @example
524
+ * ```typescript
525
+ * const externalLinks = query.externalLinks()
526
+ * for (const link of externalLinks) {
527
+ * console.log(`${link.source} -> ${link.target.name}`)
528
+ * }
529
+ * ```
530
+ */
531
+ externalLinks() {
532
+ return this.graph.externalLinks ?? [];
533
+ }
534
+ /**
535
+ * Returns external domains that components connect to.
536
+ *
537
+ * Each unique external target is returned as a separate ExternalDomain,
538
+ * with aggregated source domains and connection counts.
539
+ *
540
+ * @returns Array of ExternalDomain objects, sorted alphabetically by name
541
+ *
542
+ * @example
543
+ * ```typescript
544
+ * const externals = query.externalDomains()
545
+ * for (const ext of externals) {
546
+ * console.log(`${ext.name}: ${ext.connectionCount} connections from ${ext.sourceDomains.join(', ')}`)
547
+ * }
548
+ * ```
549
+ */
550
+ externalDomains() {
551
+ return queryExternalDomains(this.graph);
552
+ }
553
+ }
@@ -0,0 +1,8 @@
1
+ import type { RiviereGraph, Component, ComponentType } from '@living-architecture/riviere-schema';
2
+ export declare function findComponent(graph: RiviereGraph, predicate: (component: Component) => boolean): Component | undefined;
3
+ export declare function findAllComponents(graph: RiviereGraph, predicate: (component: Component) => boolean): Component[];
4
+ export declare function componentById(graph: RiviereGraph, id: string): Component | undefined;
5
+ export declare function searchComponents(graph: RiviereGraph, query: string): Component[];
6
+ export declare function componentsInDomain(graph: RiviereGraph, domainName: string): Component[];
7
+ export declare function componentsByType(graph: RiviereGraph, type: ComponentType): Component[];
8
+ //# sourceMappingURL=component-queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-queries.d.ts","sourceRoot":"","sources":["../src/component-queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAEjG,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,GAAG,SAAS,GAAG,SAAS,CAEtH;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,GAAG,SAAS,EAAE,CAEhH;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAEpF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAUhF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,CAEvF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,CAEtF"}
@@ -0,0 +1,24 @@
1
+ export function findComponent(graph, predicate) {
2
+ return graph.components.find(predicate);
3
+ }
4
+ export function findAllComponents(graph, predicate) {
5
+ return graph.components.filter(predicate);
6
+ }
7
+ export function componentById(graph, id) {
8
+ return findComponent(graph, (c) => c.id === id);
9
+ }
10
+ export function searchComponents(graph, query) {
11
+ if (query === '') {
12
+ return [];
13
+ }
14
+ const lowerQuery = query.toLowerCase();
15
+ return findAllComponents(graph, (c) => c.name.toLowerCase().includes(lowerQuery) ||
16
+ c.domain.toLowerCase().includes(lowerQuery) ||
17
+ c.type.toLowerCase().includes(lowerQuery));
18
+ }
19
+ export function componentsInDomain(graph, domainName) {
20
+ return findAllComponents(graph, (c) => c.domain === domainName);
21
+ }
22
+ export function componentsByType(graph, type) {
23
+ return findAllComponents(graph, (c) => c.type === type);
24
+ }
@@ -0,0 +1,5 @@
1
+ import type { RiviereGraph } from '@living-architecture/riviere-schema';
2
+ import type { CrossDomainLink, DomainConnection } from './domain-types';
3
+ export declare function queryCrossDomainLinks(graph: RiviereGraph, domainName: string): CrossDomainLink[];
4
+ export declare function queryDomainConnections(graph: RiviereGraph, domainName: string): DomainConnection[];
5
+ //# sourceMappingURL=cross-domain-queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-domain-queries.d.ts","sourceRoot":"","sources":["../src/cross-domain-queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAA;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAOvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,EAAE,CA+BhG;AA8ED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAOlG"}
@@ -0,0 +1,92 @@
1
+ import { parseDomainName } from './domain-types';
2
+ function buildNodeIdToDomain(graph) {
3
+ return new Map(graph.components.map((c) => [c.id, c.domain]));
4
+ }
5
+ export function queryCrossDomainLinks(graph, domainName) {
6
+ const nodeIdToDomain = buildNodeIdToDomain(graph);
7
+ const seen = new Set();
8
+ const results = [];
9
+ for (const link of graph.links) {
10
+ const sourceDomain = nodeIdToDomain.get(link.source);
11
+ const targetDomain = nodeIdToDomain.get(link.target);
12
+ if (sourceDomain !== domainName || targetDomain === domainName) {
13
+ continue;
14
+ }
15
+ if (targetDomain === undefined) {
16
+ continue;
17
+ }
18
+ const linkTypeKey = link.type === undefined ? 'UNDEFINED_LINK_TYPE' : link.type;
19
+ const key = `${targetDomain}:${linkTypeKey}`;
20
+ if (seen.has(key)) {
21
+ continue;
22
+ }
23
+ seen.add(key);
24
+ results.push({
25
+ targetDomain: parseDomainName(targetDomain),
26
+ linkType: link.type,
27
+ });
28
+ }
29
+ return results.sort(compareCrossDomainLinks);
30
+ }
31
+ const UNDEFINED_LINK_TYPE_SORT_KEY = '';
32
+ function linkTypeForSort(linkType) {
33
+ if (linkType === undefined)
34
+ return UNDEFINED_LINK_TYPE_SORT_KEY;
35
+ return linkType;
36
+ }
37
+ function compareCrossDomainLinks(a, b) {
38
+ const domainCompare = a.targetDomain.localeCompare(b.targetDomain);
39
+ if (domainCompare !== 0)
40
+ return domainCompare;
41
+ return linkTypeForSort(a.linkType).localeCompare(linkTypeForSort(b.linkType));
42
+ }
43
+ function initializeConnectionCounts() {
44
+ return { apiCount: 0, eventCount: 0 };
45
+ }
46
+ function getOrInitializeConnectionCounts(map, domain) {
47
+ const existing = map.get(domain);
48
+ if (existing !== undefined) {
49
+ return existing;
50
+ }
51
+ const counts = initializeConnectionCounts();
52
+ map.set(domain, counts);
53
+ return counts;
54
+ }
55
+ function incrementConnectionCount(map, domain, targetType) {
56
+ const counts = getOrInitializeConnectionCounts(map, domain);
57
+ if (targetType === 'API')
58
+ counts.apiCount++;
59
+ if (targetType === 'EventHandler')
60
+ counts.eventCount++;
61
+ }
62
+ function collectConnections(graph, domainName, nodeIdToDomain, nodeById) {
63
+ const outgoing = new Map();
64
+ const incoming = new Map();
65
+ for (const link of graph.links) {
66
+ const sourceDomain = nodeIdToDomain.get(link.source);
67
+ const targetDomain = nodeIdToDomain.get(link.target);
68
+ const targetType = nodeById.get(link.target)?.type;
69
+ if (sourceDomain === domainName && targetDomain !== undefined && targetDomain !== domainName) {
70
+ incrementConnectionCount(outgoing, targetDomain, targetType);
71
+ }
72
+ if (targetDomain === domainName && sourceDomain !== undefined && sourceDomain !== domainName) {
73
+ incrementConnectionCount(incoming, sourceDomain, targetType);
74
+ }
75
+ }
76
+ return { outgoing, incoming };
77
+ }
78
+ function toConnectionResults(connections, direction) {
79
+ return Array.from(connections.entries()).map(([domain, counts]) => ({
80
+ targetDomain: parseDomainName(domain),
81
+ direction,
82
+ apiCount: counts.apiCount,
83
+ eventCount: counts.eventCount,
84
+ }));
85
+ }
86
+ export function queryDomainConnections(graph, domainName) {
87
+ const nodeIdToDomain = buildNodeIdToDomain(graph);
88
+ const nodeById = new Map(graph.components.map((c) => [c.id, { type: c.type }]));
89
+ const { outgoing, incoming } = collectConnections(graph, domainName, nodeIdToDomain, nodeById);
90
+ const results = [...toConnectionResults(outgoing, 'outgoing'), ...toConnectionResults(incoming, 'incoming')];
91
+ return results.sort((a, b) => a.targetDomain.localeCompare(b.targetDomain));
92
+ }
@@ -0,0 +1,4 @@
1
+ import type { RiviereGraph } from '@living-architecture/riviere-schema';
2
+ import type { ComponentId } from './domain-types';
3
+ export declare function queryNodeDepths(graph: RiviereGraph): Map<ComponentId, number>;
4
+ //# sourceMappingURL=depth-queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"depth-queries.d.ts","sourceRoot":"","sources":["../src/depth-queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,qCAAqC,CAAA;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAQjD,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,CAc7E"}