@ontrails/trails 1.0.0-beta.32 → 1.0.0-beta.39

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.
@@ -14,7 +14,8 @@ const releaseSmokeInputSchema = z.object({
14
14
  });
15
15
 
16
16
  const releaseSmokeCheckResultSchema = z.object({
17
- check: z.enum(['packed-artifacts', 'wayfinder-dogfood']),
17
+ check: z.enum(['lock-roundtrip', 'packed-artifacts', 'wayfinder-dogfood']),
18
+ lockCount: z.number().optional(),
18
19
  message: z.string(),
19
20
  packageCount: z.number().optional(),
20
21
  passed: z.literal(true),
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { basename, extname, join } from 'node:path';
9
9
 
10
- import type { CliCommandAliasInput, Topo } from '@ontrails/core';
10
+ import type { Topo } from '@ontrails/core';
11
11
  import {
12
12
  deriveSafePath,
13
13
  NotFoundError,
@@ -15,7 +15,12 @@ import {
15
15
  trail,
16
16
  ValidationError,
17
17
  } from '@ontrails/core';
18
- import type { DiffEntry, DiffResult, TopoGraph } from '@ontrails/topographer';
18
+ import type {
19
+ DiffEntry,
20
+ DiffResult,
21
+ TopoGraph,
22
+ TopoGraphOverlayRegistration,
23
+ } from '@ontrails/topographer';
19
24
  import {
20
25
  createTopoStore,
21
26
  deriveTopoGraphDiff,
@@ -506,13 +511,11 @@ const buildSurveyLookup = (
506
511
  app: Topo,
507
512
  entityId: string,
508
513
  rootDir: string,
509
- cliAliases:
510
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
511
- | undefined,
514
+ overlays: readonly TopoGraphOverlayRegistration[] | undefined,
512
515
  surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
513
516
  ): Result<object, Error> => {
514
517
  const matches = deriveCurrentTopoMatches(app, entityId, {
515
- cliAliases,
518
+ overlays,
516
519
  rootDir,
517
520
  surfaceLayerNames,
518
521
  });
@@ -523,13 +526,11 @@ const buildSurveyTrailDetail = (
523
526
  app: Topo,
524
527
  id: string,
525
528
  rootDir: string,
526
- cliAliases:
527
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
528
- | undefined,
529
+ overlays: readonly TopoGraphOverlayRegistration[] | undefined,
529
530
  surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
530
531
  ): Result<object, Error> => {
531
532
  const detail = deriveCurrentTrailDetail(app, id, {
532
- cliAliases,
533
+ overlays,
533
534
  rootDir,
534
535
  surfaceLayerNames,
535
536
  });
@@ -581,24 +582,16 @@ type SurveyHandler = (
581
582
  app: Topo,
582
583
  input: SurveyInput,
583
584
  rootDir: string,
584
- cliAliases:
585
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
586
- | undefined,
585
+ overlays: readonly TopoGraphOverlayRegistration[] | undefined,
587
586
  surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
588
587
  ) => Result<object, Error> | Promise<Result<object, Error>>;
589
588
 
590
589
  /** Handlers keyed by survey mode. */
591
590
  const surveyHandlers: Record<SurveyMode, SurveyHandler> = {
592
- lookup: (app, input, rootDir, cliAliases, surfaceLayerNames) =>
591
+ lookup: (app, input, rootDir, overlays, surfaceLayerNames) =>
593
592
  input.id === undefined || input.id === ''
594
593
  ? Result.err(new ValidationError('Survey lookup requires an id'))
595
- : buildSurveyLookup(
596
- app,
597
- input.id,
598
- rootDir,
599
- cliAliases,
600
- surfaceLayerNames
601
- ),
594
+ : buildSurveyLookup(app, input.id, rootDir, overlays, surfaceLayerNames),
602
595
  overview: (app, _input, rootDir) =>
603
596
  Result.ok(deriveCurrentTopoList(app, { rootDir })),
604
597
  };
@@ -613,9 +606,7 @@ const dispatchSurvey = async (
613
606
  app: Topo,
614
607
  input: SurveyInput,
615
608
  rootDir: string,
616
- cliAliases:
617
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
618
- | undefined,
609
+ overlays: readonly TopoGraphOverlayRegistration[] | undefined,
619
610
  surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
620
611
  ): Promise<Result<SurveyEnvelope, Error>> => {
621
612
  const mode = deriveSurveyMode(input);
@@ -624,7 +615,7 @@ const dispatchSurvey = async (
624
615
  app,
625
616
  input,
626
617
  rootDir,
627
- cliAliases,
618
+ overlays,
628
619
  surfaceLayerNames
629
620
  );
630
621
  if (result.isErr()) {
@@ -644,13 +635,11 @@ const withFreshSurveyApp = async <T>(
644
635
  rootDir: string,
645
636
  consume: (
646
637
  app: Topo,
647
- cliAliases:
648
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
649
- | undefined
638
+ overlays: readonly TopoGraphOverlayRegistration[] | undefined
650
639
  ) => Promise<Result<T, Error>> | Result<T, Error>
651
640
  ): Promise<Result<T, Error>> =>
652
641
  withFreshAppLease(input.module, rootDir, (lease) =>
653
- consume(lease.app, lease.cliAliases)
642
+ consume(lease.app, lease.overlays)
654
643
  );
655
644
 
656
645
  const withResolvedSurveyApp = async <T>(
@@ -662,14 +651,12 @@ const withResolvedSurveyApp = async <T>(
662
651
  consume: (
663
652
  app: Topo,
664
653
  rootDir: string,
665
- cliAliases:
666
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
667
- | undefined
654
+ overlays: readonly TopoGraphOverlayRegistration[] | undefined
668
655
  ) => Promise<Result<T, Error>> | Result<T, Error>
669
656
  ): Promise<Result<T, Error>> =>
670
657
  withOperatorRootDir(input, { cwd }, (rootDir) =>
671
- withFreshSurveyApp(input, rootDir, (app, cliAliases) =>
672
- consume(app, rootDir, cliAliases)
658
+ withFreshSurveyApp(input, rootDir, (app, overlays) =>
659
+ consume(app, rootDir, overlays)
673
660
  )
674
661
  );
675
662
 
@@ -743,12 +730,12 @@ const surveyMatchOutput = z.discriminatedUnion('kind', [
743
730
  export const surveyTrail = trail('survey', {
744
731
  args: ['id'],
745
732
  blaze: async (input, ctx) =>
746
- withResolvedSurveyApp(input, ctx.cwd, (app, rootDir, cliAliases) =>
733
+ withResolvedSurveyApp(input, ctx.cwd, (app, rootDir, overlays) =>
747
734
  dispatchSurvey(
748
735
  app,
749
736
  input,
750
737
  rootDir,
751
- cliAliases,
738
+ overlays,
752
739
  readSurfaceLayerNamesFromContext(ctx)
753
740
  )
754
741
  ),
@@ -904,12 +891,12 @@ export const surveyDiffTrail = trail('survey.diff', {
904
891
  export const surveyTrailDetailTrail = trail('survey.trail', {
905
892
  args: ['id'],
906
893
  blaze: async (input, ctx) =>
907
- withResolvedSurveyApp(input, ctx.cwd, (app, rootDir, cliAliases) =>
894
+ withResolvedSurveyApp(input, ctx.cwd, (app, rootDir, overlays) =>
908
895
  buildSurveyTrailDetail(
909
896
  app,
910
897
  input.id,
911
898
  rootDir,
912
- cliAliases,
899
+ overlays,
913
900
  readSurfaceLayerNamesFromContext(ctx)
914
901
  )
915
902
  ),
@@ -8,7 +8,7 @@
8
8
  import { existsSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
10
 
11
- import type { CliCommandAliasInput, Topo, TrailContext } from '@ontrails/core';
11
+ import type { Topo, TrailContext } from '@ontrails/core';
12
12
  import {
13
13
  ConflictError,
14
14
  deriveTrailsDbPath,
@@ -27,7 +27,11 @@ import {
27
27
  readTrailsLock,
28
28
  stripTopoGraphForces,
29
29
  } from '@ontrails/topographer';
30
- import type { LockManifest, TopoGraph } from '@ontrails/topographer';
30
+ import type {
31
+ LockManifest,
32
+ TopoGraph,
33
+ TopoGraphOverlayRegistration,
34
+ } from '@ontrails/topographer';
31
35
 
32
36
  import type {
33
37
  BriefReport,
@@ -72,10 +76,8 @@ export interface CurrentTopoMatch {
72
76
  }
73
77
 
74
78
  export interface CurrentTopoReadOptions {
75
- readonly cliAliases?:
76
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
77
- | undefined;
78
79
  readonly rootDir?: string | undefined;
80
+ readonly overlays?: readonly TopoGraphOverlayRegistration[] | undefined;
79
81
  readonly surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined;
80
82
  }
81
83
 
@@ -199,7 +201,9 @@ export const deriveCurrentTrailDetail = (
199
201
  ? undefined
200
202
  : deriveTrailDetail(trail, app, undefined, {
201
203
  surfaceLayerNames: options?.surfaceLayerNames,
202
- topoGraph: deriveTopoGraph(app, { cliAliases: options?.cliAliases }),
204
+ topoGraph: deriveTopoGraph(app, {
205
+ overlays: options?.overlays,
206
+ }),
203
207
  });
204
208
  };
205
209
 
@@ -238,7 +242,9 @@ export const deriveCurrentTopoMatches = (
238
242
  (activationGraph ??= deriveActivationGraph(app));
239
243
  let topoGraph: ReturnType<typeof deriveTopoGraph> | undefined;
240
244
  const getTopoGraph = (): ReturnType<typeof deriveTopoGraph> =>
241
- (topoGraph ??= deriveTopoGraph(app, { cliAliases: options?.cliAliases }));
245
+ (topoGraph ??= deriveTopoGraph(app, {
246
+ overlays: options?.overlays,
247
+ }));
242
248
 
243
249
  const trail = app.get(id);
244
250
  if (trail !== undefined) {
@@ -267,10 +273,8 @@ export const deriveCurrentTopoMatches = (
267
273
  export const validateCurrentTopo = async (
268
274
  app: Topo,
269
275
  options?: {
270
- readonly cliAliases?:
271
- | Readonly<Record<string, readonly CliCommandAliasInput[]>>
272
- | undefined;
273
276
  readonly rootDir?: string;
277
+ readonly overlays?: readonly TopoGraphOverlayRegistration[] | undefined;
274
278
  }
275
279
  ): Promise<Result<TopoValidateReport, Error>> => {
276
280
  const rootDir = deriveRootDir(options?.rootDir);
@@ -298,7 +302,7 @@ export const validateCurrentTopo = async (
298
302
  }
299
303
 
300
304
  const currentExport = deriveCurrentTopoExport(app, {
301
- cliAliases: options?.cliAliases,
305
+ overlays: options?.overlays,
302
306
  rootDir,
303
307
  });
304
308
  if (currentExport.isErr()) {
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { Database } from 'bun:sqlite';
9
9
 
10
- import type { CliCommandAliasInput, Topo } from '@ontrails/core';
10
+ import type { Topo } from '@ontrails/core';
11
11
  import {
12
12
  ConflictError,
13
13
  deriveTrailsDir,
@@ -19,6 +19,7 @@ import {
19
19
  import type {
20
20
  LockManifest,
21
21
  TopoGraph,
22
+ TopoGraphOverlayRegistration,
22
23
  TopoSnapshot,
23
24
  TrailsLock,
24
25
  } from '@ontrails/topographer';
@@ -26,6 +27,7 @@ import type { StoredTopoExport } from '@ontrails/topographer/backend-support';
26
27
  import {
27
28
  annotateTopoGraphForces,
28
29
  carryForwardTopoGraphForces,
30
+ deriveSourceFingerprint,
29
31
  deriveTopoGraphDiff,
30
32
  deriveTopoGraphHash,
31
33
  readTopoGraph,
@@ -45,21 +47,24 @@ import {
45
47
  readGitState,
46
48
  } from './topo-support.js';
47
49
 
48
- type CliAliasesOption = Readonly<
49
- Record<string, readonly CliCommandAliasInput[]>
50
- >;
50
+ type OverlaysOption = readonly TopoGraphOverlayRegistration[];
51
51
 
52
52
  const persistAndReadStoredExport = (
53
53
  app: Topo,
54
54
  db: ReturnType<typeof openWriteTrailsDb>,
55
55
  rootDir: string,
56
- options?: { readonly cliAliases?: CliAliasesOption | undefined } | undefined
56
+ options?:
57
+ | {
58
+ readonly overlays?: OverlaysOption | undefined;
59
+ }
60
+ | undefined
57
61
  ): Result<
58
62
  { snapshot: TopoSnapshot; storedExport: StoredTopoExport },
59
63
  Error
60
64
  > => {
61
65
  const snapshotResult = createStoredTopoSnapshot(db, app, {
62
- cliAliases: options?.cliAliases,
66
+ overlays: options?.overlays,
67
+ sourceFingerprint: deriveSourceFingerprint(rootDir),
63
68
  ...readGitState(rootDir),
64
69
  ...deriveTopoCounts(app),
65
70
  });
@@ -125,8 +130,8 @@ export const mapTopoExportError = (error: unknown): Error => {
125
130
  export const deriveCurrentTopoExport = (
126
131
  app: Topo,
127
132
  options?: {
128
- readonly cliAliases?: CliAliasesOption | undefined;
129
133
  readonly rootDir?: string;
134
+ readonly overlays?: OverlaysOption | undefined;
130
135
  }
131
136
  ): Result<StoredTopoExport, Error> => {
132
137
  const rootDir = deriveRootDir(options?.rootDir);
@@ -134,7 +139,7 @@ export const deriveCurrentTopoExport = (
134
139
 
135
140
  try {
136
141
  const projected = persistAndReadStoredExport(app, db, rootDir, {
137
- cliAliases: options?.cliAliases,
142
+ overlays: options?.overlays,
138
143
  });
139
144
  return projected.isErr()
140
145
  ? projected
@@ -219,6 +224,10 @@ const writeStoredExportArtifacts = async (
219
224
  const lockManifest = JSON.parse(
220
225
  storedExport.lockManifestJson
221
226
  ) as LockManifest;
227
+ // Omit wallclock provenance from the committed artifact so recompiling the
228
+ // same sources yields byte-identical lock files; the hash never covered it.
229
+ const { generatedAt: _generatedAt, ...committedTopoGraph } =
230
+ prepared.topoGraph;
222
231
  const lockPath = await writeTrailsLock(
223
232
  {
224
233
  scope: lockManifest.scope,
@@ -236,7 +245,7 @@ const writeStoredExportArtifacts = async (
236
245
  (entry) => entry.kind === 'trail'
237
246
  ).length,
238
247
  },
239
- topoGraph: prepared.topoGraph,
248
+ topoGraph: committedTopoGraph,
240
249
  topoGraphHash: prepared.hash,
241
250
  version: 4,
242
251
  } as TrailsLock,
@@ -256,9 +265,9 @@ const writeStoredExportArtifacts = async (
256
265
  export const exportCurrentTopo = async (
257
266
  app: Topo,
258
267
  options?: {
259
- readonly cliAliases?: CliAliasesOption | undefined;
260
268
  readonly force?: boolean | undefined;
261
269
  readonly rootDir?: string;
270
+ readonly overlays?: OverlaysOption | undefined;
262
271
  }
263
272
  ): Promise<Result<TopoExportReport, Error>> => {
264
273
  const rootDir = deriveRootDir(options?.rootDir);
@@ -266,7 +275,7 @@ export const exportCurrentTopo = async (
266
275
 
267
276
  try {
268
277
  const candidate = deriveCurrentTopoExport(app, {
269
- cliAliases: options?.cliAliases,
278
+ overlays: options?.overlays,
270
279
  rootDir,
271
280
  });
272
281
  if (candidate.isErr()) {
@@ -283,7 +292,7 @@ export const exportCurrentTopo = async (
283
292
 
284
293
  db = openWriteTrailsDb({ rootDir });
285
294
  const persisted = persistAndReadStoredExport(app, db, rootDir, {
286
- cliAliases: options?.cliAliases,
295
+ overlays: options?.overlays,
287
296
  });
288
297
  if (persisted.isErr()) {
289
298
  return persisted;
@@ -29,6 +29,7 @@ export const topoSnapshotOutput = z.object({
29
29
  pinnedAs: z.string().optional(),
30
30
  resourceCount: z.number(),
31
31
  signalCount: z.number(),
32
+ sourceFingerprint: z.string().optional(),
32
33
  trailCount: z.number(),
33
34
  });
34
35
 
@@ -8,7 +8,7 @@ export const validateTrail = trail('validate', {
8
8
  blaze: async (input, ctx) =>
9
9
  withFreshOperatorApp(input, ctx, ({ lease, rootDir }) =>
10
10
  validateCurrentTopo(lease.app, {
11
- cliAliases: lease.cliAliases,
11
+ overlays: lease.overlays,
12
12
  rootDir,
13
13
  })
14
14
  ),
@@ -40,7 +40,6 @@ const wayfindInputSchema = z
40
40
  .default(false)
41
41
  .describe('Render the describe view for the selected target'),
42
42
  errors: z.boolean().default(false).describe('Resolve trail error facts'),
43
- facets: z.boolean().default(false).describe('Resolve surface facet facts'),
44
43
  impact: z
45
44
  .boolean()
46
45
  .default(false)
@@ -66,6 +65,12 @@ const wayfindInputSchema = z
66
65
  .boolean()
67
66
  .default(false)
68
67
  .describe('Render the outline view for a source file target'),
68
+ overlay: z
69
+ .string()
70
+ .optional()
71
+ .describe(
72
+ 'Read a namespaced fact overlay from the saved graph (e.g. --overlay cloudflare)'
73
+ ),
69
74
  overview: z
70
75
  .boolean()
71
76
  .default(false)
@@ -81,6 +86,10 @@ const wayfindInputSchema = z
81
86
  .string()
82
87
  .optional()
83
88
  .describe('Graph entity ID or source file path to inspect'),
89
+ trailheads: z
90
+ .boolean()
91
+ .default(false)
92
+ .describe('Resolve surface trailhead facts'),
84
93
  trails: z.boolean().default(false).describe('Resolve trail facts'),
85
94
  view: wayfinderViewSchema
86
95
  .default('list')
@@ -134,7 +143,7 @@ const wayfindInputSchema = z
134
143
  (input.adapter === undefined &&
135
144
  !input.contours &&
136
145
  !input.errors &&
137
- !input.facets &&
146
+ !input.trailheads &&
138
147
  input.intent === undefined &&
139
148
  !input.resources &&
140
149
  !input.signals &&
@@ -155,7 +164,34 @@ const wayfindInputSchema = z
155
164
  '--include attaches facts to a target or filtered population from locked artifacts; impact/deps and live-source includes are not supported yet.',
156
165
  path: ['include'],
157
166
  }
158
- );
167
+ )
168
+ .refine(
169
+ (input) =>
170
+ input.overlay === undefined ||
171
+ (input.target === undefined &&
172
+ !input.deps &&
173
+ !input.impact &&
174
+ input.include.length === 0 &&
175
+ input.adapter === undefined &&
176
+ !input.contours &&
177
+ !input.errors &&
178
+ !input.trailheads &&
179
+ input.intent === undefined &&
180
+ !input.resources &&
181
+ !input.signals &&
182
+ !input.surfaces &&
183
+ !input.trails),
184
+ {
185
+ message:
186
+ 'The --overlay flag reads one lock overlay and cannot be combined with targets, population selectors, or includes.',
187
+ path: ['overlay'],
188
+ }
189
+ )
190
+ .refine((input) => input.overlay === undefined || input.source !== 'live', {
191
+ message:
192
+ '--overlay reads namespaced overlays from locked artifacts; live source does not carry lock overlays.',
193
+ path: ['overlay'],
194
+ });
159
195
 
160
196
  const wayfindComposeInputSchema = z
161
197
  .object({
@@ -200,7 +236,7 @@ const hasLiveTypedFilter = (input: WayfindInput): boolean =>
200
236
  input.adapter !== undefined ||
201
237
  input.contours ||
202
238
  input.errors ||
203
- input.facets ||
239
+ input.trailheads ||
204
240
  input.intent !== undefined ||
205
241
  input.resources ||
206
242
  input.signals ||
@@ -214,14 +250,14 @@ const populationFilters = (
214
250
  readonly kind?:
215
251
  | readonly (
216
252
  | 'contour'
217
- | 'facet'
253
+ | 'trailhead'
218
254
  | 'resource'
219
255
  | 'signal'
220
256
  | 'surface'
221
257
  | 'trail'
222
258
  )[]
223
259
  | 'contour'
224
- | 'facet'
260
+ | 'trailhead'
225
261
  | 'resource'
226
262
  | 'signal'
227
263
  | 'surface'
@@ -232,7 +268,7 @@ const populationFilters = (
232
268
  } => {
233
269
  const kinds = [
234
270
  ...(input.contours ? ['contour' as const] : []),
235
- ...(input.facets ? ['facet' as const] : []),
271
+ ...(input.trailheads ? ['trailhead' as const] : []),
236
272
  ...(input.resources ? ['resource' as const] : []),
237
273
  ...(input.signals ? ['signal' as const] : []),
238
274
  ...(input.surfaces ? ['surface' as const] : []),
@@ -713,9 +749,9 @@ const viewPopulation = async (
713
749
  view: 'list' as const,
714
750
  };
715
751
  }
716
- if (input.facets) {
752
+ if (input.trailheads) {
717
753
  return {
718
- result: ctx.compose('wayfind.facets', {
754
+ result: ctx.compose('wayfind.trailheads', {
719
755
  filters,
720
756
  limit: input.limit,
721
757
  ...sourceInput(input),
@@ -767,6 +803,17 @@ export const wayfindTrail = trail('wayfind.navigate', {
767
803
  );
768
804
  }
769
805
  }
806
+ if (input.overlay !== undefined) {
807
+ return envelopeFor(
808
+ await ctx.compose('wayfind.overlay', {
809
+ namespace: input.overlay,
810
+ ...sourceInput(input),
811
+ }),
812
+ input,
813
+ ctx,
814
+ 'list'
815
+ );
816
+ }
770
817
  const dispatched =
771
818
  (await viewRelation(input, ctx)) ??
772
819
  (await (input.target === undefined
@@ -791,7 +838,8 @@ export const wayfindTrail = trail('wayfind.navigate', {
791
838
  'wayfind.describe',
792
839
  'wayfind.errors',
793
840
  'wayfind.examples',
794
- 'wayfind.facets',
841
+ 'wayfind.overlay',
842
+ 'wayfind.trailheads',
795
843
  'wayfind.impact',
796
844
  'wayfind.nearby',
797
845
  'wayfind.outline',
@@ -842,13 +890,17 @@ export const wayfindTrail = trail('wayfind.navigate', {
842
890
  name: 'List surface facts',
843
891
  },
844
892
  {
845
- input: { facets: true },
846
- name: 'List facet facts',
893
+ input: { trailheads: true },
894
+ name: 'List trailhead facts',
847
895
  },
848
896
  {
849
897
  input: { overview: true },
850
898
  name: 'Show graph overview',
851
899
  },
900
+ {
901
+ input: { overlay: 'cloudflare' },
902
+ name: 'Read a namespaced lock overlay',
903
+ },
852
904
  {
853
905
  input: { include: ['examples'], target: 'wayfind.search' },
854
906
  name: 'Attach examples for a target',