@ontrails/topography 1.0.0-beta.41 → 1.0.0-beta.43
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/CHANGELOG.md +20 -0
- package/README.md +4 -4
- package/package.json +3 -3
- package/src/activation-report.ts +9 -9
- package/src/derive.ts +20 -20
- package/src/index.ts +4 -4
- package/src/internal/topo-store-read.ts +9 -9
- package/src/internal/topo-store.ts +42 -42
- package/src/io.ts +1 -1
- package/src/{library-projection.ts → library-derivation.ts} +3 -5
- package/src/permit.ts +4 -4
- package/src/surface-bindings.ts +4 -4
- package/src/topo-store.ts +1 -1
- package/src/types.ts +4 -4
- package/src/versioning.ts +23 -26
- package/src/wayfind/error-facts.ts +14 -14
- package/src/wayfind/queries.ts +8 -8
- package/src/wayfind/relations.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @ontrails/topography
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.43
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`88a6a62`](https://github.com/outfitter-dev/trails/commit/88a6a62a9e9e230ca6d368fa78dc3ece6c816204): Complete the v1 classification-first cutover from projection/project vocabulary
|
|
8
|
+
to derive/derived for contract-owned fact production and render/rendered for
|
|
9
|
+
surface presentation. Public type, helper, rule, relation, and report names move
|
|
10
|
+
without compatibility aliases; ordinary repository/project nouns remain
|
|
11
|
+
explicit preserves or structured review inventory.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [`9f0842e`](https://github.com/outfitter-dev/trails/commit/9f0842ee9d7c7155d86a4fd023760ac0a5636f5d): Retire the temporary root vocabulary-cutover toolchain now that Regrade owns
|
|
16
|
+
structured migration plans, safe rewrites, classification, census, CLI/MCP
|
|
17
|
+
reports, and immutable history. Remove the obsolete source exemptions so
|
|
18
|
+
Oxlint and Warden enforce the durable transition contract directly, and add a
|
|
19
|
+
history-driven Regrade audit surface for current-tree regression checks.
|
|
20
|
+
|
|
21
|
+
## 1.0.0-beta.42
|
|
22
|
+
|
|
3
23
|
## 1.0.0-beta.41
|
|
4
24
|
|
|
5
25
|
## 1.0.0-beta.40
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ This is not a private helper package. The Trails operator app consumes Topograph
|
|
|
20
20
|
## What it owns
|
|
21
21
|
|
|
22
22
|
- deterministic TopoGraph generation from an established topo
|
|
23
|
-
- structured example and field-override provenance
|
|
23
|
+
- structured example and field-override provenance derivation for TopoGraph entries
|
|
24
24
|
- stable hashing for CI drift detection
|
|
25
25
|
- semantic diffing between two TopoGraphs
|
|
26
26
|
- file I/O helpers for root `trails.lock` plus legacy artifact-family readers
|
|
@@ -91,11 +91,11 @@ Compatibility helpers still read the previous `.trails/trails.lock` plus `.trail
|
|
|
91
91
|
| `readTopoGraph(options?)` | Read a TopoGraph from v4 `trails.lock` or legacy `topo.lock` |
|
|
92
92
|
| `writeTopoGraph(topoGraph, options?)` | Write legacy `topo.lock` for explicit migration/testing paths |
|
|
93
93
|
| `writeLockManifest(manifest, options?)` | Write legacy `trails.lock` as a v3 manifest |
|
|
94
|
-
| `readLockManifest(options?)` | Read v3 manifests,
|
|
94
|
+
| `readLockManifest(options?)` | Read v3 manifests, deriving v4 locks back to v3 for compatibility |
|
|
95
95
|
| `createTopoStore(options?)` | Read-only query interface over the persisted topo state in the Trails state-store `trails.db` |
|
|
96
96
|
| `createMockTopoStore(seed?)` | Seeded in-memory mock for tests that need a `ReadOnlyTopoStore` |
|
|
97
97
|
| `topoStore` | Read-only `resource()` wrapper around `createTopoStore`, suitable for `resources: [...]` |
|
|
98
|
-
| `createTopoSnapshot(topo, options?)` | Persist a new topo snapshot row plus its denormalized
|
|
98
|
+
| `createTopoSnapshot(topo, options?)` | Persist a new topo snapshot row plus its denormalized derived facts |
|
|
99
99
|
| `listTopoSnapshots(options?)` | List historical topo snapshots (filterable by pinned status) |
|
|
100
100
|
| `pinTopoSnapshot(id, name, options?)` / `unpinTopoSnapshot(nameOrId, options?)` | Manage human-named pins |
|
|
101
101
|
|
|
@@ -132,7 +132,7 @@ Wayfind trails are internal by default. Surface hosts expose selected query trai
|
|
|
132
132
|
|
|
133
133
|
### Operator File Outline
|
|
134
134
|
|
|
135
|
-
File outline is an operator capability, not a public
|
|
135
|
+
File outline is an operator capability, not a public Topography query trail. Use `trails wayfind file <file> --outline` for a compact map of authored trail and app declarations, surface membership, saved graph matches, and diagnostics. Add `--source` when the inspection also needs import, export, and declaration rows. The operator parses the explicit file through `@ontrails/source` and reconciles trail IDs with saved Topography artifacts. Missing artifacts are diagnostics, not hard failures, so outline remains useful in a fresh checkout or during repair work.
|
|
136
136
|
|
|
137
137
|
### Backend Support Subpath
|
|
138
138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/topography",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.43",
|
|
4
4
|
"description": "Durable graph substrate for Trails: deterministic TopoGraphs, lockfile helpers, and semantic diffing.",
|
|
5
5
|
"files": [
|
|
6
6
|
"src/**/*.ts",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@ontrails/adapter-kit": "^1.0.0-beta.
|
|
27
|
+
"@ontrails/adapter-kit": "^1.0.0-beta.43"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
30
|
+
"@ontrails/core": "^1.0.0-beta.43",
|
|
31
31
|
"zod": "^4.3.5"
|
|
32
32
|
}
|
|
33
33
|
}
|
package/src/activation-report.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ActivationEntry,
|
|
3
3
|
ActivationSource,
|
|
4
|
-
|
|
4
|
+
ActivationSourceFacts,
|
|
5
5
|
AnyTrail,
|
|
6
6
|
Topo,
|
|
7
7
|
} from '@ontrails/core';
|
|
8
8
|
import {
|
|
9
9
|
activationSourceKey,
|
|
10
|
-
|
|
10
|
+
deriveActivationSourceFacts,
|
|
11
11
|
} from '@ontrails/core';
|
|
12
12
|
|
|
13
13
|
export interface ActivationChainReport {
|
|
@@ -18,7 +18,7 @@ export interface ActivationChainReport {
|
|
|
18
18
|
|
|
19
19
|
type JsonSchemaReport = Readonly<Record<string, unknown>>;
|
|
20
20
|
|
|
21
|
-
export interface ActivationSourceReport extends
|
|
21
|
+
export interface ActivationSourceReport extends ActivationSourceFacts {
|
|
22
22
|
readonly cron?: string | undefined;
|
|
23
23
|
readonly hasParse?: true | undefined;
|
|
24
24
|
readonly hasPayloadSchema?: true | undefined;
|
|
@@ -153,12 +153,12 @@ const compareChains = (
|
|
|
153
153
|
const sortedUnique = (values: Iterable<string>): readonly string[] =>
|
|
154
154
|
[...new Set(values)].toSorted();
|
|
155
155
|
|
|
156
|
-
const
|
|
156
|
+
const deriveActivationSource = (
|
|
157
157
|
source: ActivationSource
|
|
158
158
|
): ActivationSourceReport =>
|
|
159
|
-
|
|
159
|
+
deriveActivationSourceFacts(source) as ActivationSourceReport;
|
|
160
160
|
|
|
161
|
-
const
|
|
161
|
+
const deriveActivationEdge = (
|
|
162
162
|
trailId: string,
|
|
163
163
|
activation: ActivationEntry
|
|
164
164
|
): ActivationEdgeReport => {
|
|
@@ -187,8 +187,8 @@ const collectActivationSourceCatalog = (
|
|
|
187
187
|
const sources = new Map<string, ActivationSourceReport>();
|
|
188
188
|
for (const trail of trails) {
|
|
189
189
|
for (const activation of trail.activationSources) {
|
|
190
|
-
const
|
|
191
|
-
sources.set(
|
|
190
|
+
const derived = deriveActivationSource(activation.source);
|
|
191
|
+
sources.set(derived.key, derived);
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
return [...sources.values()].toSorted((a, b) => a.key.localeCompare(b.key));
|
|
@@ -200,7 +200,7 @@ const collectActivationEdges = (
|
|
|
200
200
|
const edges = new Map<string, ActivationEdgeReport>();
|
|
201
201
|
for (const trail of trails) {
|
|
202
202
|
for (const activation of trail.activationSources) {
|
|
203
|
-
const edge =
|
|
203
|
+
const edge = deriveActivationEdge(trail.id, activation);
|
|
204
204
|
const key = `${edge.sourceKey}\0${edge.trailId}`;
|
|
205
205
|
const previous = edges.get(key);
|
|
206
206
|
edges.set(
|
package/src/derive.ts
CHANGED
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
activationSourceKey,
|
|
8
8
|
deriveStructuredSignalExamples,
|
|
9
9
|
deriveStructuredTrailExamples,
|
|
10
|
-
|
|
10
|
+
deriveTrailCliCommandRendering,
|
|
11
11
|
getEntityReferences,
|
|
12
|
-
|
|
12
|
+
deriveActivationSourceFacts,
|
|
13
13
|
validateEstablishedTopo,
|
|
14
14
|
signalDiagnosticDefinitions,
|
|
15
15
|
zodToJsonSchema,
|
|
@@ -28,14 +28,14 @@ import type {
|
|
|
28
28
|
} from '@ontrails/core';
|
|
29
29
|
|
|
30
30
|
import { addPermitRequirement } from './permit.js';
|
|
31
|
-
import {
|
|
31
|
+
import { deriveTopoGraphLibrary } from './library-derivation.js';
|
|
32
32
|
import { collectTopoGraphOverlays } from './overlays.js';
|
|
33
33
|
import {
|
|
34
34
|
resolveCliAliasInputsFromOverlays,
|
|
35
35
|
resolveTrailheadEntriesFromOverlays,
|
|
36
36
|
} from './surface-bindings.js';
|
|
37
37
|
import { TOPO_GRAPH_SCHEMA_VERSION } from './types.js';
|
|
38
|
-
import {
|
|
38
|
+
import { deriveTrailVersions } from './versioning.js';
|
|
39
39
|
import type {
|
|
40
40
|
DeriveTopoGraphOptions,
|
|
41
41
|
JsonSchema,
|
|
@@ -156,12 +156,12 @@ const SIGNAL_GOVERNANCE_HOOKS = {
|
|
|
156
156
|
producers: 'trail.fires',
|
|
157
157
|
} as const;
|
|
158
158
|
|
|
159
|
-
const
|
|
159
|
+
const deriveActivationSource = (
|
|
160
160
|
source: ActivationSource
|
|
161
161
|
): TopoGraphActivationSource =>
|
|
162
|
-
|
|
162
|
+
deriveActivationSourceFacts(source) as TopoGraphActivationSource;
|
|
163
163
|
|
|
164
|
-
const
|
|
164
|
+
const deriveActivationEdge = (
|
|
165
165
|
trailId: string,
|
|
166
166
|
activation: ActivationEntry
|
|
167
167
|
): TopoGraphActivationEdge => {
|
|
@@ -190,8 +190,8 @@ const collectActivationSourceCatalog = (
|
|
|
190
190
|
const sources = new Map<string, TopoGraphActivationSource>();
|
|
191
191
|
for (const trail of topo.trails.values()) {
|
|
192
192
|
for (const activation of trail.activationSources) {
|
|
193
|
-
const
|
|
194
|
-
sources.set(
|
|
193
|
+
const derived = deriveActivationSource(activation.source);
|
|
194
|
+
sources.set(derived.key, derived);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
return [...sources.values()].toSorted((a, b) => a.key.localeCompare(b.key));
|
|
@@ -203,7 +203,7 @@ const collectActivationGraphEdges = (
|
|
|
203
203
|
const edges = new Map<string, TopoGraphActivationEdge>();
|
|
204
204
|
for (const trail of topo.trails.values()) {
|
|
205
205
|
for (const activation of trail.activationSources) {
|
|
206
|
-
const edge =
|
|
206
|
+
const edge = deriveActivationEdge(trail.id, activation);
|
|
207
207
|
const key = `${edge.sourceKey}\0${edge.trailId}`;
|
|
208
208
|
const previous = edges.get(key);
|
|
209
209
|
edges.set(
|
|
@@ -359,7 +359,7 @@ const addTrailRelations = (
|
|
|
359
359
|
...(activation.meta === undefined
|
|
360
360
|
? {}
|
|
361
361
|
: { meta: canonicalize(activation.meta) }),
|
|
362
|
-
source:
|
|
362
|
+
source: deriveActivationSource(activation.source),
|
|
363
363
|
...(activation.where === undefined
|
|
364
364
|
? {}
|
|
365
365
|
: { where: sortKeys({ predicate: true }) }),
|
|
@@ -482,16 +482,16 @@ const addVersioning = (
|
|
|
482
482
|
if (t.version === undefined) {
|
|
483
483
|
return;
|
|
484
484
|
}
|
|
485
|
-
const
|
|
486
|
-
if (
|
|
485
|
+
const derivation = deriveTrailVersions(t, toSortedJsonSchema);
|
|
486
|
+
if (derivation === undefined) {
|
|
487
487
|
return;
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
entry['marker'] =
|
|
491
|
-
entry['supports'] =
|
|
492
|
-
entry['version'] =
|
|
493
|
-
if (
|
|
494
|
-
entry['versions'] =
|
|
490
|
+
entry['marker'] = derivation.marker;
|
|
491
|
+
entry['supports'] = derivation.supports;
|
|
492
|
+
entry['version'] = derivation.version;
|
|
493
|
+
if (derivation.versions !== undefined) {
|
|
494
|
+
entry['versions'] = derivation.versions;
|
|
495
495
|
}
|
|
496
496
|
};
|
|
497
497
|
|
|
@@ -503,7 +503,7 @@ const trailToEntry = (
|
|
|
503
503
|
const raw = t as unknown as Record<string, unknown>;
|
|
504
504
|
const surfaces = extractSurfaces(raw);
|
|
505
505
|
const entry: Record<string, unknown> = {
|
|
506
|
-
cli:
|
|
506
|
+
cli: deriveTrailCliCommandRendering(t, {
|
|
507
507
|
aliasSource: 'surface',
|
|
508
508
|
aliases: surfaceAliases?.[t.id],
|
|
509
509
|
}),
|
|
@@ -673,7 +673,7 @@ export const deriveTopoGraph = (
|
|
|
673
673
|
options?.overlays
|
|
674
674
|
);
|
|
675
675
|
const overlays = collectTopoGraphOverlays(topo, options?.overlays);
|
|
676
|
-
const library =
|
|
676
|
+
const library = deriveTopoGraphLibrary(topo);
|
|
677
677
|
|
|
678
678
|
return {
|
|
679
679
|
activationGraph: buildActivationGraph(activationEdges, activationSources),
|
package/src/index.ts
CHANGED
|
@@ -61,7 +61,7 @@ export {
|
|
|
61
61
|
TRAILS_LOCK_SCHEMA_VERSION,
|
|
62
62
|
trailsLockSchema,
|
|
63
63
|
topoGraphTrailheadEntrySchema,
|
|
64
|
-
|
|
64
|
+
topoGraphLibraryDerivedSchema,
|
|
65
65
|
workspaceTopoMetadataSchema,
|
|
66
66
|
workspaceTrailCollisionSchema,
|
|
67
67
|
workspaceTrailEntrySchema,
|
|
@@ -98,7 +98,7 @@ export type {
|
|
|
98
98
|
TopoGraphLibraryExclusionReason,
|
|
99
99
|
TopoGraphLibraryExport,
|
|
100
100
|
TopoGraphLibraryExportSource,
|
|
101
|
-
|
|
101
|
+
TopoGraphLibraryDerived,
|
|
102
102
|
TopoGraphLayerReference,
|
|
103
103
|
TopoGraphOverlayRegistration,
|
|
104
104
|
TopoGraphOverlays,
|
|
@@ -151,7 +151,7 @@ export type {
|
|
|
151
151
|
TopoStoreRef,
|
|
152
152
|
TopoStoreSignalDetailRecord,
|
|
153
153
|
TopoStoreSignalRecord,
|
|
154
|
-
|
|
154
|
+
TopoStoreSurfaceDerivedRecord,
|
|
155
155
|
TopoStoreTopoGraphEntryRecord,
|
|
156
156
|
TopoStoreTopoGraphRecord,
|
|
157
157
|
TopoStoreTrailDetailRecord,
|
|
@@ -169,7 +169,7 @@ export type {
|
|
|
169
169
|
TrailErrorFacts,
|
|
170
170
|
TrailErrorFactsCompleteness,
|
|
171
171
|
TrailErrorFactsOptions,
|
|
172
|
-
|
|
172
|
+
TrailErrorTaxonomyFacts,
|
|
173
173
|
} from './wayfind/error-facts.js';
|
|
174
174
|
export {
|
|
175
175
|
createWayfinderEntityPredicate,
|
|
@@ -52,7 +52,7 @@ export interface TopoStoreActivationContextRecord {
|
|
|
52
52
|
readonly trailIds: readonly string[];
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export interface
|
|
55
|
+
export interface TopoStoreSurfaceDerivedRecord {
|
|
56
56
|
readonly derivedName: string;
|
|
57
57
|
readonly method: string | null;
|
|
58
58
|
readonly surface: string;
|
|
@@ -103,7 +103,7 @@ export interface TopoStoreTrailDetailRecord extends TopoStoreTrailRecord {
|
|
|
103
103
|
readonly layers: readonly TopoGraphLayerReference[];
|
|
104
104
|
readonly output: JsonSchema | null;
|
|
105
105
|
readonly resources: readonly string[];
|
|
106
|
-
readonly
|
|
106
|
+
readonly derivedSurfaces: readonly TopoStoreSurfaceDerivedRecord[];
|
|
107
107
|
readonly surfaces: readonly string[];
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -175,7 +175,7 @@ interface TopoExampleRow {
|
|
|
175
175
|
readonly signals: string | null;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
interface
|
|
178
|
+
interface TopoSurfaceDerivedRow {
|
|
179
179
|
readonly derived_name: string;
|
|
180
180
|
readonly method: string | null;
|
|
181
181
|
readonly surface: string;
|
|
@@ -424,13 +424,13 @@ const readTrailExamples = (
|
|
|
424
424
|
signals: row.signals === null ? null : parseJson(row.signals),
|
|
425
425
|
}));
|
|
426
426
|
|
|
427
|
-
const
|
|
427
|
+
const readTrailSurfaceDerived = (
|
|
428
428
|
db: Database,
|
|
429
429
|
snapshotId: string,
|
|
430
430
|
trailId: string
|
|
431
|
-
): readonly
|
|
431
|
+
): readonly TopoStoreSurfaceDerivedRecord[] =>
|
|
432
432
|
db
|
|
433
|
-
.query<
|
|
433
|
+
.query<TopoSurfaceDerivedRow, [string, string]>(
|
|
434
434
|
`SELECT trail_id, surface, derived_name, method
|
|
435
435
|
FROM topo_surfaces
|
|
436
436
|
WHERE snapshot_id = ? AND trail_id = ?
|
|
@@ -567,7 +567,7 @@ const buildTrailGraphDetail = (
|
|
|
567
567
|
| 'input'
|
|
568
568
|
| 'layers'
|
|
569
569
|
| 'output'
|
|
570
|
-
| '
|
|
570
|
+
| 'derivedSurfaces'
|
|
571
571
|
| 'surfaces'
|
|
572
572
|
> => {
|
|
573
573
|
const storedTopoGraph = readStoredTopoGraph(db, snapshotId);
|
|
@@ -579,6 +579,7 @@ const buildTrailGraphDetail = (
|
|
|
579
579
|
activationEdges: readTrailActivationEdges(storedTopoGraph, trailId),
|
|
580
580
|
activationSources: entryDetail.activationSources,
|
|
581
581
|
cli: entryDetail.cli,
|
|
582
|
+
derivedSurfaces: readTrailSurfaceDerived(db, snapshotId, trailId),
|
|
582
583
|
detours: entryDetail.detours,
|
|
583
584
|
entities: entryDetail.entities,
|
|
584
585
|
entityDetails: readTrailEntityDetails(
|
|
@@ -591,7 +592,6 @@ const buildTrailGraphDetail = (
|
|
|
591
592
|
input: entryDetail.input,
|
|
592
593
|
layers: entryDetail.layers,
|
|
593
594
|
output: entryDetail.output,
|
|
594
|
-
surfaceProjections: readTrailSurfaceProjections(db, snapshotId, trailId),
|
|
595
595
|
surfaces: entryDetail.surfaces,
|
|
596
596
|
};
|
|
597
597
|
};
|
|
@@ -871,6 +871,7 @@ export const getTopoStoreTrail = (
|
|
|
871
871
|
activationSources: graphDetail.activationSources,
|
|
872
872
|
cli: graphDetail.cli,
|
|
873
873
|
composes: readTrailComposings(db, snapshot.id, trailId),
|
|
874
|
+
derivedSurfaces: graphDetail.derivedSurfaces,
|
|
874
875
|
detours: graphDetail.detours,
|
|
875
876
|
entities: graphDetail.entities,
|
|
876
877
|
entityDetails: graphDetail.entityDetails,
|
|
@@ -881,7 +882,6 @@ export const getTopoStoreTrail = (
|
|
|
881
882
|
layers: graphDetail.layers,
|
|
882
883
|
output: graphDetail.output,
|
|
883
884
|
resources: readTrailResourceIds(db, snapshot.id, trailId),
|
|
884
|
-
surfaceProjections: graphDetail.surfaceProjections,
|
|
885
885
|
surfaces: graphDetail.surfaces,
|
|
886
886
|
};
|
|
887
887
|
};
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
deriveCliPath,
|
|
8
8
|
deriveStructuredSignalExamples,
|
|
9
9
|
deriveStructuredTrailExamples,
|
|
10
|
-
|
|
10
|
+
deriveTrailCliCommandRendering,
|
|
11
11
|
getEntityReferences,
|
|
12
|
-
|
|
12
|
+
deriveActivationSourceFacts,
|
|
13
13
|
signalDiagnosticDefinitions,
|
|
14
14
|
validateEstablishedTopo,
|
|
15
15
|
zodToJsonSchema,
|
|
@@ -28,7 +28,7 @@ import type {
|
|
|
28
28
|
} from '@ontrails/core';
|
|
29
29
|
|
|
30
30
|
import { addPermitRequirement } from '../permit.js';
|
|
31
|
-
import {
|
|
31
|
+
import { deriveTopoGraphLibrary } from '../library-derivation.js';
|
|
32
32
|
import { collectTopoGraphOverlays } from '../overlays.js';
|
|
33
33
|
import {
|
|
34
34
|
resolveCliAliasInputsFromOverlays,
|
|
@@ -39,12 +39,12 @@ import {
|
|
|
39
39
|
LOCK_MANIFEST_SCHEMA_VERSION,
|
|
40
40
|
TOPO_GRAPH_SCHEMA_VERSION,
|
|
41
41
|
} from '../types.js';
|
|
42
|
-
import {
|
|
42
|
+
import { deriveTrailVersions } from '../versioning.js';
|
|
43
43
|
import type {
|
|
44
44
|
LockManifest,
|
|
45
45
|
TopoGraphFieldOverride,
|
|
46
46
|
TopoGraphFieldOverrideKey,
|
|
47
|
-
|
|
47
|
+
TopoGraphLibraryDerived,
|
|
48
48
|
TopoGraphOverlays,
|
|
49
49
|
TopoGraphTrailheadEntry,
|
|
50
50
|
} from '../types.js';
|
|
@@ -69,7 +69,7 @@ type TopoGraphRecord = Readonly<{
|
|
|
69
69
|
>;
|
|
70
70
|
readonly entries: readonly TopoGraphEntryRecord[];
|
|
71
71
|
readonly generatedAt: string;
|
|
72
|
-
readonly library:
|
|
72
|
+
readonly library: TopoGraphLibraryDerived;
|
|
73
73
|
readonly overlays?: TopoGraphOverlays | undefined;
|
|
74
74
|
readonly topoGraphSchemaVersion: typeof TOPO_GRAPH_SCHEMA_VERSION;
|
|
75
75
|
readonly trailheads?: readonly TopoGraphTrailheadEntry[] | undefined;
|
|
@@ -268,7 +268,7 @@ const SIGNAL_GOVERNANCE_HOOKS = {
|
|
|
268
268
|
producers: 'trail.fires',
|
|
269
269
|
} as const;
|
|
270
270
|
|
|
271
|
-
interface
|
|
271
|
+
interface NormalizedTopoFacts {
|
|
272
272
|
readonly activationEdges: readonly TopoActivationEdgeRow[];
|
|
273
273
|
readonly activationSources: readonly TopoActivationSourceRow[];
|
|
274
274
|
readonly composings: readonly TopoComposingRow[];
|
|
@@ -450,12 +450,12 @@ export const normalizeOnRows = (
|
|
|
450
450
|
}))
|
|
451
451
|
);
|
|
452
452
|
|
|
453
|
-
const
|
|
453
|
+
const deriveActivationSource = (
|
|
454
454
|
source: ActivationSource
|
|
455
455
|
): ActivationSourceCatalogRecord =>
|
|
456
|
-
|
|
456
|
+
deriveActivationSourceFacts(source) as ActivationSourceCatalogRecord;
|
|
457
457
|
|
|
458
|
-
const
|
|
458
|
+
const deriveActivationEdge = (
|
|
459
459
|
trailId: string,
|
|
460
460
|
activation: ActivationEntry
|
|
461
461
|
): ActivationGraphEdgeRecord => {
|
|
@@ -484,8 +484,8 @@ const collectActivationSourceCatalog = (
|
|
|
484
484
|
const sources = new Map<string, ActivationSourceCatalogRecord>();
|
|
485
485
|
for (const trail of trails) {
|
|
486
486
|
for (const activation of trail.activationSources) {
|
|
487
|
-
const
|
|
488
|
-
sources.set(
|
|
487
|
+
const derived = deriveActivationSource(activation.source);
|
|
488
|
+
sources.set(derived.key, derived);
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
return [...sources.values()].toSorted((a, b) => a.key.localeCompare(b.key));
|
|
@@ -497,7 +497,7 @@ const collectActivationGraphEdges = (
|
|
|
497
497
|
const edges = new Map<string, ActivationGraphEdgeRecord>();
|
|
498
498
|
for (const trail of trails) {
|
|
499
499
|
for (const activation of trail.activationSources) {
|
|
500
|
-
const edge =
|
|
500
|
+
const edge = deriveActivationEdge(trail.id, activation);
|
|
501
501
|
const key = `${edge.sourceKey}\0${edge.trailId}`;
|
|
502
502
|
const previous = edges.get(key);
|
|
503
503
|
edges.set(
|
|
@@ -606,10 +606,10 @@ const normalizeTrailSignalRows = (
|
|
|
606
606
|
});
|
|
607
607
|
|
|
608
608
|
/**
|
|
609
|
-
*
|
|
609
|
+
* Derive surface rows for stored topo.
|
|
610
610
|
*
|
|
611
611
|
* Currently records only CLI-derived rows. MCP, HTTP, and other surface
|
|
612
|
-
*
|
|
612
|
+
* derivations are intentionally deferred until the topo-store schema supports
|
|
613
613
|
* multi-surface representation. The JSON export (`topo_graph` in
|
|
614
614
|
* `topo_exports`) is more faithful for now. See ADR-0015 for the target shape.
|
|
615
615
|
*/
|
|
@@ -657,10 +657,10 @@ const normalizeExampleRows = (
|
|
|
657
657
|
}));
|
|
658
658
|
});
|
|
659
659
|
|
|
660
|
-
const
|
|
660
|
+
const deriveNormalizedTopoRows = (
|
|
661
661
|
topo: Topo,
|
|
662
662
|
snapshotId: string
|
|
663
|
-
):
|
|
663
|
+
): NormalizedTopoFacts => {
|
|
664
664
|
const trails = topo.list().toSorted((a, b) => a.id.localeCompare(b.id));
|
|
665
665
|
const resources = topo
|
|
666
666
|
.listResources()
|
|
@@ -896,7 +896,7 @@ const addTrailRelations = (
|
|
|
896
896
|
...(activation.meta === undefined
|
|
897
897
|
? {}
|
|
898
898
|
: { meta: canonicalize(activation.meta) }),
|
|
899
|
-
source:
|
|
899
|
+
source: deriveActivationSource(activation.source),
|
|
900
900
|
...(activation.where === undefined
|
|
901
901
|
? {}
|
|
902
902
|
: { where: sortKeys({ predicate: true }) }),
|
|
@@ -1024,7 +1024,7 @@ const buildTrailEntryBase = (
|
|
|
1024
1024
|
} => {
|
|
1025
1025
|
const raw = trail as unknown as Record<string, unknown>;
|
|
1026
1026
|
const entry: Record<string, unknown> = {
|
|
1027
|
-
cli:
|
|
1027
|
+
cli: deriveTrailCliCommandRendering(trail, {
|
|
1028
1028
|
aliasSource: 'surface',
|
|
1029
1029
|
aliases: surfaceAliases?.[trail.id],
|
|
1030
1030
|
}),
|
|
@@ -1065,19 +1065,19 @@ const addVersioning = (
|
|
|
1065
1065
|
if (trail.version === undefined) {
|
|
1066
1066
|
return;
|
|
1067
1067
|
}
|
|
1068
|
-
const
|
|
1068
|
+
const derivation = deriveTrailVersions(
|
|
1069
1069
|
trail,
|
|
1070
1070
|
(schema) => sortedJsonSchema(schema as ZodSchemaInput).value
|
|
1071
1071
|
);
|
|
1072
|
-
if (
|
|
1072
|
+
if (derivation === undefined) {
|
|
1073
1073
|
return;
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
1076
|
-
entry['marker'] =
|
|
1077
|
-
entry['supports'] =
|
|
1078
|
-
entry['version'] =
|
|
1079
|
-
if (
|
|
1080
|
-
entry['versions'] =
|
|
1076
|
+
entry['marker'] = derivation.marker;
|
|
1077
|
+
entry['supports'] = derivation.supports;
|
|
1078
|
+
entry['version'] = derivation.version;
|
|
1079
|
+
if (derivation.versions !== undefined) {
|
|
1080
|
+
entry['versions'] = derivation.versions;
|
|
1081
1081
|
}
|
|
1082
1082
|
};
|
|
1083
1083
|
|
|
@@ -1306,7 +1306,7 @@ const buildTopoGraph = (
|
|
|
1306
1306
|
),
|
|
1307
1307
|
entries,
|
|
1308
1308
|
generatedAt,
|
|
1309
|
-
library:
|
|
1309
|
+
library: deriveTopoGraphLibrary(topo),
|
|
1310
1310
|
...(overlays === undefined ? {} : { overlays }),
|
|
1311
1311
|
topoGraphSchemaVersion: TOPO_GRAPH_SCHEMA_VERSION,
|
|
1312
1312
|
...(trailheads === undefined ? {} : { trailheads }),
|
|
@@ -1400,13 +1400,13 @@ const insertRows = <TRow>(
|
|
|
1400
1400
|
}
|
|
1401
1401
|
};
|
|
1402
1402
|
|
|
1403
|
-
const
|
|
1403
|
+
const insertDerivedRows = (
|
|
1404
1404
|
db: Database,
|
|
1405
|
-
|
|
1405
|
+
derivation: NormalizedTopoFacts
|
|
1406
1406
|
): void => {
|
|
1407
1407
|
insertRows(
|
|
1408
1408
|
db,
|
|
1409
|
-
|
|
1409
|
+
derivation.trails,
|
|
1410
1410
|
`INSERT INTO topo_trails (
|
|
1411
1411
|
id, intent, idempotent, has_output, has_examples, example_count, description, pattern, meta, snapshot_id
|
|
1412
1412
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -1425,54 +1425,54 @@ const insertProjectedRows = (
|
|
|
1425
1425
|
);
|
|
1426
1426
|
insertRows(
|
|
1427
1427
|
db,
|
|
1428
|
-
|
|
1428
|
+
derivation.composings,
|
|
1429
1429
|
'INSERT INTO topo_composings (source_id, target_id, snapshot_id) VALUES (?, ?, ?)',
|
|
1430
1430
|
(row) => [row.sourceId, row.targetId, row.snapshotId]
|
|
1431
1431
|
);
|
|
1432
1432
|
insertRows(
|
|
1433
1433
|
db,
|
|
1434
|
-
|
|
1434
|
+
derivation.trailResources,
|
|
1435
1435
|
`INSERT INTO topo_trail_resources (trail_id, resource_id, snapshot_id)
|
|
1436
1436
|
VALUES (?, ?, ?)`,
|
|
1437
1437
|
(row) => [row.trailId, row.resourceId, row.snapshotId]
|
|
1438
1438
|
);
|
|
1439
1439
|
insertRows(
|
|
1440
1440
|
db,
|
|
1441
|
-
|
|
1441
|
+
derivation.resources,
|
|
1442
1442
|
`INSERT INTO topo_resources (id, has_mock, has_health, snapshot_id)
|
|
1443
1443
|
VALUES (?, ?, ?, ?)`,
|
|
1444
1444
|
(row) => [row.id, row.hasMock, row.hasHealth, row.snapshotId]
|
|
1445
1445
|
);
|
|
1446
1446
|
insertRows(
|
|
1447
1447
|
db,
|
|
1448
|
-
|
|
1448
|
+
derivation.signals,
|
|
1449
1449
|
'INSERT INTO topo_signals (id, description, snapshot_id) VALUES (?, ?, ?)',
|
|
1450
1450
|
(row) => [row.id, row.description, row.snapshotId]
|
|
1451
1451
|
);
|
|
1452
1452
|
insertRows(
|
|
1453
1453
|
db,
|
|
1454
|
-
|
|
1454
|
+
derivation.trailSignals,
|
|
1455
1455
|
`INSERT INTO topo_trail_signals (trail_id, signal_id, snapshot_id)
|
|
1456
1456
|
VALUES (?, ?, ?)`,
|
|
1457
1457
|
(row) => [row.trailId, row.signalId, row.snapshotId]
|
|
1458
1458
|
);
|
|
1459
1459
|
insertRows(
|
|
1460
1460
|
db,
|
|
1461
|
-
|
|
1461
|
+
derivation.fires,
|
|
1462
1462
|
`INSERT INTO topo_trail_fires (trail_id, signal_id, snapshot_id)
|
|
1463
1463
|
VALUES (?, ?, ?)`,
|
|
1464
1464
|
(row) => [row.trailId, row.signalId, row.snapshotId]
|
|
1465
1465
|
);
|
|
1466
1466
|
insertRows(
|
|
1467
1467
|
db,
|
|
1468
|
-
|
|
1468
|
+
derivation.on,
|
|
1469
1469
|
`INSERT INTO topo_trail_on (trail_id, signal_id, snapshot_id)
|
|
1470
1470
|
VALUES (?, ?, ?)`,
|
|
1471
1471
|
(row) => [row.trailId, row.signalId, row.snapshotId]
|
|
1472
1472
|
);
|
|
1473
1473
|
insertRows(
|
|
1474
1474
|
db,
|
|
1475
|
-
|
|
1475
|
+
derivation.activationSources,
|
|
1476
1476
|
`INSERT INTO topo_activation_sources (
|
|
1477
1477
|
source_key, source_id, source_kind, source, snapshot_id
|
|
1478
1478
|
) VALUES (?, ?, ?, ?, ?)`,
|
|
@@ -1486,7 +1486,7 @@ const insertProjectedRows = (
|
|
|
1486
1486
|
);
|
|
1487
1487
|
insertRows(
|
|
1488
1488
|
db,
|
|
1489
|
-
|
|
1489
|
+
derivation.activationEdges,
|
|
1490
1490
|
`INSERT INTO topo_activation_edges (
|
|
1491
1491
|
source_key, source_id, source_kind, trail_id, has_where, edge, snapshot_id
|
|
1492
1492
|
) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -1502,7 +1502,7 @@ const insertProjectedRows = (
|
|
|
1502
1502
|
);
|
|
1503
1503
|
insertRows(
|
|
1504
1504
|
db,
|
|
1505
|
-
|
|
1505
|
+
derivation.surfaces,
|
|
1506
1506
|
`INSERT INTO topo_surfaces (trail_id, surface, derived_name, method, snapshot_id)
|
|
1507
1507
|
VALUES (?, ?, ?, ?, ?)`,
|
|
1508
1508
|
(row) => [
|
|
@@ -1515,7 +1515,7 @@ const insertProjectedRows = (
|
|
|
1515
1515
|
);
|
|
1516
1516
|
insertRows(
|
|
1517
1517
|
db,
|
|
1518
|
-
|
|
1518
|
+
derivation.examples,
|
|
1519
1519
|
`INSERT INTO topo_examples (
|
|
1520
1520
|
id, trail_id, ordinal, name, description, input, expected, expected_match, error, signals, snapshot_id
|
|
1521
1521
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -1641,7 +1641,7 @@ export const createTopoSnapshot = (
|
|
|
1641
1641
|
const snapshot = db.transaction(() => {
|
|
1642
1642
|
const record = insertTopoSnapshotRecord(db, snapshotInput);
|
|
1643
1643
|
const artifacts = buildStoredTopoExport(db, record, topo, snapshotInput);
|
|
1644
|
-
|
|
1644
|
+
insertDerivedRows(db, deriveNormalizedTopoRows(topo, record.id));
|
|
1645
1645
|
insertSchemaRows(db, artifacts.schemaRows);
|
|
1646
1646
|
insertStoredExport(db, artifacts.exportRow);
|
|
1647
1647
|
return record;
|
package/src/io.ts
CHANGED
|
@@ -222,7 +222,7 @@ export const writeTrailsLock = async (
|
|
|
222
222
|
/**
|
|
223
223
|
* Read a lock v4 manifest from `<dir>/trails.lock`.
|
|
224
224
|
*
|
|
225
|
-
* v5 root locks are
|
|
225
|
+
* v5 root locks are derived back to the v4 manifest shape for compatibility.
|
|
226
226
|
*/
|
|
227
227
|
export const readLockManifest = async (
|
|
228
228
|
options?: ReadOptions
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
TopoGraphLibraryCollision,
|
|
11
11
|
TopoGraphLibraryExclusion,
|
|
12
12
|
TopoGraphLibraryExport,
|
|
13
|
-
|
|
13
|
+
TopoGraphLibraryDerived,
|
|
14
14
|
} from './types.js';
|
|
15
15
|
|
|
16
16
|
const sortKeys = <T extends Record<string, unknown>>(obj: T): T => {
|
|
@@ -75,9 +75,7 @@ const collectLibraryExclusions = (
|
|
|
75
75
|
return excluded.toSorted((a, b) => a.trailId.localeCompare(b.trailId));
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
export const
|
|
79
|
-
topo: Topo
|
|
80
|
-
): TopoGraphLibraryProjection => {
|
|
78
|
+
export const deriveTopoGraphLibrary = (topo: Topo): TopoGraphLibraryDerived => {
|
|
81
79
|
const selected = filterSurfaceTrails([...topo.trails.values()]).filter(
|
|
82
80
|
(trail) => !isDraftId(trail.id)
|
|
83
81
|
);
|
|
@@ -129,5 +127,5 @@ export const collectLibraryProjection = (
|
|
|
129
127
|
collisions,
|
|
130
128
|
excluded: collectLibraryExclusions(topo, selectedIds),
|
|
131
129
|
exports,
|
|
132
|
-
}) as unknown as
|
|
130
|
+
}) as unknown as TopoGraphLibraryDerived;
|
|
133
131
|
};
|
package/src/permit.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { AnyTrail, PermitRequirement } from '@ontrails/core';
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type DerivedPermitRequirement =
|
|
4
4
|
| 'public'
|
|
5
5
|
| { readonly scopes: readonly string[] };
|
|
6
6
|
|
|
7
|
-
export const
|
|
7
|
+
export const derivePermitRequirement = (
|
|
8
8
|
permit: PermitRequirement
|
|
9
|
-
):
|
|
9
|
+
): DerivedPermitRequirement =>
|
|
10
10
|
permit === 'public' ? 'public' : { scopes: [...permit.scopes].toSorted() };
|
|
11
11
|
|
|
12
12
|
export const addPermitRequirement = (
|
|
@@ -14,6 +14,6 @@ export const addPermitRequirement = (
|
|
|
14
14
|
trail: AnyTrail
|
|
15
15
|
): void => {
|
|
16
16
|
if (trail.permit !== undefined) {
|
|
17
|
-
entry['permit'] =
|
|
17
|
+
entry['permit'] = derivePermitRequirement(trail.permit);
|
|
18
18
|
}
|
|
19
19
|
};
|
package/src/surface-bindings.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Both derivation pipelines — `deriveTopoGraph` and the store-side
|
|
5
5
|
* `buildTopoGraph` — resolve the app-authored `surfaces` overlay's `cli`
|
|
6
6
|
* bindings into per-trail CLI alias inputs and its `mcp` list bindings into
|
|
7
|
-
*
|
|
7
|
+
* derived trailhead entries through this single helper module, so compiled
|
|
8
8
|
* locks and fresh derivations cannot diverge from the runtime surfaces'
|
|
9
9
|
* reading of the same bindings.
|
|
10
10
|
*/
|
|
@@ -55,15 +55,15 @@ export const resolveCliAliasInputsFromOverlays = (
|
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* Resolve
|
|
58
|
+
* Resolve derived trailhead entries from overlay registrations.
|
|
59
59
|
*
|
|
60
60
|
* Finds the app-authored `surfaces` overlay among the registrations and
|
|
61
|
-
*
|
|
61
|
+
* derives each `mcp` list binding into one `TopoGraphTrailheadEntry`: the
|
|
62
62
|
* binding name becomes the trailhead id, the sorted expanded member trail
|
|
63
63
|
* ids become `memberIds`, the surfaces list is `['mcp']`, and the
|
|
64
64
|
* description is the deterministic derived default shared with the MCP
|
|
65
65
|
* surface. Scalar `mcp` bindings are tool synonyms, not grouped entries, so
|
|
66
|
-
* they
|
|
66
|
+
* they render no trailhead. Throws a `ValidationError` for group bindings
|
|
67
67
|
* whose member union is empty or synonym bindings that violate the shared
|
|
68
68
|
* expansion rules. Returns `undefined` when no `mcp` list bindings exist.
|
|
69
69
|
*
|
package/src/topo-store.ts
CHANGED
|
@@ -190,7 +190,7 @@ export type {
|
|
|
190
190
|
TopoStoreRef,
|
|
191
191
|
TopoStoreSignalDetailRecord,
|
|
192
192
|
TopoStoreSignalRecord,
|
|
193
|
-
|
|
193
|
+
TopoStoreSurfaceDerivedRecord,
|
|
194
194
|
TopoStoreTopoGraphEntryRecord,
|
|
195
195
|
TopoStoreTopoGraphRecord,
|
|
196
196
|
TopoStoreTrailDetailRecord,
|
package/src/types.ts
CHANGED
|
@@ -173,7 +173,7 @@ export interface TopoGraphLibraryCollision {
|
|
|
173
173
|
readonly trailIds: readonly string[];
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
export interface
|
|
176
|
+
export interface TopoGraphLibraryDerived {
|
|
177
177
|
readonly app: string;
|
|
178
178
|
readonly collisions: readonly TopoGraphLibraryCollision[];
|
|
179
179
|
readonly excluded: readonly TopoGraphLibraryExclusion[];
|
|
@@ -278,7 +278,7 @@ export interface TopoGraph {
|
|
|
278
278
|
readonly entries: readonly TopoGraphEntry[];
|
|
279
279
|
readonly trailheads?: readonly TopoGraphTrailheadEntry[] | undefined;
|
|
280
280
|
readonly forces?: readonly TopoGraphForceEntry[] | undefined;
|
|
281
|
-
readonly library?:
|
|
281
|
+
readonly library?: TopoGraphLibraryDerived | undefined;
|
|
282
282
|
/**
|
|
283
283
|
* Namespaced fact overlays contributed by registered overlay contributions
|
|
284
284
|
* (adapters). Unknown namespaces are preserved verbatim by every reader
|
|
@@ -406,7 +406,7 @@ const topoGraphLibraryCollisionSchema = z
|
|
|
406
406
|
})
|
|
407
407
|
.strict();
|
|
408
408
|
|
|
409
|
-
export const
|
|
409
|
+
export const topoGraphLibraryDerivedSchema = z
|
|
410
410
|
.object({
|
|
411
411
|
app: z.string(),
|
|
412
412
|
collisions: z.array(topoGraphLibraryCollisionSchema),
|
|
@@ -661,7 +661,7 @@ export const topoGraphSchema = z
|
|
|
661
661
|
entries: z.array(topoGraphEntrySchema),
|
|
662
662
|
forces: z.array(topoGraphForceEntrySchema).optional(),
|
|
663
663
|
generatedAt: z.string().optional(),
|
|
664
|
-
library:
|
|
664
|
+
library: topoGraphLibraryDerivedSchema.optional(),
|
|
665
665
|
overlays: z.record(z.string(), z.unknown()).optional(),
|
|
666
666
|
topoGraphSchemaVersion: z.literal(TOPO_GRAPH_SCHEMA_VERSION),
|
|
667
667
|
trailheads: z.array(topoGraphTrailheadEntrySchema).optional(),
|
package/src/versioning.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type {
|
|
|
23
23
|
TopoGraphVersionEntry,
|
|
24
24
|
} from './types.js';
|
|
25
25
|
|
|
26
|
-
type
|
|
26
|
+
type SchemaDeriver = (schema: unknown) => JsonSchema;
|
|
27
27
|
|
|
28
28
|
const sortPlainRecord = <T extends Record<string, unknown>>(value: T): T => {
|
|
29
29
|
const sorted: Record<string, unknown> = {};
|
|
@@ -33,7 +33,7 @@ const sortPlainRecord = <T extends Record<string, unknown>>(value: T): T => {
|
|
|
33
33
|
return sorted as T;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const
|
|
36
|
+
const deriveVersionDetours = (
|
|
37
37
|
entry: TrailVersionEntry
|
|
38
38
|
): TopoGraphVersionEntry['detours'] => {
|
|
39
39
|
const raw = entry as unknown as Record<string, unknown>;
|
|
@@ -57,7 +57,7 @@ const projectVersionDetours = (
|
|
|
57
57
|
});
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
const
|
|
60
|
+
const deriveVersionRuntimeRefs = (
|
|
61
61
|
entry: TrailVersionEntry,
|
|
62
62
|
field: 'composes' | 'resources'
|
|
63
63
|
): readonly string[] | undefined => {
|
|
@@ -85,46 +85,46 @@ const projectVersionRuntimeRefs = (
|
|
|
85
85
|
return refs.toSorted();
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
export const
|
|
88
|
+
export const deriveTrailVersionEntry = (
|
|
89
89
|
entry: TrailVersionEntry,
|
|
90
|
-
|
|
90
|
+
deriveSchema: SchemaDeriver
|
|
91
91
|
): TopoGraphVersionEntry => {
|
|
92
92
|
const kind = getTrailVersionEntryKind(entry);
|
|
93
93
|
const examples = deriveStructuredTrailExamples(entry.examples, {
|
|
94
94
|
provenance: { source: 'trail.versions.examples' },
|
|
95
95
|
});
|
|
96
|
-
const
|
|
96
|
+
const derived: Record<string, unknown> = {
|
|
97
97
|
exampleCount: entry.examples?.length ?? 0,
|
|
98
|
-
input:
|
|
98
|
+
input: deriveSchema(entry.input),
|
|
99
99
|
kind,
|
|
100
100
|
marker: deriveTrailVersionEntryMarker(entry),
|
|
101
|
-
output:
|
|
101
|
+
output: deriveSchema(entry.output),
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
if (examples !== undefined) {
|
|
105
|
-
|
|
105
|
+
derived['examples'] = examples;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (entry.status !== undefined) {
|
|
109
|
-
|
|
109
|
+
derived['status'] = sortPlainRecord({ ...entry.status });
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
if (kind === 'fork') {
|
|
113
|
-
const composes =
|
|
114
|
-
const resources =
|
|
115
|
-
const detours =
|
|
113
|
+
const composes = deriveVersionRuntimeRefs(entry, 'composes');
|
|
114
|
+
const resources = deriveVersionRuntimeRefs(entry, 'resources');
|
|
115
|
+
const detours = deriveVersionDetours(entry);
|
|
116
116
|
if (composes !== undefined) {
|
|
117
|
-
|
|
117
|
+
derived['composes'] = composes;
|
|
118
118
|
}
|
|
119
119
|
if (resources !== undefined) {
|
|
120
|
-
|
|
120
|
+
derived['resources'] = resources;
|
|
121
121
|
}
|
|
122
122
|
if (detours !== undefined) {
|
|
123
|
-
|
|
123
|
+
derived['detours'] = detours;
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
return sortPlainRecord(
|
|
127
|
+
return sortPlainRecord(derived) as unknown as TopoGraphVersionEntry;
|
|
128
128
|
};
|
|
129
129
|
|
|
130
130
|
export interface TopoGraphVersionMarkerRecord {
|
|
@@ -241,9 +241,9 @@ export const resolveTopoGraphVersionReference = (
|
|
|
241
241
|
};
|
|
242
242
|
};
|
|
243
243
|
|
|
244
|
-
export const
|
|
244
|
+
export const deriveTrailVersions = (
|
|
245
245
|
trail: AnyTrail,
|
|
246
|
-
|
|
246
|
+
deriveSchema: SchemaDeriver
|
|
247
247
|
):
|
|
248
248
|
| {
|
|
249
249
|
readonly marker: string;
|
|
@@ -256,14 +256,11 @@ export const projectTrailVersions = (
|
|
|
256
256
|
return undefined;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
-
const
|
|
259
|
+
const derivedEntries: Record<string, TopoGraphVersionEntry> = {};
|
|
260
260
|
for (const [rawVersion, entry] of Object.entries(
|
|
261
261
|
trail.versions ?? {}
|
|
262
262
|
).toSorted(([left], [right]) => Number(left) - Number(right))) {
|
|
263
|
-
|
|
264
|
-
entry,
|
|
265
|
-
projectSchema
|
|
266
|
-
);
|
|
263
|
+
derivedEntries[rawVersion] = deriveTrailVersionEntry(entry, deriveSchema);
|
|
267
264
|
}
|
|
268
265
|
|
|
269
266
|
const currentMarker = deriveCurrentTrailVersionMarker(trail);
|
|
@@ -273,8 +270,8 @@ export const projectTrailVersions = (
|
|
|
273
270
|
marker: currentMarker,
|
|
274
271
|
supports: deriveSupportedTrailVersions(trail),
|
|
275
272
|
version: trail.version,
|
|
276
|
-
...(Object.keys(
|
|
277
|
-
? { versions:
|
|
273
|
+
...(Object.keys(derivedEntries).length > 0
|
|
274
|
+
? { versions: derivedEntries }
|
|
278
275
|
: {}),
|
|
279
276
|
};
|
|
280
277
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
errorClasses,
|
|
3
|
-
|
|
3
|
+
renderErrorClassSurface,
|
|
4
4
|
surfaceNames,
|
|
5
5
|
} from '@ontrails/core';
|
|
6
6
|
import type {
|
|
7
7
|
ErrorCategory,
|
|
8
8
|
ErrorClassRegistryEntry,
|
|
9
|
-
|
|
9
|
+
ErrorClassSurfaceRendering,
|
|
10
10
|
SurfaceName,
|
|
11
11
|
} from '@ontrails/core';
|
|
12
12
|
import type {
|
|
@@ -35,7 +35,7 @@ export type TrailErrorFactsCompleteness =
|
|
|
35
35
|
readonly status: 'unknown';
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export interface
|
|
38
|
+
export interface TrailErrorTaxonomyFacts {
|
|
39
39
|
readonly category?: ErrorCategory | undefined;
|
|
40
40
|
readonly dynamicCategory?:
|
|
41
41
|
| {
|
|
@@ -45,7 +45,7 @@ export interface TrailErrorTaxonomyProjection {
|
|
|
45
45
|
readonly known: boolean;
|
|
46
46
|
readonly name: string;
|
|
47
47
|
readonly retryable?: boolean | undefined;
|
|
48
|
-
readonly surfaces: readonly
|
|
48
|
+
readonly surfaces: readonly ErrorClassSurfaceRendering[];
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export type TrailErrorFactProvenance =
|
|
@@ -78,7 +78,7 @@ export interface TrailErrorFact {
|
|
|
78
78
|
readonly completeness: TrailErrorFactsCompleteness;
|
|
79
79
|
readonly kind: TrailErrorFactKind;
|
|
80
80
|
readonly provenance: TrailErrorFactProvenance;
|
|
81
|
-
readonly taxonomy:
|
|
81
|
+
readonly taxonomy: TrailErrorTaxonomyFacts;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export interface TrailErrorFacts {
|
|
@@ -110,10 +110,10 @@ const errorClassByName: ReadonlyMap<string, ErrorClassRegistryEntry> = new Map(
|
|
|
110
110
|
errorClasses.map((entry) => [entry.name, entry])
|
|
111
111
|
);
|
|
112
112
|
|
|
113
|
-
const
|
|
113
|
+
const deriveTaxonomyFacts = (
|
|
114
114
|
errorName: string,
|
|
115
115
|
surfaces: readonly SurfaceName[]
|
|
116
|
-
):
|
|
116
|
+
): TrailErrorTaxonomyFacts => {
|
|
117
117
|
const registryEntry = errorClassByName.get(errorName);
|
|
118
118
|
if (registryEntry === undefined) {
|
|
119
119
|
return {
|
|
@@ -141,8 +141,8 @@ const taxonomyProjection = (
|
|
|
141
141
|
name: registryEntry.name,
|
|
142
142
|
retryable: registryEntry.retryable,
|
|
143
143
|
surfaces: surfaces.flatMap((surface) => {
|
|
144
|
-
const
|
|
145
|
-
return
|
|
144
|
+
const derived = renderErrorClassSurface(surface, errorName);
|
|
145
|
+
return derived === undefined ? [] : [derived];
|
|
146
146
|
}),
|
|
147
147
|
};
|
|
148
148
|
};
|
|
@@ -189,7 +189,7 @@ const documentedFacts = (
|
|
|
189
189
|
source: 'trail.examples',
|
|
190
190
|
trailId: entry.id,
|
|
191
191
|
},
|
|
192
|
-
taxonomy:
|
|
192
|
+
taxonomy: deriveTaxonomyFacts(example.error, surfaces),
|
|
193
193
|
},
|
|
194
194
|
];
|
|
195
195
|
});
|
|
@@ -207,7 +207,7 @@ const handledFacts = (
|
|
|
207
207
|
source: 'trail.detours',
|
|
208
208
|
trailId: entry.id,
|
|
209
209
|
},
|
|
210
|
-
taxonomy:
|
|
210
|
+
taxonomy: deriveTaxonomyFacts(detour.on, surfaces),
|
|
211
211
|
})
|
|
212
212
|
);
|
|
213
213
|
|
|
@@ -231,7 +231,7 @@ const versionDocumentedFacts = (
|
|
|
231
231
|
trailId,
|
|
232
232
|
version,
|
|
233
233
|
},
|
|
234
|
-
taxonomy:
|
|
234
|
+
taxonomy: deriveTaxonomyFacts(example.error, surfaces),
|
|
235
235
|
},
|
|
236
236
|
];
|
|
237
237
|
});
|
|
@@ -252,7 +252,7 @@ const versionHandledFacts = (
|
|
|
252
252
|
trailId,
|
|
253
253
|
version,
|
|
254
254
|
},
|
|
255
|
-
taxonomy:
|
|
255
|
+
taxonomy: deriveTaxonomyFacts(detour.on, surfaces),
|
|
256
256
|
})
|
|
257
257
|
);
|
|
258
258
|
|
|
@@ -285,7 +285,7 @@ const inputFact = (
|
|
|
285
285
|
trailId: input.trailId,
|
|
286
286
|
...(input.version === undefined ? {} : { version: input.version }),
|
|
287
287
|
},
|
|
288
|
-
taxonomy:
|
|
288
|
+
taxonomy: deriveTaxonomyFacts(input.errorName, surfaces),
|
|
289
289
|
});
|
|
290
290
|
|
|
291
291
|
const byTrail = (
|
package/src/wayfind/queries.ts
CHANGED
|
@@ -198,7 +198,7 @@ const cliRouteSchema = z.object({
|
|
|
198
198
|
target: z.string(),
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
-
const
|
|
201
|
+
const cliDerivedSchema = z
|
|
202
202
|
.object({
|
|
203
203
|
path: z.array(z.string()).readonly(),
|
|
204
204
|
routes: z.array(cliRouteSchema).readonly().optional(),
|
|
@@ -206,7 +206,7 @@ const cliProjectionSchema = z
|
|
|
206
206
|
.nullable();
|
|
207
207
|
|
|
208
208
|
const trailSummarySchema = entrySummarySchema.extend({
|
|
209
|
-
cli:
|
|
209
|
+
cli: cliDerivedSchema,
|
|
210
210
|
composes: z.array(z.string()).readonly(),
|
|
211
211
|
intent: z.enum(['destroy', 'read', 'write']),
|
|
212
212
|
kind: z.literal('trail'),
|
|
@@ -280,7 +280,7 @@ const errorFactsCompletenessSchema = z.discriminatedUnion('status', [
|
|
|
280
280
|
}),
|
|
281
281
|
]);
|
|
282
282
|
|
|
283
|
-
const
|
|
283
|
+
const errorSurfaceDerivedSchema = z.object({
|
|
284
284
|
category: z.enum(errorCategories),
|
|
285
285
|
code: z.number(),
|
|
286
286
|
name: z.string(),
|
|
@@ -288,7 +288,7 @@ const errorSurfaceProjectionSchema = z.object({
|
|
|
288
288
|
surface: z.enum(surfaceNames),
|
|
289
289
|
});
|
|
290
290
|
|
|
291
|
-
const
|
|
291
|
+
const errorTaxonomyDerivedSchema = z.object({
|
|
292
292
|
category: z.enum(errorCategories).optional(),
|
|
293
293
|
dynamicCategory: z
|
|
294
294
|
.object({
|
|
@@ -298,7 +298,7 @@ const errorTaxonomyProjectionSchema = z.object({
|
|
|
298
298
|
known: z.boolean(),
|
|
299
299
|
name: z.string(),
|
|
300
300
|
retryable: z.boolean().optional(),
|
|
301
|
-
surfaces: z.array(
|
|
301
|
+
surfaces: z.array(errorSurfaceDerivedSchema).readonly(),
|
|
302
302
|
});
|
|
303
303
|
|
|
304
304
|
const errorFactProvenanceSchema = z.object({
|
|
@@ -321,7 +321,7 @@ const errorFactSchema = z.object({
|
|
|
321
321
|
completeness: errorFactsCompletenessSchema,
|
|
322
322
|
kind: z.enum(['documented', 'handled', 'inferred', 'observed']),
|
|
323
323
|
provenance: errorFactProvenanceSchema,
|
|
324
|
-
taxonomy:
|
|
324
|
+
taxonomy: errorTaxonomyDerivedSchema,
|
|
325
325
|
});
|
|
326
326
|
|
|
327
327
|
const trailErrorFactsSchema = z.object({
|
|
@@ -1097,13 +1097,13 @@ const contractFor = (
|
|
|
1097
1097
|
const withGraph = async <TValue>(
|
|
1098
1098
|
input: SourceInput,
|
|
1099
1099
|
cwd: string | undefined,
|
|
1100
|
-
|
|
1100
|
+
derive: (loaded: LoadedWayfinderGraph) => TValue
|
|
1101
1101
|
): Promise<Result<TValue, TrailsError>> => {
|
|
1102
1102
|
const loaded = await loadGraph(input, cwd);
|
|
1103
1103
|
if (loaded.isErr()) {
|
|
1104
1104
|
return loaded;
|
|
1105
1105
|
}
|
|
1106
|
-
return Result.ok(
|
|
1106
|
+
return Result.ok(derive(loaded.value));
|
|
1107
1107
|
};
|
|
1108
1108
|
|
|
1109
1109
|
const notFound = (kind: string, id: string): NotFoundError =>
|
package/src/wayfind/relations.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const relationKindSchema = z.enum([
|
|
|
12
12
|
'trailhead-groups',
|
|
13
13
|
'fired-by',
|
|
14
14
|
'has-version',
|
|
15
|
-
'surface-
|
|
15
|
+
'surface-renders',
|
|
16
16
|
'used-by',
|
|
17
17
|
]);
|
|
18
18
|
|
|
@@ -142,7 +142,7 @@ export const relationEdges = (graph: TopoGraph): readonly RelationEdge[] => {
|
|
|
142
142
|
add(refFor(graph, signalId, 'signal'), relation, target);
|
|
143
143
|
}
|
|
144
144
|
for (const surfaceId of entry.surfaces) {
|
|
145
|
-
add(refFor(graph, surfaceId, 'surface'), 'surface-
|
|
145
|
+
add(refFor(graph, surfaceId, 'surface'), 'surface-renders', target);
|
|
146
146
|
}
|
|
147
147
|
if (entry.kind === 'trail' && entry.version !== undefined) {
|
|
148
148
|
add(target, 'has-version', {
|
|
@@ -174,7 +174,7 @@ export const relationEdges = (graph: TopoGraph): readonly RelationEdge[] => {
|
|
|
174
174
|
const memberRef = refFor(graph, memberId, 'trail');
|
|
175
175
|
add(trailheadRef, 'trailhead-groups', memberRef);
|
|
176
176
|
for (const surfaceRef of surfaceRefs) {
|
|
177
|
-
add(surfaceRef, 'surface-
|
|
177
|
+
add(surfaceRef, 'surface-renders', memberRef);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
}
|