@crvouga/atlas 0.10.2 → 0.12.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +7 -0
  3. package/dist/reports/src/catalog-health.d.ts +142 -0
  4. package/dist/reports/src/catalog-health.js +257 -0
  5. package/dist/reports/src/test-catalog.d.ts +8 -0
  6. package/dist/reports/src/test-catalog.js +5 -1
  7. package/dist/schema/src/api.d.ts +79 -0
  8. package/dist/schema/src/api.js +50 -0
  9. package/dist/server/src/app.js +9 -0
  10. package/dist/server/src/llms.js +1 -0
  11. package/dist/server/src/openapi.js +18 -2
  12. package/dist/server/src/settings-page.d.ts +4 -4
  13. package/dist/server/src/settings-page.js +17 -78
  14. package/docs/reference/http-api.md +34 -0
  15. package/docs/reference/reports.md +224 -0
  16. package/docs/reference/schema.md +10 -0
  17. package/docs/reference/server.md +4 -4
  18. package/docs/server.md +10 -0
  19. package/docs/ui.md +142 -0
  20. package/docs/workspaces.md +130 -0
  21. package/llms-full.txt +322 -0
  22. package/llms.txt +2 -0
  23. package/package.json +17 -10
  24. package/packages/schema/schemas/catalog-health.schema.json +400 -0
  25. package/packages/server/openapi.json +596 -0
  26. package/packages/visualizer/src/admin/SettingsPage.tsx +456 -0
  27. package/packages/visualizer/src/admin/main.tsx +11 -0
  28. package/packages/visualizer/src/app/AppShell.tsx +62 -38
  29. package/packages/visualizer/src/app/gazetteer.tsx +90 -83
  30. package/packages/visualizer/src/app/router.tsx +33 -15
  31. package/packages/visualizer/src/components/ActivityIndicator.tsx +4 -5
  32. package/packages/visualizer/src/components/DataIssues.tsx +36 -54
  33. package/packages/visualizer/src/components/Header.tsx +197 -125
  34. package/packages/visualizer/src/components/Legend.tsx +13 -41
  35. package/packages/visualizer/src/components/ProjectPicker.tsx +61 -26
  36. package/packages/visualizer/src/components/Screen.tsx +80 -71
  37. package/packages/visualizer/src/components/SpecLogo.tsx +15 -0
  38. package/packages/visualizer/src/components/Status.tsx +20 -34
  39. package/packages/visualizer/src/components/labels.tsx +8 -3
  40. package/packages/visualizer/src/components/spec-logos/APACHE-2.0.txt +206 -0
  41. package/packages/visualizer/src/components/spec-logos/CAMUNDA-MIT.txt +21 -0
  42. package/packages/visualizer/src/components/spec-logos/FILE-ICONS-ISC.txt +13 -0
  43. package/packages/visualizer/src/components/spec-logos/README.md +23 -0
  44. package/packages/visualizer/src/components/spec-logos/SIMPLE-ICONS-CC0.txt +30 -0
  45. package/packages/visualizer/src/components/spec-logos/alloy.svg +1 -0
  46. package/packages/visualizer/src/components/spec-logos/dmn.svg +3 -0
  47. package/packages/visualizer/src/components/spec-logos/gherkin.svg +1 -0
  48. package/packages/visualizer/src/components/spec-logos/openslo.svg +63 -0
  49. package/packages/visualizer/src/components/spec-logos/quint.svg +1 -0
  50. package/packages/visualizer/src/evidence/artifacts.tsx +263 -110
  51. package/packages/visualizer/src/evidence/chart-source.tsx +18 -0
  52. package/packages/visualizer/src/evidence/check-detail.tsx +283 -139
  53. package/packages/visualizer/src/evidence/decision-table.tsx +35 -26
  54. package/packages/visualizer/src/evidence/document-page.tsx +51 -29
  55. package/packages/visualizer/src/evidence/feature-page.tsx +121 -53
  56. package/packages/visualizer/src/evidence/marks.tsx +112 -79
  57. package/packages/visualizer/src/evidence/model-page.tsx +77 -46
  58. package/packages/visualizer/src/evidence/model-trace.tsx +55 -35
  59. package/packages/visualizer/src/evidence/overview.tsx +137 -69
  60. package/packages/visualizer/src/evidence/recording-page.tsx +70 -31
  61. package/packages/visualizer/src/evidence/recording.tsx +135 -68
  62. package/packages/visualizer/src/evidence/snapshot.tsx +99 -43
  63. package/packages/visualizer/src/evidence/source-text.tsx +33 -0
  64. package/packages/visualizer/src/evidence/sources-panel.tsx +138 -144
  65. package/packages/visualizer/src/evidence/spec-header.tsx +52 -36
  66. package/packages/visualizer/src/evidence/spec-index.tsx +61 -32
  67. package/packages/visualizer/src/evidence/spec-page.tsx +56 -25
  68. package/packages/visualizer/src/evidence/test-filter.tsx +38 -14
  69. package/packages/visualizer/src/explorer/Explorer.tsx +688 -261
  70. package/packages/visualizer/src/explorer/ExplorerIndex.tsx +136 -68
  71. package/packages/visualizer/src/explorer/RunsView.tsx +307 -94
  72. package/packages/visualizer/src/explorer/TestDetail.tsx +393 -118
  73. package/packages/visualizer/src/explorer/parts.tsx +35 -13
  74. package/packages/visualizer/src/find/Finder.tsx +106 -90
  75. package/packages/visualizer/src/find/open.ts +17 -20
  76. package/packages/visualizer/src/live/test-progress.tsx +60 -22
  77. package/packages/visualizer/src/looks/Stage.tsx +223 -110
  78. package/packages/visualizer/src/main.tsx +9 -11
  79. package/packages/visualizer/src/map/ChartMap.tsx +125 -69
  80. package/packages/visualizer/src/map/EventEdge.tsx +59 -37
  81. package/packages/visualizer/src/map/map.module.css +12 -546
  82. package/packages/visualizer/src/map/nodes.tsx +124 -70
  83. package/packages/visualizer/src/reachable.test.ts +3 -2
  84. package/packages/visualizer/src/spec-kind.ts +2 -2
  85. package/packages/visualizer/src/ui/grammars/ALLOY-MIT.txt +21 -0
  86. package/packages/visualizer/src/ui/grammars/QUINT-APACHE-2.0.txt +206 -0
  87. package/packages/visualizer/src/ui/grammars/README.md +14 -0
  88. package/packages/visualizer/src/ui/grammars/alloy.json +71 -0
  89. package/packages/visualizer/src/ui/grammars/quint.json +152 -0
  90. package/packages/visualizer/src/ui/highlight.ts +50 -0
  91. package/packages/visualizer/src/ui/theme.tsx +109 -0
  92. package/packages/visualizer/src/views/ChartPage.tsx +46 -26
  93. package/packages/visualizer/src/views/ChartTests.tsx +59 -22
  94. package/packages/visualizer/src/views/ProductPage.tsx +16 -15
  95. package/packages/visualizer/src/views/SidePanel.tsx +241 -131
  96. package/packages/visualizer/src/app/gazetteer.module.css +0 -107
  97. package/packages/visualizer/src/components/ActivityIndicator.module.css +0 -4
  98. package/packages/visualizer/src/components/DataIssues.module.css +0 -136
  99. package/packages/visualizer/src/components/Header.module.css +0 -114
  100. package/packages/visualizer/src/components/Legend.module.css +0 -84
  101. package/packages/visualizer/src/components/Screen.module.css +0 -312
  102. package/packages/visualizer/src/components/Status.module.css +0 -116
  103. package/packages/visualizer/src/components/icons.tsx +0 -156
  104. package/packages/visualizer/src/evidence/artifacts.module.css +0 -213
  105. package/packages/visualizer/src/evidence/evidence.module.css +0 -907
  106. package/packages/visualizer/src/evidence/recording.module.css +0 -460
  107. package/packages/visualizer/src/evidence/sources.module.css +0 -168
  108. package/packages/visualizer/src/evidence/spec-snapshot.tsx +0 -26
  109. package/packages/visualizer/src/explorer/explorer.module.css +0 -1238
  110. package/packages/visualizer/src/find/Finder.module.css +0 -168
  111. package/packages/visualizer/src/looks/stage.module.css +0 -300
  112. package/packages/visualizer/src/styles/global.css +0 -105
  113. package/packages/visualizer/src/styles/tokens.css +0 -80
  114. package/packages/visualizer/src/views/views.module.css +0 -546
@@ -1,3 +1,6 @@
1
+ import { SpecLogo } from '../components/SpecLogo';
2
+ import { IconChevronDown, IconChevronRight } from '@tabler/icons-react';
3
+ import { ActionIcon, Badge, Box, Group, NavLink, Stack, Title } from '@mantine/core';
1
4
  /**
2
5
  * @module Project index
3
6
  *
@@ -7,34 +10,39 @@
7
10
  import type { StatusCounts } from '@crvouga/atlas/reports/evidence-types';
8
11
  import { Link } from '@tanstack/react-router';
9
12
  import { useMemo, useState } from 'react';
10
-
11
- import evidenceStyles from '../evidence/evidence.module.css';
12
13
  import { useEvidence } from '../evidence/evidence-context';
13
14
  import { StatusMark } from '../evidence/marks';
14
15
  import { SPEC_KINDS } from '../spec-kind';
15
16
  import { useTestCatalog } from './catalog';
16
- import styles from './explorer.module.css';
17
17
  import { patched, useExplorerSearch } from './navigate';
18
18
  import { values } from './search';
19
-
20
19
  /** Domains start open when the index has at most this many specs. */
21
20
  const OPEN_SPECS = 80;
22
-
23
21
  function statusOf(counts: StatusCounts) {
24
22
  return counts.failed ? 'failed' : counts.running ? 'running' : counts.passed ? 'passed' : 'not-run';
25
23
  }
26
-
27
24
  /** A tally for an index row: failures in red, otherwise how many tests, quietly. */
28
25
  function Tally({ counts, tests }: { counts: StatusCounts; tests: number }) {
29
- if (counts.failed) return <span className={evidenceStyles.tally} data-status="failed">{counts.failed} failed</span>;
30
- if (counts.running) return <span className={evidenceStyles.tally} data-status="running">Running</span>;
31
- return <span className={evidenceStyles.tally} data-status={counts.passed === tests ? 'passed' : 'not-run'}>{counts.passed === tests ? `${tests} passed` : `${counts.passed}/${tests}`}</span>;
26
+ return (
27
+ <Badge size="xs" color={counts.failed ? 'red' : counts.running ? 'blue' : counts.passed === tests ? 'green' : 'gray'}>
28
+ {counts.failed
29
+ ? `${counts.failed} failed`
30
+ : counts.running
31
+ ? 'Running'
32
+ : counts.passed === tests
33
+ ? `${tests} passed`
34
+ : `${counts.passed}/${tests}`}
35
+ </Badge>
36
+ );
32
37
  }
33
-
34
38
  function add(a: StatusCounts, b: StatusCounts): StatusCounts {
35
- return { passed: a.passed + b.passed, failed: a.failed + b.failed, running: a.running + b.running, 'not-run': a['not-run'] + b['not-run'] };
39
+ return {
40
+ passed: a.passed + b.passed,
41
+ failed: a.failed + b.failed,
42
+ running: a.running + b.running,
43
+ 'not-run': a['not-run'] + b['not-run']
44
+ };
36
45
  }
37
-
38
46
  /** The index beside the explorer. */
39
47
  export function ProjectIndex() {
40
48
  const { files } = useEvidence();
@@ -42,7 +50,14 @@ export function ProjectIndex() {
42
50
  const search = useExplorerSearch();
43
51
  const kinds = SPEC_KINDS.filter((kind) => files.some(kind.accepts));
44
52
  const domains = useMemo(() => {
45
- const byDomain = new Map<string, { specs: NonNullable<typeof model>['facets']['specs'][number][]; counts: StatusCounts; tests: number }>();
53
+ const byDomain = new Map<
54
+ string,
55
+ {
56
+ specs: NonNullable<typeof model>['facets']['specs'][number][];
57
+ counts: StatusCounts;
58
+ tests: number;
59
+ }
60
+ >();
46
61
  for (const spec of model?.facets.specs ?? []) {
47
62
  const domain = spec.domain || '(no domain)';
48
63
  const entry = byDomain.get(domain) ?? { specs: [], counts: { passed: 0, failed: 0, running: 0, 'not-run': 0 }, tests: 0 };
@@ -57,64 +72,117 @@ export function ProjectIndex() {
57
72
  const chosenSpecs = values(search.spec);
58
73
  const chosenDomains = values(search.domain);
59
74
  const [toggled, setToggled] = useState<ReadonlyMap<string, boolean>>(new Map());
60
- const isOpen = (domain: string, specs: readonly { path: string }[]) => toggled.get(domain) ?? (specCount <= OPEN_SPECS || chosenDomains.includes(domain) || specs.some((spec) => chosenSpecs.includes(spec.path)));
75
+ const isOpen = (
76
+ domain: string,
77
+ specs: readonly {
78
+ path: string;
79
+ }[]
80
+ ) =>
81
+ toggled.get(domain) ??
82
+ (specCount <= OPEN_SPECS || chosenDomains.includes(domain) || specs.some((spec) => chosenSpecs.includes(spec.path)));
61
83
  const everything = !search.spec && !search.domain;
62
84
  return (
63
- <nav className={evidenceStyles.index} aria-label="Project specs">
64
- {kinds.length > 0 && (
65
- <>
66
- <h2 className={styles.indexHeading}>Specs</h2>
67
- <ul>
85
+ <Box component="nav" aria-label="Project specs" p="md">
86
+ <Stack gap="lg">
87
+ {kinds.length > 0 && (
88
+ <Stack gap="xs">
89
+ <Title order={3} c="dimmed">
90
+ Specs
91
+ </Title>
68
92
  {kinds.map((kind) => (
69
- <li key={kind.id}>
70
- <Link className={evidenceStyles.indexRow} to="/specs/$kind" params={{ kind: kind.id }} search={{}}>{kind.label}</Link>
71
- </li>
93
+ <NavLink
94
+ key={kind.id}
95
+ label={kind.label}
96
+ leftSection={<SpecLogo kind={kind} />}
97
+ renderRoot={(props) => <Link {...props} to="/specs/$kind" params={{ kind: kind.id }} search={{}} />}
98
+ />
72
99
  ))}
73
- </ul>
74
- </>
75
- )}
76
- {domains.length > 0 && (
77
- <section aria-label="Tests by spec">
78
- <h2 className={styles.indexHeading}>Tests</h2>
79
- <ul>
80
- <li>
81
- <Link className={evidenceStyles.indexRow} to="/" search={patched({ spec: undefined, domain: undefined, tab: undefined })} resetScroll={false} activeOptions={{ exact: true }} aria-current={everything ? 'page' : undefined}>
82
- <span className={evidenceStyles.indexName}>All tests</span>
83
- {model && <Tally counts={model.summary.counts} tests={model.summary.tests} />}
84
- </Link>
85
- </li>
86
- </ul>
87
- {domains.map(([domain, entry]) => {
88
- const open = isOpen(domain, entry.specs);
89
- return (
90
- <div key={domain} className={styles.indexDomain}>
91
- <div className={styles.indexDomainHead}>
92
- <button type="button" className={styles.indexToggle} aria-expanded={open} aria-label={`${open ? 'Collapse' : 'Expand'} ${domain}`} onClick={() => setToggled(new Map(toggled).set(domain, !open))}>
93
- <span className={styles.chevron} data-open={open || undefined} aria-hidden="true" />
94
- </button>
95
- <Link className={evidenceStyles.indexRow} to="/" search={patched({ domain, spec: undefined, tab: undefined })} resetScroll={false} activeOptions={{ exact: true }} aria-current={chosenDomains.length === 1 && chosenDomains[0] === domain && !search.spec ? 'page' : undefined}>
96
- <span className={evidenceStyles.indexName}><strong>{domain}</strong></span>
97
- <Tally counts={entry.counts} tests={entry.tests} />
98
- </Link>
99
- </div>
100
- {open && (
101
- <ul className={styles.indexSpecs}>
102
- {entry.specs.map((spec) => (
103
- <li key={spec.path}>
104
- <Link className={evidenceStyles.indexRow} to="/" search={patched({ spec: spec.path, domain: undefined, tab: undefined })} resetScroll={false} activeOptions={{ exact: true }} title={spec.path} aria-current={chosenSpecs.length === 1 && chosenSpecs[0] === spec.path ? 'page' : undefined}>
105
- <StatusMark status={statusOf(spec.counts)} size={14} />
106
- <span className={evidenceStyles.indexName}>{spec.title}</span>
107
- <Tally counts={spec.counts} tests={spec.tests} />
108
- </Link>
109
- </li>
110
- ))}
111
- </ul>
112
- )}
113
- </div>
114
- );
115
- })}
116
- </section>
117
- )}
118
- </nav>
100
+ </Stack>
101
+ )}
102
+ {domains.length > 0 && (
103
+ <Box component="section" aria-label="Tests by spec">
104
+ <Title order={3} c="dimmed" mb="sm">
105
+ Tests
106
+ </Title>
107
+ <NavLink
108
+ label="All tests"
109
+ active={everything}
110
+ rightSection={model && <Tally counts={model.summary.counts} tests={model.summary.tests} />}
111
+ aria-current={everything ? 'page' : undefined}
112
+ renderRoot={(props) => (
113
+ <Link
114
+ {...props}
115
+ to="/"
116
+ search={patched({ spec: undefined, domain: undefined, tab: undefined })}
117
+ resetScroll={false}
118
+ activeOptions={{ exact: true }}
119
+ />
120
+ )}
121
+ />
122
+ {domains.map(([domain, entry]) => {
123
+ const open = isOpen(domain, entry.specs);
124
+ const chosen = chosenDomains.length === 1 && chosenDomains[0] === domain && !search.spec;
125
+ return (
126
+ <Box key={domain} mt="sm">
127
+ <Group wrap="nowrap" gap={0}>
128
+ <ActionIcon
129
+ color="gray"
130
+ aria-expanded={open}
131
+ aria-label={`${open ? 'Collapse' : 'Expand'} ${domain}`}
132
+ onClick={() => setToggled(new Map(toggled).set(domain, !open))}
133
+ >
134
+ {open ? <IconChevronDown size={16} aria-hidden="true" /> : <IconChevronRight size={16} aria-hidden="true" />}
135
+ </ActionIcon>
136
+ <NavLink
137
+ flex={1}
138
+ label={domain}
139
+ active={chosen}
140
+ rightSection={<Tally counts={entry.counts} tests={entry.tests} />}
141
+ aria-current={chosen ? 'page' : undefined}
142
+ renderRoot={(props) => (
143
+ <Link
144
+ {...props}
145
+ to="/"
146
+ search={patched({ domain, spec: undefined, tab: undefined })}
147
+ resetScroll={false}
148
+ activeOptions={{ exact: true }}
149
+ />
150
+ )}
151
+ />
152
+ </Group>
153
+ {open && (
154
+ <Box pl="sm">
155
+ {entry.specs.map((spec) => {
156
+ const chosen = chosenSpecs.length === 1 && chosenSpecs[0] === spec.path;
157
+ return (
158
+ <NavLink
159
+ key={spec.path}
160
+ label={spec.title}
161
+ title={spec.path}
162
+ active={chosen}
163
+ leftSection={<StatusMark status={statusOf(spec.counts)} size={14} />}
164
+ rightSection={<Tally counts={spec.counts} tests={spec.tests} />}
165
+ aria-current={chosen ? 'page' : undefined}
166
+ renderRoot={(props) => (
167
+ <Link
168
+ {...props}
169
+ to="/"
170
+ search={patched({ spec: spec.path, domain: undefined, tab: undefined })}
171
+ resetScroll={false}
172
+ activeOptions={{ exact: true }}
173
+ />
174
+ )}
175
+ />
176
+ );
177
+ })}
178
+ </Box>
179
+ )}
180
+ </Box>
181
+ );
182
+ })}
183
+ </Box>
184
+ )}
185
+ </Stack>
186
+ </Box>
119
187
  );
120
188
  }