@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.
- package/CHANGELOG.md +866 -0
- package/README.md +228 -0
- package/package.json +40 -0
- package/src/activation-report.ts +385 -0
- package/src/backend-support.ts +11 -0
- package/src/derive.ts +690 -0
- package/src/diff.ts +1088 -0
- package/src/forces.ts +125 -0
- package/src/hash.ts +55 -0
- package/src/index.ts +273 -0
- package/src/internal/topo-snapshots.ts +682 -0
- package/src/internal/topo-store-read.ts +1102 -0
- package/src/internal/topo-store.ts +1651 -0
- package/src/io.ts +280 -0
- package/src/library-derivation.ts +131 -0
- package/src/overlays.ts +155 -0
- package/src/permit.ts +19 -0
- package/src/source-fingerprint.ts +103 -0
- package/src/surface-bindings.ts +101 -0
- package/src/topo-store.ts +716 -0
- package/src/types.ts +749 -0
- package/src/versioning.ts +277 -0
- package/src/wayfind/error-facts.ts +363 -0
- package/src/wayfind/filters.ts +430 -0
- package/src/wayfind/loader.ts +443 -0
- package/src/wayfind/navigation.ts +265 -0
- package/src/wayfind/provenance.ts +152 -0
- package/src/wayfind/queries.ts +1656 -0
- package/src/wayfind/relations.ts +344 -0
- package/src/workspace-configured-aliases.ts +221 -0
- package/src/workspace-lock-census.ts +306 -0
- package/src/workspace-view-types.ts +104 -0
- package/src/workspace-view.ts +486 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for topo graphs, diffing, and lock files.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
CliCommandRoute,
|
|
7
|
+
OverlayProvenance,
|
|
8
|
+
StructuredSignalExample,
|
|
9
|
+
StructuredTrailExample,
|
|
10
|
+
Topo,
|
|
11
|
+
TrailVersionStatus,
|
|
12
|
+
} from '@ontrails/core';
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
|
|
15
|
+
export const TOPO_GRAPH_SCHEMA_VERSION = 4;
|
|
16
|
+
export const LOCK_MANIFEST_SCHEMA_VERSION = 4;
|
|
17
|
+
export const TRAILS_LOCK_SCHEMA_VERSION = 5;
|
|
18
|
+
|
|
19
|
+
export type TopoGraphExample = StructuredSignalExample | StructuredTrailExample;
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// JSON Schema (lightweight alias)
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/** A JSON Schema object produced by zodToJsonSchema. */
|
|
26
|
+
export type JsonSchema = Readonly<Record<string, unknown>>;
|
|
27
|
+
|
|
28
|
+
export interface TopoGraphEntityReference {
|
|
29
|
+
readonly entity: string;
|
|
30
|
+
readonly field: string;
|
|
31
|
+
readonly identity: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type TopoGraphFieldOverrideKey =
|
|
35
|
+
| 'hint'
|
|
36
|
+
| 'label'
|
|
37
|
+
| 'message'
|
|
38
|
+
| 'options';
|
|
39
|
+
|
|
40
|
+
export interface TopoGraphFieldOverride {
|
|
41
|
+
readonly field: string;
|
|
42
|
+
readonly overrides: readonly TopoGraphFieldOverrideKey[];
|
|
43
|
+
readonly provenance: {
|
|
44
|
+
readonly source: 'trail.fields';
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface TopoGraphLayerReference {
|
|
49
|
+
readonly input?: JsonSchema | undefined;
|
|
50
|
+
readonly name: string;
|
|
51
|
+
readonly scope: 'topo' | 'trail';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TopoGraphActivationSource extends Readonly<
|
|
55
|
+
Record<string, unknown>
|
|
56
|
+
> {
|
|
57
|
+
readonly cron?: string | undefined;
|
|
58
|
+
readonly hasParse?: true | undefined;
|
|
59
|
+
readonly hasPayloadSchema?: true | undefined;
|
|
60
|
+
readonly hasVerify?: true | undefined;
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly input?: unknown;
|
|
63
|
+
readonly inputSchema?: JsonSchema | undefined;
|
|
64
|
+
readonly kind: string;
|
|
65
|
+
readonly key: string;
|
|
66
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
67
|
+
readonly method?: string | undefined;
|
|
68
|
+
readonly parseOutputSchema?: JsonSchema | undefined;
|
|
69
|
+
readonly path?: string | undefined;
|
|
70
|
+
readonly payloadSchema?: JsonSchema | undefined;
|
|
71
|
+
readonly timezone?: string | undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface TopoGraphActivationEdge extends Readonly<
|
|
75
|
+
Record<string, unknown>
|
|
76
|
+
> {
|
|
77
|
+
readonly hasWhere: boolean;
|
|
78
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
79
|
+
readonly sourceId: string;
|
|
80
|
+
readonly sourceKey: string;
|
|
81
|
+
readonly sourceKind: string;
|
|
82
|
+
readonly trailId: string;
|
|
83
|
+
readonly where?: { readonly predicate: true } | undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface TopoGraphActivationGraph {
|
|
87
|
+
readonly edgeCount: number;
|
|
88
|
+
readonly edges: readonly TopoGraphActivationEdge[];
|
|
89
|
+
readonly sourceCount: number;
|
|
90
|
+
readonly sourceKeys: readonly string[];
|
|
91
|
+
readonly trailIds: readonly string[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface TopoGraphActivationEntry {
|
|
95
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
96
|
+
readonly source: TopoGraphActivationSource;
|
|
97
|
+
readonly where?: { readonly predicate: true } | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface TopoGraphPermitRequirement {
|
|
101
|
+
readonly scopes: readonly string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface TopoGraphVersionDetour {
|
|
105
|
+
readonly maxAttempts: number;
|
|
106
|
+
readonly on: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface TopoGraphVersionEntry {
|
|
110
|
+
readonly exampleCount: number;
|
|
111
|
+
readonly examples?: readonly StructuredTrailExample[] | undefined;
|
|
112
|
+
readonly kind: 'revision' | 'fork';
|
|
113
|
+
readonly input: JsonSchema;
|
|
114
|
+
readonly marker: string;
|
|
115
|
+
readonly output: JsonSchema;
|
|
116
|
+
readonly status?: TrailVersionStatus | undefined;
|
|
117
|
+
readonly composes?: readonly string[] | undefined;
|
|
118
|
+
readonly detours?: readonly TopoGraphVersionDetour[] | undefined;
|
|
119
|
+
readonly resources?: readonly string[] | undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface TopoGraphForceEntry {
|
|
123
|
+
readonly acceptedAt: string;
|
|
124
|
+
readonly change: 'modified' | 'removed';
|
|
125
|
+
readonly detail: string;
|
|
126
|
+
readonly id: string;
|
|
127
|
+
readonly kind: 'entity' | 'trail' | 'signal' | 'resource';
|
|
128
|
+
readonly reason?: string | undefined;
|
|
129
|
+
readonly severity: 'breaking';
|
|
130
|
+
readonly source: 'trails compile --force';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface TopoGraphTrailheadEntry {
|
|
134
|
+
readonly id: string;
|
|
135
|
+
readonly description: string;
|
|
136
|
+
readonly memberIds: readonly string[];
|
|
137
|
+
readonly memberSetHash: string;
|
|
138
|
+
readonly surfaces: readonly string[];
|
|
139
|
+
readonly visibility?: 'public' | 'internal' | undefined;
|
|
140
|
+
readonly descriptionStableThrough?: string | undefined;
|
|
141
|
+
readonly visibilityWideningAccepted?: true | undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type TopoGraphLibraryExportSource =
|
|
145
|
+
| 'derived'
|
|
146
|
+
| 'trail-hint'
|
|
147
|
+
| 'package-config';
|
|
148
|
+
|
|
149
|
+
export interface TopoGraphLibraryExport {
|
|
150
|
+
readonly description?: string | undefined;
|
|
151
|
+
readonly exportName: string;
|
|
152
|
+
readonly input?: JsonSchema | undefined;
|
|
153
|
+
readonly intent: 'read' | 'write' | 'destroy';
|
|
154
|
+
readonly nameSource: TopoGraphLibraryExportSource;
|
|
155
|
+
readonly output?: JsonSchema | undefined;
|
|
156
|
+
readonly resources: readonly string[];
|
|
157
|
+
readonly trailId: string;
|
|
158
|
+
readonly version?: number | undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type TopoGraphLibraryExclusionReason =
|
|
162
|
+
| 'activation'
|
|
163
|
+
| 'draft'
|
|
164
|
+
| 'internal';
|
|
165
|
+
|
|
166
|
+
export interface TopoGraphLibraryExclusion {
|
|
167
|
+
readonly reason: TopoGraphLibraryExclusionReason;
|
|
168
|
+
readonly trailId: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface TopoGraphLibraryCollision {
|
|
172
|
+
readonly exportName: string;
|
|
173
|
+
readonly trailIds: readonly string[];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface TopoGraphLibraryDerived {
|
|
177
|
+
readonly app: string;
|
|
178
|
+
readonly collisions: readonly TopoGraphLibraryCollision[];
|
|
179
|
+
readonly excluded: readonly TopoGraphLibraryExclusion[];
|
|
180
|
+
readonly exports: readonly TopoGraphLibraryExport[];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// TopoGraph
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Namespaced fact overlays embedded on a topo graph, keyed by contribution
|
|
189
|
+
* namespace. Values are overlay-owned JSON-plain facts; readers that do not
|
|
190
|
+
* recognize a namespace preserve it verbatim.
|
|
191
|
+
*/
|
|
192
|
+
export type TopoGraphOverlays = Readonly<Record<string, unknown>>;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* A namespaced overlay contribution consumed by the topo graph derivers as
|
|
196
|
+
* data. Each registration owns one namespace and supplies the schema its
|
|
197
|
+
* derived facts must satisfy before they enter the graph.
|
|
198
|
+
*/
|
|
199
|
+
export interface TopoGraphOverlayRegistration {
|
|
200
|
+
/** Unique dotted-kebab namespace, e.g. "cloudflare" or "cloudflare.workers". */
|
|
201
|
+
readonly namespace: string;
|
|
202
|
+
/**
|
|
203
|
+
* Who authored the registration. Absent means adapter-derived. The
|
|
204
|
+
* reserved `surfaces` namespace requires `'app-authored'` provenance.
|
|
205
|
+
*/
|
|
206
|
+
readonly provenance?: OverlayProvenance | undefined;
|
|
207
|
+
/** Zod schema the derived facts must satisfy before they enter the graph. */
|
|
208
|
+
readonly schema: z.ZodType;
|
|
209
|
+
/** Derive the namespace's facts from the topo. Must be deterministic. */
|
|
210
|
+
readonly derive: (topo: Topo) => unknown;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface TopoGraphEntry {
|
|
214
|
+
readonly id: string;
|
|
215
|
+
readonly kind: 'entity' | 'trail' | 'signal' | 'resource';
|
|
216
|
+
readonly surfaces: readonly string[];
|
|
217
|
+
readonly cli?:
|
|
218
|
+
| {
|
|
219
|
+
readonly path: readonly string[];
|
|
220
|
+
readonly routes?: readonly CliCommandRoute[] | undefined;
|
|
221
|
+
}
|
|
222
|
+
| undefined;
|
|
223
|
+
readonly input?: JsonSchema | undefined;
|
|
224
|
+
readonly marker?: string | undefined;
|
|
225
|
+
readonly payload?: JsonSchema | undefined;
|
|
226
|
+
readonly output?: JsonSchema | undefined;
|
|
227
|
+
readonly version?: number | undefined;
|
|
228
|
+
readonly versions?:
|
|
229
|
+
| Readonly<Record<string, TopoGraphVersionEntry>>
|
|
230
|
+
| undefined;
|
|
231
|
+
readonly supports?: readonly number[] | undefined;
|
|
232
|
+
readonly forces?: readonly TopoGraphForceEntry[] | undefined;
|
|
233
|
+
readonly intent?: 'read' | 'write' | 'destroy' | undefined;
|
|
234
|
+
readonly idempotent?: boolean | undefined;
|
|
235
|
+
readonly dryRunCapable?: boolean | undefined;
|
|
236
|
+
readonly permit?: 'public' | TopoGraphPermitRequirement | undefined;
|
|
237
|
+
readonly pattern?: string | undefined;
|
|
238
|
+
readonly deprecated?: boolean | undefined;
|
|
239
|
+
readonly replacedBy?: string | undefined;
|
|
240
|
+
readonly activationSources?: readonly TopoGraphActivationEntry[] | undefined;
|
|
241
|
+
readonly composes?: readonly string[] | undefined;
|
|
242
|
+
readonly entities?: readonly string[] | undefined;
|
|
243
|
+
readonly schema?: JsonSchema | undefined;
|
|
244
|
+
readonly identity?: string | undefined;
|
|
245
|
+
readonly references?: readonly TopoGraphEntityReference[] | undefined;
|
|
246
|
+
readonly resources?: readonly string[] | undefined;
|
|
247
|
+
readonly fires?: readonly string[] | undefined;
|
|
248
|
+
readonly on?: readonly string[] | undefined;
|
|
249
|
+
readonly from?: readonly string[] | undefined;
|
|
250
|
+
readonly producers?: readonly string[] | undefined;
|
|
251
|
+
readonly consumers?: readonly string[] | undefined;
|
|
252
|
+
readonly diagnostics?: Readonly<Record<string, unknown>> | undefined;
|
|
253
|
+
readonly governance?: Readonly<Record<string, unknown>> | undefined;
|
|
254
|
+
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
255
|
+
readonly fieldOverrides?: readonly TopoGraphFieldOverride[] | undefined;
|
|
256
|
+
readonly layers?: readonly TopoGraphLayerReference[] | undefined;
|
|
257
|
+
readonly detours?:
|
|
258
|
+
| readonly { readonly on: string; readonly maxAttempts: number }[]
|
|
259
|
+
| undefined;
|
|
260
|
+
readonly healthcheck?: boolean | undefined;
|
|
261
|
+
readonly exampleCount: number;
|
|
262
|
+
readonly examples?: readonly TopoGraphExample[] | undefined;
|
|
263
|
+
readonly description?: string | undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface TopoGraph {
|
|
267
|
+
readonly topoGraphSchemaVersion: typeof TOPO_GRAPH_SCHEMA_VERSION;
|
|
268
|
+
readonly activationGraph: TopoGraphActivationGraph;
|
|
269
|
+
readonly activationSources: Readonly<
|
|
270
|
+
Record<string, TopoGraphActivationSource>
|
|
271
|
+
>;
|
|
272
|
+
/**
|
|
273
|
+
* Wallclock provenance for in-memory derivations only. The committed
|
|
274
|
+
* `trails.lock` omits it so recompiles are byte-identical; it is always
|
|
275
|
+
* excluded from the graph hash.
|
|
276
|
+
*/
|
|
277
|
+
readonly generatedAt?: string | undefined;
|
|
278
|
+
readonly entries: readonly TopoGraphEntry[];
|
|
279
|
+
readonly trailheads?: readonly TopoGraphTrailheadEntry[] | undefined;
|
|
280
|
+
readonly forces?: readonly TopoGraphForceEntry[] | undefined;
|
|
281
|
+
readonly library?: TopoGraphLibraryDerived | undefined;
|
|
282
|
+
/**
|
|
283
|
+
* Namespaced fact overlays contributed by registered overlay contributions
|
|
284
|
+
* (adapters). Unknown namespaces are preserved verbatim by every reader
|
|
285
|
+
* (tolerant reader) and covered by the canonical graph hash.
|
|
286
|
+
*/
|
|
287
|
+
readonly overlays?: TopoGraphOverlays | undefined;
|
|
288
|
+
readonly workspace?: WorkspaceTopoMetadata | undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export interface DeriveTopoGraphOptions {
|
|
292
|
+
readonly overlays?: readonly TopoGraphOverlayRegistration[] | undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
// Lock Manifest
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
/** Workspace-owned trail entry serialized into a workspace topo graph. */
|
|
300
|
+
export const workspaceTrailEntrySchema = z
|
|
301
|
+
.object({
|
|
302
|
+
appName: z.string(),
|
|
303
|
+
modulePath: z.string(),
|
|
304
|
+
trailId: z.string(),
|
|
305
|
+
})
|
|
306
|
+
.strict();
|
|
307
|
+
|
|
308
|
+
export type WorkspaceTrailEntry = z.infer<typeof workspaceTrailEntrySchema>;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Workspace-wide trail-id index serialized into a workspace topo graph.
|
|
312
|
+
*
|
|
313
|
+
* Each key is a fully-qualified trail identifier. Each value carries the app
|
|
314
|
+
* that owns it plus the app module path used by consumers that need to load
|
|
315
|
+
* the owning topo without re-walking workspace manifests.
|
|
316
|
+
*
|
|
317
|
+
* @see TopoGraph for the artifact envelope that may carry this index.
|
|
318
|
+
*/
|
|
319
|
+
export const workspaceTrailIndexSchema = z.record(
|
|
320
|
+
z.string(),
|
|
321
|
+
workspaceTrailEntrySchema
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
export type WorkspaceTrailIndex = z.infer<typeof workspaceTrailIndexSchema>;
|
|
325
|
+
|
|
326
|
+
/** A trail id exported by more than one app in a workspace topo graph. */
|
|
327
|
+
export const workspaceTrailCollisionSchema = z
|
|
328
|
+
.object({
|
|
329
|
+
apps: z.array(z.string()).min(2),
|
|
330
|
+
owners: z.array(workspaceTrailEntrySchema).min(2),
|
|
331
|
+
trailId: z.string(),
|
|
332
|
+
})
|
|
333
|
+
.strict();
|
|
334
|
+
|
|
335
|
+
export type WorkspaceTrailCollision = z.infer<
|
|
336
|
+
typeof workspaceTrailCollisionSchema
|
|
337
|
+
>;
|
|
338
|
+
|
|
339
|
+
/** Workspace metadata serialized into a workspace-scope TopoGraph. */
|
|
340
|
+
export const workspaceTopoMetadataSchema = z
|
|
341
|
+
.object({
|
|
342
|
+
collisions: z.array(workspaceTrailCollisionSchema).optional(),
|
|
343
|
+
trails: workspaceTrailIndexSchema,
|
|
344
|
+
})
|
|
345
|
+
.strict();
|
|
346
|
+
|
|
347
|
+
export type WorkspaceTopoMetadata = z.infer<typeof workspaceTopoMetadataSchema>;
|
|
348
|
+
|
|
349
|
+
const topoGraphForceEntrySchema = z
|
|
350
|
+
.object({
|
|
351
|
+
acceptedAt: z.string(),
|
|
352
|
+
change: z.enum(['modified', 'removed']),
|
|
353
|
+
detail: z.string(),
|
|
354
|
+
id: z.string(),
|
|
355
|
+
kind: z.enum(['entity', 'trail', 'signal', 'resource']),
|
|
356
|
+
reason: z.string().optional(),
|
|
357
|
+
severity: z.literal('breaking'),
|
|
358
|
+
source: z.literal('trails compile --force'),
|
|
359
|
+
})
|
|
360
|
+
.strict();
|
|
361
|
+
|
|
362
|
+
export const topoGraphTrailheadEntrySchema = z
|
|
363
|
+
.object({
|
|
364
|
+
description: z.string(),
|
|
365
|
+
descriptionStableThrough: z.string().optional(),
|
|
366
|
+
id: z.string(),
|
|
367
|
+
memberIds: z.array(z.string()),
|
|
368
|
+
memberSetHash: z.string().regex(/^[0-9a-f]{64}$/),
|
|
369
|
+
surfaces: z.array(z.string()),
|
|
370
|
+
visibility: z.enum(['public', 'internal']).optional(),
|
|
371
|
+
visibilityWideningAccepted: z.literal(true).optional(),
|
|
372
|
+
})
|
|
373
|
+
.strict();
|
|
374
|
+
|
|
375
|
+
const topoGraphLibraryExportSourceSchema = z.enum([
|
|
376
|
+
'derived',
|
|
377
|
+
'package-config',
|
|
378
|
+
'trail-hint',
|
|
379
|
+
]);
|
|
380
|
+
|
|
381
|
+
const topoGraphLibraryExportSchema = z
|
|
382
|
+
.object({
|
|
383
|
+
description: z.string().optional(),
|
|
384
|
+
exportName: z.string(),
|
|
385
|
+
input: z.record(z.string(), z.unknown()).optional(),
|
|
386
|
+
intent: z.enum(['destroy', 'read', 'write']),
|
|
387
|
+
nameSource: topoGraphLibraryExportSourceSchema,
|
|
388
|
+
output: z.record(z.string(), z.unknown()).optional(),
|
|
389
|
+
resources: z.array(z.string()),
|
|
390
|
+
trailId: z.string(),
|
|
391
|
+
version: z.number().int().positive().optional(),
|
|
392
|
+
})
|
|
393
|
+
.strict();
|
|
394
|
+
|
|
395
|
+
const topoGraphLibraryExclusionSchema = z
|
|
396
|
+
.object({
|
|
397
|
+
reason: z.enum(['activation', 'draft', 'internal']),
|
|
398
|
+
trailId: z.string(),
|
|
399
|
+
})
|
|
400
|
+
.strict();
|
|
401
|
+
|
|
402
|
+
const topoGraphLibraryCollisionSchema = z
|
|
403
|
+
.object({
|
|
404
|
+
exportName: z.string(),
|
|
405
|
+
trailIds: z.array(z.string()),
|
|
406
|
+
})
|
|
407
|
+
.strict();
|
|
408
|
+
|
|
409
|
+
export const topoGraphLibraryDerivedSchema = z
|
|
410
|
+
.object({
|
|
411
|
+
app: z.string(),
|
|
412
|
+
collisions: z.array(topoGraphLibraryCollisionSchema),
|
|
413
|
+
excluded: z.array(topoGraphLibraryExclusionSchema),
|
|
414
|
+
exports: z.array(topoGraphLibraryExportSchema),
|
|
415
|
+
})
|
|
416
|
+
.strict();
|
|
417
|
+
|
|
418
|
+
const topoGraphEntityReferenceSchema = z
|
|
419
|
+
.object({
|
|
420
|
+
entity: z.string(),
|
|
421
|
+
field: z.string(),
|
|
422
|
+
identity: z.string(),
|
|
423
|
+
})
|
|
424
|
+
.strict();
|
|
425
|
+
|
|
426
|
+
const openFactBagSchema = z.record(z.string(), z.unknown());
|
|
427
|
+
|
|
428
|
+
const topoGraphWhereSchema = z
|
|
429
|
+
.object({
|
|
430
|
+
predicate: z.literal(true),
|
|
431
|
+
})
|
|
432
|
+
.strict();
|
|
433
|
+
|
|
434
|
+
const topoGraphActivationSourceSchema = z
|
|
435
|
+
.object({
|
|
436
|
+
cron: z.string().optional(),
|
|
437
|
+
hasParse: z.literal(true).optional(),
|
|
438
|
+
hasPayloadSchema: z.literal(true).optional(),
|
|
439
|
+
hasVerify: z.literal(true).optional(),
|
|
440
|
+
id: z.string(),
|
|
441
|
+
input: z.unknown().optional(),
|
|
442
|
+
inputSchema: openFactBagSchema.optional(),
|
|
443
|
+
key: z.string(),
|
|
444
|
+
kind: z.string(),
|
|
445
|
+
meta: openFactBagSchema.optional(),
|
|
446
|
+
method: z.string().optional(),
|
|
447
|
+
parseOutputSchema: openFactBagSchema.optional(),
|
|
448
|
+
path: z.string().optional(),
|
|
449
|
+
payloadSchema: openFactBagSchema.optional(),
|
|
450
|
+
timezone: z.string().optional(),
|
|
451
|
+
})
|
|
452
|
+
.passthrough();
|
|
453
|
+
|
|
454
|
+
const topoGraphActivationEdgeSchema = z
|
|
455
|
+
.object({
|
|
456
|
+
hasWhere: z.boolean(),
|
|
457
|
+
meta: openFactBagSchema.optional(),
|
|
458
|
+
sourceId: z.string(),
|
|
459
|
+
sourceKey: z.string(),
|
|
460
|
+
sourceKind: z.string(),
|
|
461
|
+
trailId: z.string(),
|
|
462
|
+
where: topoGraphWhereSchema.optional(),
|
|
463
|
+
})
|
|
464
|
+
.passthrough();
|
|
465
|
+
|
|
466
|
+
const topoGraphActivationGraphSchema = z
|
|
467
|
+
.object({
|
|
468
|
+
edgeCount: z.number().int().nonnegative(),
|
|
469
|
+
edges: z.array(topoGraphActivationEdgeSchema),
|
|
470
|
+
sourceCount: z.number().int().nonnegative(),
|
|
471
|
+
sourceKeys: z.array(z.string()),
|
|
472
|
+
trailIds: z.array(z.string()),
|
|
473
|
+
})
|
|
474
|
+
.strict();
|
|
475
|
+
|
|
476
|
+
const topoGraphActivationEntrySchema = z
|
|
477
|
+
.object({
|
|
478
|
+
meta: openFactBagSchema.optional(),
|
|
479
|
+
source: topoGraphActivationSourceSchema,
|
|
480
|
+
where: topoGraphWhereSchema.optional(),
|
|
481
|
+
})
|
|
482
|
+
.strict();
|
|
483
|
+
|
|
484
|
+
const topoGraphCliRouteSchema = z
|
|
485
|
+
.object({
|
|
486
|
+
kind: z.enum(['alias', 'canonical']),
|
|
487
|
+
path: z.array(z.string()),
|
|
488
|
+
source: z.enum(['derived', 'surface', 'trail']),
|
|
489
|
+
target: z.string(),
|
|
490
|
+
})
|
|
491
|
+
.strict();
|
|
492
|
+
|
|
493
|
+
const topoGraphCliSchema = z
|
|
494
|
+
.object({
|
|
495
|
+
path: z.array(z.string()),
|
|
496
|
+
routes: z.array(topoGraphCliRouteSchema).optional(),
|
|
497
|
+
})
|
|
498
|
+
.strict();
|
|
499
|
+
|
|
500
|
+
const structuredTrailExampleSignalAssertionSchema = z
|
|
501
|
+
.object({
|
|
502
|
+
payload: z.unknown().optional(),
|
|
503
|
+
payloadMatch: z.unknown().optional(),
|
|
504
|
+
signalId: z.string(),
|
|
505
|
+
times: z.number().optional(),
|
|
506
|
+
})
|
|
507
|
+
.strict();
|
|
508
|
+
|
|
509
|
+
const structuredTrailExampleSchema = z
|
|
510
|
+
.object({
|
|
511
|
+
description: z.string().optional(),
|
|
512
|
+
error: z.string().optional(),
|
|
513
|
+
expected: z.unknown().optional(),
|
|
514
|
+
expectedMatch: z.unknown().optional(),
|
|
515
|
+
input: z.unknown(),
|
|
516
|
+
kind: z.enum(['error', 'success']),
|
|
517
|
+
name: z.string(),
|
|
518
|
+
provenance: z
|
|
519
|
+
.object({
|
|
520
|
+
source: z.enum(['trail.examples', 'trail.versions.examples']),
|
|
521
|
+
})
|
|
522
|
+
.strict(),
|
|
523
|
+
signals: z.array(structuredTrailExampleSignalAssertionSchema).optional(),
|
|
524
|
+
})
|
|
525
|
+
.strict();
|
|
526
|
+
|
|
527
|
+
const structuredSignalExampleSchema = z
|
|
528
|
+
.object({
|
|
529
|
+
kind: z.literal('payload'),
|
|
530
|
+
payload: z.unknown(),
|
|
531
|
+
provenance: z
|
|
532
|
+
.object({
|
|
533
|
+
source: z.literal('signal.examples'),
|
|
534
|
+
})
|
|
535
|
+
.strict(),
|
|
536
|
+
})
|
|
537
|
+
.strict();
|
|
538
|
+
|
|
539
|
+
const topoGraphExampleSchema = z.union([
|
|
540
|
+
structuredSignalExampleSchema,
|
|
541
|
+
structuredTrailExampleSchema,
|
|
542
|
+
]);
|
|
543
|
+
|
|
544
|
+
const trailVersionStatusSchema = z.discriminatedUnion('state', [
|
|
545
|
+
z
|
|
546
|
+
.object({
|
|
547
|
+
migration: z.array(z.string()).optional(),
|
|
548
|
+
note: z.string().optional(),
|
|
549
|
+
state: z.literal('deprecated'),
|
|
550
|
+
successor: z.number().int().positive().optional(),
|
|
551
|
+
})
|
|
552
|
+
.passthrough(),
|
|
553
|
+
z
|
|
554
|
+
.object({
|
|
555
|
+
reason: z.string().optional(),
|
|
556
|
+
state: z.literal('archived'),
|
|
557
|
+
})
|
|
558
|
+
.passthrough(),
|
|
559
|
+
]);
|
|
560
|
+
|
|
561
|
+
const topoGraphVersionDetourSchema = z
|
|
562
|
+
.object({
|
|
563
|
+
maxAttempts: z.number().int().positive(),
|
|
564
|
+
on: z.string(),
|
|
565
|
+
})
|
|
566
|
+
.strict();
|
|
567
|
+
|
|
568
|
+
const topoGraphVersionEntrySchema = z
|
|
569
|
+
.object({
|
|
570
|
+
composes: z.array(z.string()).optional(),
|
|
571
|
+
detours: z.array(topoGraphVersionDetourSchema).optional(),
|
|
572
|
+
exampleCount: z.number().int().nonnegative(),
|
|
573
|
+
examples: z.array(structuredTrailExampleSchema).optional(),
|
|
574
|
+
input: openFactBagSchema,
|
|
575
|
+
kind: z.enum(['fork', 'revision']),
|
|
576
|
+
marker: z.string(),
|
|
577
|
+
output: openFactBagSchema,
|
|
578
|
+
resources: z.array(z.string()).optional(),
|
|
579
|
+
status: trailVersionStatusSchema.optional(),
|
|
580
|
+
})
|
|
581
|
+
.strict();
|
|
582
|
+
|
|
583
|
+
const topoGraphPermitRequirementSchema = z
|
|
584
|
+
.object({
|
|
585
|
+
scopes: z.array(z.string()),
|
|
586
|
+
})
|
|
587
|
+
.strict();
|
|
588
|
+
|
|
589
|
+
const topoGraphFieldOverrideSchema = z
|
|
590
|
+
.object({
|
|
591
|
+
field: z.string(),
|
|
592
|
+
overrides: z.array(z.enum(['hint', 'label', 'message', 'options'])),
|
|
593
|
+
provenance: z
|
|
594
|
+
.object({
|
|
595
|
+
source: z.literal('trail.fields'),
|
|
596
|
+
})
|
|
597
|
+
.strict(),
|
|
598
|
+
})
|
|
599
|
+
.strict();
|
|
600
|
+
|
|
601
|
+
const topoGraphLayerReferenceSchema = z
|
|
602
|
+
.object({
|
|
603
|
+
input: openFactBagSchema.optional(),
|
|
604
|
+
name: z.string(),
|
|
605
|
+
scope: z.enum(['topo', 'trail']),
|
|
606
|
+
})
|
|
607
|
+
.strict();
|
|
608
|
+
|
|
609
|
+
const topoGraphEntrySchema = z
|
|
610
|
+
.object({
|
|
611
|
+
activationSources: z.array(topoGraphActivationEntrySchema).optional(),
|
|
612
|
+
cli: topoGraphCliSchema.optional(),
|
|
613
|
+
composes: z.array(z.string()).optional(),
|
|
614
|
+
consumers: z.array(z.string()).optional(),
|
|
615
|
+
deprecated: z.boolean().optional(),
|
|
616
|
+
description: z.string().optional(),
|
|
617
|
+
detours: z.array(topoGraphVersionDetourSchema).optional(),
|
|
618
|
+
diagnostics: openFactBagSchema.optional(),
|
|
619
|
+
dryRunCapable: z.boolean().optional(),
|
|
620
|
+
entities: z.array(z.string()).optional(),
|
|
621
|
+
exampleCount: z.number().int().nonnegative(),
|
|
622
|
+
examples: z.array(topoGraphExampleSchema).optional(),
|
|
623
|
+
fieldOverrides: z.array(topoGraphFieldOverrideSchema).optional(),
|
|
624
|
+
fires: z.array(z.string()).optional(),
|
|
625
|
+
forces: z.array(topoGraphForceEntrySchema).optional(),
|
|
626
|
+
from: z.array(z.string()).optional(),
|
|
627
|
+
governance: openFactBagSchema.optional(),
|
|
628
|
+
healthcheck: z.boolean().optional(),
|
|
629
|
+
id: z.string(),
|
|
630
|
+
idempotent: z.boolean().optional(),
|
|
631
|
+
identity: z.string().optional(),
|
|
632
|
+
input: openFactBagSchema.optional(),
|
|
633
|
+
intent: z.enum(['destroy', 'read', 'write']).optional(),
|
|
634
|
+
kind: z.enum(['entity', 'trail', 'signal', 'resource']),
|
|
635
|
+
layers: z.array(topoGraphLayerReferenceSchema).optional(),
|
|
636
|
+
marker: z.string().optional(),
|
|
637
|
+
meta: openFactBagSchema.optional(),
|
|
638
|
+
on: z.array(z.string()).optional(),
|
|
639
|
+
output: openFactBagSchema.optional(),
|
|
640
|
+
pattern: z.string().optional(),
|
|
641
|
+
payload: openFactBagSchema.optional(),
|
|
642
|
+
permit: z
|
|
643
|
+
.union([z.literal('public'), topoGraphPermitRequirementSchema])
|
|
644
|
+
.optional(),
|
|
645
|
+
producers: z.array(z.string()).optional(),
|
|
646
|
+
references: z.array(topoGraphEntityReferenceSchema).optional(),
|
|
647
|
+
replacedBy: z.string().optional(),
|
|
648
|
+
resources: z.array(z.string()).optional(),
|
|
649
|
+
schema: openFactBagSchema.optional(),
|
|
650
|
+
supports: z.array(z.number().int().positive()).optional(),
|
|
651
|
+
surfaces: z.array(z.string()),
|
|
652
|
+
version: z.number().int().positive().optional(),
|
|
653
|
+
versions: z.record(z.string(), topoGraphVersionEntrySchema).optional(),
|
|
654
|
+
})
|
|
655
|
+
.strict();
|
|
656
|
+
|
|
657
|
+
export const topoGraphSchema = z
|
|
658
|
+
.object({
|
|
659
|
+
activationGraph: topoGraphActivationGraphSchema,
|
|
660
|
+
activationSources: z.record(z.string(), topoGraphActivationSourceSchema),
|
|
661
|
+
entries: z.array(topoGraphEntrySchema),
|
|
662
|
+
forces: z.array(topoGraphForceEntrySchema).optional(),
|
|
663
|
+
generatedAt: z.string().optional(),
|
|
664
|
+
library: topoGraphLibraryDerivedSchema.optional(),
|
|
665
|
+
overlays: z.record(z.string(), z.unknown()).optional(),
|
|
666
|
+
topoGraphSchemaVersion: z.literal(TOPO_GRAPH_SCHEMA_VERSION),
|
|
667
|
+
trailheads: z.array(topoGraphTrailheadEntrySchema).optional(),
|
|
668
|
+
workspace: workspaceTopoMetadataSchema.optional(),
|
|
669
|
+
})
|
|
670
|
+
.strict();
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Lock v4 manifest artifact pointer.
|
|
674
|
+
*/
|
|
675
|
+
export const lockManifestArtifactSchema = z
|
|
676
|
+
.object({
|
|
677
|
+
path: z.string(),
|
|
678
|
+
role: z.literal('topo'),
|
|
679
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
680
|
+
})
|
|
681
|
+
.strict();
|
|
682
|
+
|
|
683
|
+
export const lockManifestSummarySchema = z
|
|
684
|
+
.object({
|
|
685
|
+
entities: z.number().int().nonnegative(),
|
|
686
|
+
resources: z.number().int().nonnegative(),
|
|
687
|
+
signals: z.number().int().nonnegative(),
|
|
688
|
+
trails: z.number().int().nonnegative(),
|
|
689
|
+
})
|
|
690
|
+
.strict();
|
|
691
|
+
|
|
692
|
+
export const lockManifestSchema = z
|
|
693
|
+
.object({
|
|
694
|
+
artifacts: z.array(lockManifestArtifactSchema).min(1),
|
|
695
|
+
scope: z.record(z.string(), z.string()),
|
|
696
|
+
summary: lockManifestSummarySchema,
|
|
697
|
+
version: z.literal(LOCK_MANIFEST_SCHEMA_VERSION),
|
|
698
|
+
})
|
|
699
|
+
.strict();
|
|
700
|
+
|
|
701
|
+
export type LockManifestArtifact = z.infer<typeof lockManifestArtifactSchema>;
|
|
702
|
+
export type LockManifestSummary = z.infer<typeof lockManifestSummarySchema>;
|
|
703
|
+
export type LockManifest = z.infer<typeof lockManifestSchema>;
|
|
704
|
+
|
|
705
|
+
export const trailsLockSchema = z
|
|
706
|
+
.object({
|
|
707
|
+
scope: z.record(z.string(), z.string()),
|
|
708
|
+
summary: lockManifestSummarySchema,
|
|
709
|
+
topoGraph: topoGraphSchema,
|
|
710
|
+
topoGraphHash: z.string().regex(/^[0-9a-f]{64}$/),
|
|
711
|
+
version: z.literal(TRAILS_LOCK_SCHEMA_VERSION),
|
|
712
|
+
})
|
|
713
|
+
.strict();
|
|
714
|
+
|
|
715
|
+
export type TrailsLock = z.infer<typeof trailsLockSchema>;
|
|
716
|
+
|
|
717
|
+
// ---------------------------------------------------------------------------
|
|
718
|
+
// Diff
|
|
719
|
+
// ---------------------------------------------------------------------------
|
|
720
|
+
|
|
721
|
+
export interface DiffEntry {
|
|
722
|
+
readonly id: string;
|
|
723
|
+
readonly kind: 'entity' | 'trail' | 'signal' | 'resource' | 'trailhead';
|
|
724
|
+
readonly change: 'added' | 'removed' | 'modified';
|
|
725
|
+
readonly severity: 'info' | 'warning' | 'breaking';
|
|
726
|
+
readonly details: readonly string[];
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
export interface DiffResult {
|
|
730
|
+
readonly entries: readonly DiffEntry[];
|
|
731
|
+
readonly breaking: readonly DiffEntry[];
|
|
732
|
+
readonly warnings: readonly DiffEntry[];
|
|
733
|
+
readonly info: readonly DiffEntry[];
|
|
734
|
+
readonly hasBreaking: boolean;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// ---------------------------------------------------------------------------
|
|
738
|
+
// I/O options
|
|
739
|
+
// ---------------------------------------------------------------------------
|
|
740
|
+
|
|
741
|
+
export interface WriteOptions {
|
|
742
|
+
/** Directory to write to. Defaults to ".trails/" */
|
|
743
|
+
readonly dir?: string | undefined;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export interface ReadOptions {
|
|
747
|
+
/** Directory to read from. Defaults to ".trails/" */
|
|
748
|
+
readonly dir?: string | undefined;
|
|
749
|
+
}
|