@ontrails/topography 0.2.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.
@@ -0,0 +1,152 @@
1
+ export type WayfinderFactCategory =
2
+ | 'authored'
3
+ | 'derived'
4
+ | 'inferred'
5
+ | 'observed';
6
+
7
+ export type WayfinderArtifactKind = 'lockManifest' | 'topoGraph' | 'topoStore';
8
+
9
+ export interface WayfinderArtifactSource {
10
+ readonly kind: WayfinderArtifactKind;
11
+ readonly path?: string | undefined;
12
+ readonly schemaVersion?: number | undefined;
13
+ }
14
+
15
+ export interface WayfinderContractRef {
16
+ readonly id: string;
17
+ readonly kind:
18
+ | 'entity'
19
+ | 'trailhead'
20
+ | 'resource'
21
+ | 'signal'
22
+ | 'topo'
23
+ | 'trail';
24
+ readonly field?: string | undefined;
25
+ }
26
+
27
+ export interface WayfinderArtifactStatusFresh {
28
+ readonly status: 'fresh';
29
+ }
30
+
31
+ export interface WayfinderArtifactStatusMissing {
32
+ readonly artifacts: readonly WayfinderArtifactKind[];
33
+ readonly status: 'missing';
34
+ }
35
+
36
+ export type WayfinderStaleReason =
37
+ | {
38
+ readonly actual: string;
39
+ readonly expected: string;
40
+ readonly reason: 'lock-manifest-hash-mismatch';
41
+ }
42
+ | {
43
+ readonly actual: Readonly<Record<string, number>>;
44
+ readonly expected: Readonly<Record<string, number>>;
45
+ readonly reason: 'lock-manifest-summary-mismatch';
46
+ }
47
+ | {
48
+ readonly reason: 'lock-manifest-topo-artifact-missing';
49
+ }
50
+ | {
51
+ readonly actual: string;
52
+ readonly expected: string;
53
+ readonly reason: 'topo-store-hash-mismatch';
54
+ readonly snapshotId: string;
55
+ }
56
+ | {
57
+ readonly actual: string;
58
+ readonly expected: string;
59
+ readonly reason: 'topo-store-source-fingerprint-mismatch';
60
+ readonly snapshotId: string;
61
+ }
62
+ | {
63
+ readonly reason: 'topo-store-export-missing';
64
+ };
65
+
66
+ export interface WayfinderArtifactStatusStale {
67
+ readonly reasons: readonly WayfinderStaleReason[];
68
+ readonly status: 'stale';
69
+ }
70
+
71
+ export interface WayfinderArtifactStatusSchemaVersionDrift {
72
+ readonly artifact: WayfinderArtifactKind;
73
+ readonly message: string;
74
+ readonly status: 'schema-version-drift';
75
+ }
76
+
77
+ export type WayfinderArtifactStatus =
78
+ | WayfinderArtifactStatusFresh
79
+ | WayfinderArtifactStatusMissing
80
+ | WayfinderArtifactStatusStale
81
+ | WayfinderArtifactStatusSchemaVersionDrift;
82
+
83
+ export type WayfinderFreshnessFresh = WayfinderArtifactStatusFresh;
84
+ export type WayfinderFreshnessMissing = WayfinderArtifactStatusMissing;
85
+ export type WayfinderFreshnessStale = WayfinderArtifactStatusStale;
86
+ export type WayfinderFreshnessSchemaVersionDrift =
87
+ WayfinderArtifactStatusSchemaVersionDrift;
88
+ export type WayfinderFreshness = WayfinderArtifactStatus;
89
+
90
+ export type WayfinderFactDriftStatus = 'absent' | 'aligned' | 'drifted';
91
+
92
+ export interface WayfinderFactDrift {
93
+ readonly artifacts?: readonly WayfinderArtifactKind[] | undefined;
94
+ readonly reasons?: readonly WayfinderStaleReason[] | undefined;
95
+ readonly status: WayfinderFactDriftStatus;
96
+ }
97
+
98
+ export interface WayfinderFact<TValue> {
99
+ readonly artifactStatus: WayfinderArtifactStatus;
100
+ readonly category: WayfinderFactCategory;
101
+ readonly derivedFrom: WayfinderContractRef | null;
102
+ readonly drift: WayfinderFactDrift;
103
+ readonly source: WayfinderArtifactSource;
104
+ readonly value: TValue;
105
+ }
106
+
107
+ export type WayfinderFactInput<TValue> = Omit<
108
+ WayfinderFact<TValue>,
109
+ 'drift'
110
+ > & {
111
+ readonly drift?: WayfinderFactDrift | undefined;
112
+ };
113
+
114
+ export const wayfinderDriftFromArtifactStatus = (
115
+ artifactStatus: WayfinderArtifactStatus
116
+ ): WayfinderFactDrift => {
117
+ switch (artifactStatus.status) {
118
+ case 'fresh': {
119
+ return { status: 'aligned' };
120
+ }
121
+ case 'missing': {
122
+ return {
123
+ artifacts: artifactStatus.artifacts,
124
+ status: 'absent',
125
+ };
126
+ }
127
+ case 'schema-version-drift':
128
+ case 'stale': {
129
+ return {
130
+ ...(artifactStatus.status === 'stale'
131
+ ? { reasons: artifactStatus.reasons }
132
+ : {}),
133
+ status: 'drifted',
134
+ };
135
+ }
136
+ default: {
137
+ artifactStatus satisfies never;
138
+ return { status: 'drifted' };
139
+ }
140
+ }
141
+ };
142
+
143
+ export const wayfinderDriftFromFreshness = (
144
+ artifactStatus: WayfinderArtifactStatus
145
+ ): WayfinderFactDrift => wayfinderDriftFromArtifactStatus(artifactStatus);
146
+
147
+ export const wayfinderFact = <TValue>(
148
+ fact: WayfinderFactInput<TValue>
149
+ ): WayfinderFact<TValue> => ({
150
+ ...fact,
151
+ drift: fact.drift ?? wayfinderDriftFromArtifactStatus(fact.artifactStatus),
152
+ });