@seed-ship/mcp-ui-solid 6.11.0 → 6.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,19 +5,46 @@
5
5
  import { describe, it, expect } from 'vitest'
6
6
  import { validateAgainstRegistry, getComponentEntry, ComponentRegistry } from './component-registry'
7
7
  import type { ComponentType } from '../types'
8
+ import { ComponentTypeSchema } from '@seed-ship/mcp-ui-spec'
8
9
 
9
- /** All 19 component types in the registry */
10
+ /** All 20 component types in the registry */
10
11
  const ALL_TYPES: ComponentType[] = [
11
12
  'chart', 'table', 'metric', 'text', 'grid',
12
13
  'action', 'footer', 'carousel', 'artifact',
13
- 'code', 'map', 'form', 'modal', 'action-group',
14
+ 'code', 'map', 'graph', 'form', 'modal', 'action-group',
14
15
  'image-gallery', 'video', 'iframe', 'image', 'link',
15
16
  ]
16
17
 
18
+ // Schema component types that are intentionally NOT standalone registry
19
+ // entries. `composite` is a layout container rendered inline by
20
+ // UIResourceRenderer (like a bare layout), not a leaf component with its own
21
+ // params schema / examples.
22
+ const REGISTRY_EXCEPTIONS = new Set<string>(['composite'])
23
+
24
+ describe('registry ↔ schema parity (P1.5)', () => {
25
+ it('every schema component type has a registry entry (except documented containers)', () => {
26
+ const missing = (ComponentTypeSchema.options as readonly string[]).filter(
27
+ (t) => !REGISTRY_EXCEPTIONS.has(t) && !ComponentRegistry.has(t as ComponentType)
28
+ )
29
+ expect(missing).toEqual([])
30
+ })
31
+
32
+ it('every registry type is a valid schema component type', () => {
33
+ const schemaTypes = new Set<string>(ComponentTypeSchema.options)
34
+ const extra = Array.from(ComponentRegistry.keys()).filter((t) => !schemaTypes.has(t))
35
+ expect(extra).toEqual([])
36
+ })
37
+
38
+ it('graph is registered (regression for P1.5)', () => {
39
+ expect(ComponentRegistry.has('graph')).toBe(true)
40
+ expect(getComponentEntry('graph')?.type).toBe('graph')
41
+ })
42
+ })
43
+
17
44
  describe('ComponentRegistry', () => {
18
45
  describe('registry completeness', () => {
19
- it('has exactly 19 registered types', () => {
20
- expect(ComponentRegistry.size).toBe(19)
46
+ it('has exactly 20 registered types', () => {
47
+ expect(ComponentRegistry.size).toBe(20)
21
48
  })
22
49
 
23
50
  it.each(ALL_TYPES)('has registry entry for "%s"', (type) => {
@@ -632,6 +632,75 @@ export const MapRegistry: ComponentRegistryEntry = {
632
632
  limits: DEFAULT_RESOURCE_LIMITS,
633
633
  }
634
634
 
635
+ /**
636
+ * Graph Registry Entry (v6.12.0 — audit P1.5)
637
+ */
638
+ export const GraphRegistry: ComponentRegistryEntry = {
639
+ type: 'graph',
640
+ name: 'NodeLinkGraph',
641
+ description:
642
+ 'Render a node-link graph (entities and their relationships) with @antv/g6. Best for provenance/source chains, dependency or process graphs, and ontology-lite entity/relation views. Degrades to an edge table when the graph engine is unavailable.',
643
+ schema: {
644
+ type: 'object',
645
+ properties: {
646
+ nodes: {
647
+ type: 'array',
648
+ description: 'Graph nodes (at least one required)',
649
+ items: {
650
+ type: 'object',
651
+ properties: {
652
+ id: { type: 'string' },
653
+ label: { type: 'string' },
654
+ group: { type: 'string' },
655
+ },
656
+ required: ['id'],
657
+ },
658
+ },
659
+ edges: {
660
+ type: 'array',
661
+ description: 'Edges between node ids',
662
+ items: {
663
+ type: 'object',
664
+ properties: {
665
+ source: { type: 'string' },
666
+ target: { type: 'string' },
667
+ label: { type: 'string' },
668
+ weight: { type: 'number' },
669
+ },
670
+ required: ['source', 'target'],
671
+ },
672
+ },
673
+ layout: {
674
+ type: 'string',
675
+ enum: ['force', 'radial', 'grid', 'dagre', 'circular'],
676
+ description: 'Layout algorithm (default: force)',
677
+ },
678
+ directed: { type: 'boolean', description: 'Render edges as directed (arrows)' },
679
+ },
680
+ required: ['nodes'],
681
+ },
682
+ examples: [
683
+ {
684
+ query: 'Show how this figure was derived',
685
+ component: {
686
+ id: 'example-graph-1',
687
+ type: 'graph',
688
+ position: { colStart: 1, colSpan: 12 },
689
+ params: {
690
+ directed: true,
691
+ layout: 'dagre',
692
+ nodes: [
693
+ { id: 'claim', label: 'Population = 522 250', group: 'claim' },
694
+ { id: 'source', label: 'INSEE 2021', group: 'source' },
695
+ ],
696
+ edges: [{ source: 'claim', target: 'source', label: 'derived from' }],
697
+ },
698
+ },
699
+ },
700
+ ],
701
+ limits: DEFAULT_RESOURCE_LIMITS,
702
+ }
703
+
635
704
  /**
636
705
  * Form Registry Entry
637
706
  */
@@ -990,6 +1059,8 @@ export const ComponentRegistry: Map<ComponentType, ComponentRegistryEntry> = new
990
1059
  // v2.2.5: Complete registry
991
1060
  ['code', CodeRegistry],
992
1061
  ['map', MapRegistry],
1062
+ ['graph', GraphRegistry], // v6.12.0: audit P1.5 — registry/schema parity
1063
+
993
1064
  ['form', FormRegistry],
994
1065
  ['modal', ModalRegistry],
995
1066
  ['action-group', ActionGroupRegistry],
@@ -0,0 +1,42 @@
1
+ /**
2
+ * v6.13.0 — graph is now first-class in the `UIComponent` params union
3
+ * (audit follow-up to P1.4/P1.5).
4
+ *
5
+ * Compile-time: a `graph` component with `nodes`/`edges` params must be
6
+ * assignable to `UIComponent` (it was not before — the union ended at
7
+ * `MapComponentParams`). Runtime asserts keep the shape visible in reports.
8
+ */
9
+ import { describe, it, expect } from 'vitest'
10
+ import type { UIComponent, GraphComponentParams, GraphNode, GraphEdge } from './index'
11
+
12
+ describe('GraphComponentParams in the UIComponent union (P1.4/P1.5 follow-up)', () => {
13
+ it('accepts a graph component with nodes/edges/layout/directed', () => {
14
+ const nodes: GraphNode[] = [
15
+ { id: 'a', label: 'A', group: 'g1' },
16
+ { id: 'b', label: 'B' },
17
+ ]
18
+ const edges: GraphEdge[] = [{ source: 'a', target: 'b', label: 'rel', weight: 1 }]
19
+ const params: GraphComponentParams = { nodes, edges, layout: 'dagre', directed: true }
20
+
21
+ const component: UIComponent = {
22
+ id: 'g1',
23
+ type: 'graph',
24
+ position: { colStart: 1, colSpan: 12 },
25
+ params,
26
+ }
27
+
28
+ expect(component.type).toBe('graph')
29
+ expect((component.params as GraphComponentParams).nodes).toHaveLength(2)
30
+ expect((component.params as GraphComponentParams).edges).toHaveLength(1)
31
+ expect((component.params as GraphComponentParams).layout).toBe('dagre')
32
+ })
33
+
34
+ it('requires nodes (edges optional) and tolerates passthrough props', () => {
35
+ const minimal: GraphComponentParams = { nodes: [{ id: 'only' }] }
36
+ expect(minimal.edges).toBeUndefined()
37
+
38
+ const extra: GraphComponentParams = { nodes: [{ id: 'x', custom: 42 }], theme: 'dark' }
39
+ expect((extra.nodes[0] as Record<string, unknown>).custom).toBe(42)
40
+ expect((extra as Record<string, unknown>).theme).toBe('dark')
41
+ })
42
+ })
@@ -1021,6 +1021,63 @@ export interface MapPMTilesConfig {
1021
1021
  minZoom?: number
1022
1022
  }
1023
1023
 
1024
+ /**
1025
+ * Node-link graph params (v6.13.0 — audit follow-up to P1.4/P1.5).
1026
+ *
1027
+ * Mirrors `GraphComponentParamsSchema` in `@seed-ship/mcp-ui-spec`. The
1028
+ * `<GraphRenderer>` reads these loosely (and the spec schema accepts
1029
+ * passthrough node/edge props), so an index signature keeps the type
1030
+ * permissive — matching the runtime behaviour without over-constraining.
1031
+ */
1032
+ export interface GraphNode {
1033
+ /** Stable node id — referenced by edges. */
1034
+ id: string
1035
+ label?: string
1036
+ /** Optional grouping/category key (drives default coloring). */
1037
+ group?: string
1038
+ /** Generic ranking/importance signal (opaque to the lib). */
1039
+ weight?: number
1040
+ /** Passthrough G6 node props (type, size, style, data, …). */
1041
+ [key: string]: unknown
1042
+ }
1043
+
1044
+ export interface GraphEdge {
1045
+ /** Must match a `GraphNode.id`. */
1046
+ source: string
1047
+ /** Must match a `GraphNode.id`. */
1048
+ target: string
1049
+ label?: string
1050
+ weight?: number
1051
+ /** Passthrough G6 edge props (style, data, …). */
1052
+ [key: string]: unknown
1053
+ }
1054
+
1055
+ export type GraphLayout = 'force' | 'radial' | 'grid' | 'dagre' | 'circular' | 'concentric'
1056
+
1057
+ /**
1058
+ * Generic node-link graph component params. Domain-neutral by design — the
1059
+ * meaning of `weight`/`group` is decided by the consumer.
1060
+ */
1061
+ export interface GraphComponentParams {
1062
+ /** Nodes (at least one expected at runtime). */
1063
+ nodes: GraphNode[]
1064
+ edges?: GraphEdge[]
1065
+ layout?: GraphLayout
1066
+ /** Render edges as directed (arrows). */
1067
+ directed?: boolean
1068
+ /**
1069
+ * Rendering hint only — NOT wired; the renderer always uses the G6 canvas
1070
+ * default (cf. audit P0). Kept for forward-compat with the spec.
1071
+ */
1072
+ rendererPref?: 'canvas' | 'svg'
1073
+ height?: number
1074
+ width?: number
1075
+ /** Custom CSS class. */
1076
+ className?: string
1077
+ /** Passthrough for forward-compat graph options. */
1078
+ [key: string]: unknown
1079
+ }
1080
+
1024
1081
  /**
1025
1082
  * Grid component parameters (Phase 5.0)
1026
1083
  * Enables nested CSS Grid layouts for template builder
@@ -1079,7 +1136,7 @@ export interface UIComponent {
1079
1136
  /**
1080
1137
  * Component parameters (type-specific)
1081
1138
  */
1082
- params: ChartComponentParams | TableComponentParams | MetricComponentParams | TextComponentParams | ActionComponentParams | GridComponentParams | FormComponentParams | ModalComponentParams | ActionGroupParams | ImageGalleryParams | VideoComponentParams | CodeComponentParams | MapComponentParams
1139
+ params: ChartComponentParams | TableComponentParams | MetricComponentParams | TextComponentParams | ActionComponentParams | GridComponentParams | FormComponentParams | ModalComponentParams | ActionGroupParams | ImageGalleryParams | VideoComponentParams | CodeComponentParams | MapComponentParams | GraphComponentParams
1083
1140
 
1084
1141
  /**
1085
1142
  * Metadata for observability