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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/CHANGELOG.md +975 -0
  2. package/README.md +27 -0
  3. package/package.json +30 -7
  4. package/src/app.ts +125 -2
  5. package/src/cli.ts +345 -11
  6. package/src/completions.ts +240 -0
  7. package/src/lifecycle-source-io.ts +33 -0
  8. package/src/load-app-mirror.ts +202 -0
  9. package/src/local-state-io.ts +173 -0
  10. package/src/mcp-app.ts +40 -0
  11. package/src/mcp-options.ts +77 -0
  12. package/src/mcp.ts +8 -0
  13. package/src/project-writes.ts +377 -0
  14. package/src/regrade/config.ts +152 -0
  15. package/src/release/bindings.ts +39 -0
  16. package/src/release/check.ts +844 -0
  17. package/src/release/config.ts +63 -0
  18. package/src/release/contract-facts.ts +425 -0
  19. package/src/release/index.ts +125 -0
  20. package/src/release/native-bun-publish.ts +651 -0
  21. package/src/release/native-bun-registry.ts +754 -0
  22. package/src/release/pack-coherence.ts +455 -0
  23. package/src/release/packed-artifacts-smoke.ts +236 -0
  24. package/src/release/policy.ts +1684 -0
  25. package/src/release/semver.ts +104 -0
  26. package/src/release/smoke.ts +46 -0
  27. package/src/release/wayfinder-dogfood-smoke.ts +760 -0
  28. package/src/retired-topo-command.ts +36 -0
  29. package/src/run-adapter-check.ts +76 -0
  30. package/src/run-collision.ts +126 -0
  31. package/src/run-completions-install.ts +179 -0
  32. package/src/run-example.ts +149 -0
  33. package/src/run-examples.ts +148 -0
  34. package/src/run-quiet.ts +75 -0
  35. package/src/run-release-check.ts +74 -0
  36. package/src/run-schema.ts +41 -0
  37. package/src/run-trace.ts +273 -0
  38. package/src/run-warden.ts +39 -0
  39. package/src/run-watch.ts +432 -0
  40. package/src/run-wayfind-outline.ts +166 -0
  41. package/src/scaffold-version-sync.ts +183 -0
  42. package/src/scaffold-versions.generated.ts +12 -0
  43. package/src/trails/adapter-check.ts +244 -0
  44. package/src/trails/add-surface.ts +94 -40
  45. package/src/trails/add-trail.ts +79 -41
  46. package/src/trails/add-verify.ts +95 -25
  47. package/src/trails/compile.ts +59 -0
  48. package/src/trails/completions-complete.ts +165 -0
  49. package/src/trails/completions.ts +47 -0
  50. package/src/trails/create-adapter.ts +1024 -0
  51. package/src/trails/create-scaffold.ts +399 -104
  52. package/src/trails/create-versions.ts +62 -0
  53. package/src/trails/create.ts +186 -72
  54. package/src/trails/deprecate.ts +59 -0
  55. package/src/trails/dev-clean.ts +82 -0
  56. package/src/trails/dev-reset.ts +50 -0
  57. package/src/trails/dev-stats.ts +72 -0
  58. package/src/trails/dev-support.ts +360 -0
  59. package/src/trails/doctor.ts +77 -0
  60. package/src/trails/draft-promote.ts +949 -0
  61. package/src/trails/guide.ts +71 -69
  62. package/src/trails/load-app.ts +1142 -16
  63. package/src/trails/operator-context.ts +66 -0
  64. package/src/trails/project.ts +17 -3
  65. package/src/trails/regrade.ts +292 -0
  66. package/src/trails/release-check.ts +105 -0
  67. package/src/trails/release-smoke.ts +48 -0
  68. package/src/trails/revise.ts +53 -0
  69. package/src/trails/root-dir.ts +21 -0
  70. package/src/trails/run-example.ts +475 -0
  71. package/src/trails/run-examples.ts +129 -0
  72. package/src/trails/run.ts +434 -0
  73. package/src/trails/scaffold-json.ts +58 -0
  74. package/src/trails/survey.ts +896 -227
  75. package/src/trails/topo-activation.ts +14 -0
  76. package/src/trails/topo-constants.ts +2 -0
  77. package/src/trails/topo-history.ts +47 -0
  78. package/src/trails/topo-output-schemas.ts +259 -0
  79. package/src/trails/topo-pin.ts +38 -0
  80. package/src/trails/topo-read-support.ts +363 -0
  81. package/src/trails/topo-reports.ts +809 -0
  82. package/src/trails/topo-store-support.ts +307 -0
  83. package/src/trails/topo-support.ts +219 -0
  84. package/src/trails/topo-unpin.ts +61 -0
  85. package/src/trails/topo.ts +92 -0
  86. package/src/trails/validate.ts +27 -0
  87. package/src/trails/version-lifecycle-support.ts +935 -0
  88. package/src/trails/warden-guide.ts +134 -0
  89. package/src/trails/warden.ts +198 -58
  90. package/src/trails/wayfind.ts +1001 -0
  91. package/src/versions.ts +31 -0
  92. package/.turbo/turbo-build.log +0 -1
  93. package/.turbo/turbo-lint.log +0 -3
  94. package/.turbo/turbo-typecheck.log +0 -1
  95. package/__tests__/examples.test.ts +0 -6
  96. package/dist/bin/trails.d.ts +0 -3
  97. package/dist/bin/trails.d.ts.map +0 -1
  98. package/dist/bin/trails.js +0 -4
  99. package/dist/bin/trails.js.map +0 -1
  100. package/dist/src/app.d.ts +0 -2
  101. package/dist/src/app.d.ts.map +0 -1
  102. package/dist/src/app.js +0 -11
  103. package/dist/src/app.js.map +0 -1
  104. package/dist/src/clack.d.ts +0 -9
  105. package/dist/src/clack.d.ts.map +0 -1
  106. package/dist/src/clack.js +0 -84
  107. package/dist/src/clack.js.map +0 -1
  108. package/dist/src/cli.d.ts +0 -2
  109. package/dist/src/cli.d.ts.map +0 -1
  110. package/dist/src/cli.js +0 -13
  111. package/dist/src/cli.js.map +0 -1
  112. package/dist/src/trails/add-surface.d.ts +0 -13
  113. package/dist/src/trails/add-surface.d.ts.map +0 -1
  114. package/dist/src/trails/add-surface.js +0 -88
  115. package/dist/src/trails/add-surface.js.map +0 -1
  116. package/dist/src/trails/add-trail.d.ts +0 -11
  117. package/dist/src/trails/add-trail.d.ts.map +0 -1
  118. package/dist/src/trails/add-trail.js +0 -85
  119. package/dist/src/trails/add-trail.js.map +0 -1
  120. package/dist/src/trails/add-verify.d.ts +0 -10
  121. package/dist/src/trails/add-verify.d.ts.map +0 -1
  122. package/dist/src/trails/add-verify.js +0 -67
  123. package/dist/src/trails/add-verify.js.map +0 -1
  124. package/dist/src/trails/create-scaffold.d.ts +0 -15
  125. package/dist/src/trails/create-scaffold.d.ts.map +0 -1
  126. package/dist/src/trails/create-scaffold.js +0 -288
  127. package/dist/src/trails/create-scaffold.js.map +0 -1
  128. package/dist/src/trails/create.d.ts +0 -22
  129. package/dist/src/trails/create.d.ts.map +0 -1
  130. package/dist/src/trails/create.js +0 -121
  131. package/dist/src/trails/create.js.map +0 -1
  132. package/dist/src/trails/guide.d.ts +0 -11
  133. package/dist/src/trails/guide.d.ts.map +0 -1
  134. package/dist/src/trails/guide.js +0 -80
  135. package/dist/src/trails/guide.js.map +0 -1
  136. package/dist/src/trails/load-app.d.ts +0 -4
  137. package/dist/src/trails/load-app.d.ts.map +0 -1
  138. package/dist/src/trails/load-app.js +0 -24
  139. package/dist/src/trails/load-app.js.map +0 -1
  140. package/dist/src/trails/project.d.ts +0 -8
  141. package/dist/src/trails/project.d.ts.map +0 -1
  142. package/dist/src/trails/project.js +0 -43
  143. package/dist/src/trails/project.js.map +0 -1
  144. package/dist/src/trails/survey.d.ts +0 -33
  145. package/dist/src/trails/survey.d.ts.map +0 -1
  146. package/dist/src/trails/survey.js +0 -225
  147. package/dist/src/trails/survey.js.map +0 -1
  148. package/dist/src/trails/warden.d.ts +0 -19
  149. package/dist/src/trails/warden.d.ts.map +0 -1
  150. package/dist/src/trails/warden.js +0 -88
  151. package/dist/src/trails/warden.js.map +0 -1
  152. package/dist/tsconfig.tsbuildinfo +0 -1
  153. package/src/__tests__/create.test.ts +0 -349
  154. package/src/__tests__/guide.test.ts +0 -91
  155. package/src/__tests__/load-app.test.ts +0 -15
  156. package/src/__tests__/survey.test.ts +0 -161
  157. package/src/__tests__/warden.test.ts +0 -74
  158. package/tsconfig.json +0 -9
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Read-only live topo consumer helpers.
3
+ *
4
+ * Extracted from topo-support.ts to isolate read-only topo consumer helpers,
5
+ * keeping module boundaries clean.
6
+ */
7
+
8
+ import { existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+
11
+ import type { CliCommandAliasInput, Topo, TrailContext } from '@ontrails/core';
12
+ import {
13
+ ConflictError,
14
+ deriveTrailsDbPath,
15
+ deriveTrailsDir,
16
+ NotFoundError,
17
+ Result,
18
+ SURFACE_LAYER_NAMES_KEY,
19
+ ValidationError,
20
+ } from '@ontrails/core';
21
+ import {
22
+ deriveTopoGraph,
23
+ deriveTopoGraphDiff,
24
+ deriveTopoGraphHash,
25
+ readLockManifest,
26
+ readTopoGraph,
27
+ readTrailsLock,
28
+ stripTopoGraphForces,
29
+ } from '@ontrails/topographer';
30
+ import type { LockManifest, TopoGraph } from '@ontrails/topographer';
31
+
32
+ import type {
33
+ BriefReport,
34
+ SignalDetailReport,
35
+ SurfaceLayerNames,
36
+ SurveyListReport,
37
+ TrailDetailReport,
38
+ } from './topo-reports.js';
39
+ import {
40
+ countTrailExamples,
41
+ deriveBriefReport,
42
+ deriveResourceDetail,
43
+ deriveSignalDetail,
44
+ deriveSurveyList,
45
+ deriveTrailDetail,
46
+ } from './topo-reports.js';
47
+ import type { ActivationGraphReport } from './topo-activation.js';
48
+ import { deriveActivationGraph } from './topo-activation.js';
49
+ import type { TopoSummaryReport, TopoValidateReport } from './topo-support.js';
50
+ import { deriveRootDir, LOCK_PATH } from './topo-support.js';
51
+ import { deriveCurrentTopoExport } from './topo-store-support.js';
52
+
53
+ export type CurrentTrailDetail = TrailDetailReport;
54
+
55
+ export interface CurrentResourceDetail {
56
+ readonly description: string | null;
57
+ readonly health: 'available' | 'none';
58
+ readonly id: string;
59
+ readonly kind: 'resource';
60
+ readonly lifetime: 'singleton';
61
+ readonly usedBy: readonly string[];
62
+ }
63
+
64
+ export type CurrentTopoDetail =
65
+ | CurrentResourceDetail
66
+ | CurrentTrailDetail
67
+ | SignalDetailReport;
68
+
69
+ export interface CurrentTopoMatch {
70
+ readonly kind: CurrentTopoDetail['kind'];
71
+ readonly detail: CurrentTopoDetail;
72
+ }
73
+
74
+ export interface CurrentTopoReadOptions {
75
+ readonly cliAliases?:
76
+ | Readonly<Record<string, readonly CliCommandAliasInput[]>>
77
+ | undefined;
78
+ readonly rootDir?: string | undefined;
79
+ readonly surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined;
80
+ }
81
+
82
+ const isStringArray = (value: unknown): value is readonly string[] =>
83
+ Array.isArray(value) && value.every((item) => typeof item === 'string');
84
+
85
+ export const readSurfaceLayerNamesFromContext = (
86
+ ctx: TrailContext
87
+ ): Partial<SurfaceLayerNames> => {
88
+ const value = ctx.extensions?.[SURFACE_LAYER_NAMES_KEY];
89
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
90
+ return {};
91
+ }
92
+ const raw = value as Record<string, unknown>;
93
+ return {
94
+ ...(isStringArray(raw['cli']) ? { cli: raw['cli'] } : {}),
95
+ ...(isStringArray(raw['http']) ? { http: raw['http'] } : {}),
96
+ ...(isStringArray(raw['mcp']) ? { mcp: raw['mcp'] } : {}),
97
+ };
98
+ };
99
+
100
+ const hasCommittedLock = (trailsDir: string): boolean =>
101
+ existsSync(join(trailsDir, 'trails.lock'));
102
+
103
+ const readCommittedLock = async (
104
+ rootDir: string
105
+ ): Promise<{
106
+ readonly lockManifest: LockManifest;
107
+ readonly topoGraph: TopoGraph;
108
+ } | null> => {
109
+ const trailsLock = await readTrailsLock({ dir: rootDir });
110
+ if (trailsLock !== null) {
111
+ return {
112
+ lockManifest: {
113
+ artifacts: [
114
+ {
115
+ path: 'topo.lock',
116
+ role: 'topo',
117
+ sha256: trailsLock.topoGraphHash,
118
+ },
119
+ ],
120
+ scope: trailsLock.scope,
121
+ summary: trailsLock.summary,
122
+ version: 3,
123
+ },
124
+ topoGraph: trailsLock.topoGraph as TopoGraph,
125
+ };
126
+ }
127
+
128
+ const legacyDir = deriveTrailsDir({ rootDir });
129
+ const legacyManifest = await readLockManifest({ dir: legacyDir });
130
+ if (legacyManifest === null) {
131
+ return null;
132
+ }
133
+ const legacyTopoGraph = await readTopoGraph({ dir: legacyDir });
134
+ if (legacyTopoGraph === null) {
135
+ return null;
136
+ }
137
+ return {
138
+ lockManifest: legacyManifest,
139
+ topoGraph: legacyTopoGraph,
140
+ };
141
+ };
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Public read-only consumers
145
+ // ---------------------------------------------------------------------------
146
+
147
+ export const deriveTopoSummary = (
148
+ app: Topo,
149
+ options?: { readonly rootDir?: string }
150
+ ): TopoSummaryReport => {
151
+ const rootDir = deriveRootDir(options?.rootDir);
152
+ return {
153
+ app: deriveBriefReport(app),
154
+ dbPath: deriveTrailsDbPath({ rootDir }),
155
+ list: deriveSurveyList(app),
156
+ lockExists:
157
+ hasCommittedLock(rootDir) ||
158
+ hasCommittedLock(deriveTrailsDir({ rootDir })),
159
+ lockPath: LOCK_PATH,
160
+ };
161
+ };
162
+
163
+ export const deriveCurrentTopoBrief = (
164
+ app: Topo,
165
+ _options?: { readonly rootDir?: string }
166
+ ): BriefReport => deriveBriefReport(app);
167
+
168
+ export const deriveCurrentTopoList = (
169
+ app: Topo,
170
+ _options?: { readonly rootDir?: string }
171
+ ): SurveyListReport => deriveSurveyList(app);
172
+
173
+ export const deriveCurrentGuideEntries = (
174
+ app: Topo,
175
+ _options?: { readonly rootDir?: string }
176
+ ): readonly {
177
+ readonly description: string;
178
+ readonly exampleCount: number;
179
+ readonly id: string;
180
+ readonly kind: 'trail';
181
+ }[] =>
182
+ app
183
+ .list()
184
+ .map((trail) => ({
185
+ description: trail.description ?? '(no description)',
186
+ exampleCount: countTrailExamples(trail),
187
+ id: trail.id,
188
+ kind: 'trail' as const,
189
+ }))
190
+ .toSorted((a, b) => a.id.localeCompare(b.id));
191
+
192
+ export const deriveCurrentTrailDetail = (
193
+ app: Topo,
194
+ id: string,
195
+ options?: CurrentTopoReadOptions
196
+ ): CurrentTrailDetail | undefined => {
197
+ const trail = app.get(id);
198
+ return trail === undefined
199
+ ? undefined
200
+ : deriveTrailDetail(trail, app, undefined, {
201
+ surfaceLayerNames: options?.surfaceLayerNames,
202
+ topoGraph: deriveTopoGraph(app, { cliAliases: options?.cliAliases }),
203
+ });
204
+ };
205
+
206
+ export const deriveCurrentResourceDetail = (
207
+ app: Topo,
208
+ id: string,
209
+ _options?: { readonly rootDir?: string }
210
+ ): CurrentResourceDetail | undefined =>
211
+ app.getResource(id) === undefined
212
+ ? undefined
213
+ : (deriveResourceDetail(app, id) as CurrentResourceDetail);
214
+
215
+ export const deriveCurrentSignalDetail = (
216
+ app: Topo,
217
+ id: string,
218
+ _options?: { readonly rootDir?: string }
219
+ ): SignalDetailReport | undefined => deriveSignalDetail(app, id);
220
+
221
+ export const deriveCurrentTopoDetail = (
222
+ app: Topo,
223
+ id: string,
224
+ options?: CurrentTopoReadOptions
225
+ ): CurrentTopoDetail | undefined =>
226
+ deriveCurrentTrailDetail(app, id, options) ??
227
+ deriveCurrentResourceDetail(app, id) ??
228
+ deriveCurrentSignalDetail(app, id);
229
+
230
+ export const deriveCurrentTopoMatches = (
231
+ app: Topo,
232
+ id: string,
233
+ options?: CurrentTopoReadOptions
234
+ ): readonly CurrentTopoMatch[] => {
235
+ const matches: CurrentTopoMatch[] = [];
236
+ let activationGraph: ActivationGraphReport | undefined;
237
+ const getActivationGraph = (): ActivationGraphReport =>
238
+ (activationGraph ??= deriveActivationGraph(app));
239
+ let topoGraph: ReturnType<typeof deriveTopoGraph> | undefined;
240
+ const getTopoGraph = (): ReturnType<typeof deriveTopoGraph> =>
241
+ (topoGraph ??= deriveTopoGraph(app, { cliAliases: options?.cliAliases }));
242
+
243
+ const trail = app.get(id);
244
+ if (trail !== undefined) {
245
+ matches.push({
246
+ detail: deriveTrailDetail(trail, app, getActivationGraph(), {
247
+ surfaceLayerNames: options?.surfaceLayerNames,
248
+ topoGraph: getTopoGraph(),
249
+ }),
250
+ kind: 'trail',
251
+ });
252
+ }
253
+
254
+ const resource = deriveCurrentResourceDetail(app, id);
255
+ if (resource !== undefined) {
256
+ matches.push({ detail: resource, kind: 'resource' });
257
+ }
258
+
259
+ const signal = deriveSignalDetail(app, id, activationGraph);
260
+ if (signal !== undefined) {
261
+ matches.push({ detail: signal, kind: 'signal' });
262
+ }
263
+
264
+ return matches;
265
+ };
266
+
267
+ export const validateCurrentTopo = async (
268
+ app: Topo,
269
+ options?: {
270
+ readonly cliAliases?:
271
+ | Readonly<Record<string, readonly CliCommandAliasInput[]>>
272
+ | undefined;
273
+ readonly rootDir?: string;
274
+ }
275
+ ): Promise<Result<TopoValidateReport, Error>> => {
276
+ const rootDir = deriveRootDir(options?.rootDir);
277
+ let committedLock: Awaited<ReturnType<typeof readCommittedLock>>;
278
+ try {
279
+ committedLock = await readCommittedLock(rootDir);
280
+ } catch (error) {
281
+ const message =
282
+ error instanceof Error
283
+ ? error.message
284
+ : 'Unable to read committed trails.lock manifest.';
285
+ return Result.err(
286
+ error instanceof Error
287
+ ? new ValidationError(message, { cause: error })
288
+ : new ValidationError(message)
289
+ );
290
+ }
291
+
292
+ if (committedLock === null) {
293
+ return Result.err(
294
+ new NotFoundError(
295
+ 'No committed trails.lock found. Run `trails compile` first.'
296
+ )
297
+ );
298
+ }
299
+
300
+ const currentExport = deriveCurrentTopoExport(app, {
301
+ cliAliases: options?.cliAliases,
302
+ rootDir,
303
+ });
304
+ if (currentExport.isErr()) {
305
+ return currentExport;
306
+ }
307
+ const currentTopo = JSON.parse(
308
+ currentExport.value.topoGraphJson
309
+ ) as TopoGraph;
310
+ const currentHash = currentExport.value.topoGraphHash;
311
+ const topoArtifact = committedLock.lockManifest.artifacts.find(
312
+ (artifact) => artifact.role === 'topo' && artifact.path === 'topo.lock'
313
+ );
314
+ if (topoArtifact === undefined) {
315
+ return Result.err(
316
+ new NotFoundError(
317
+ 'No topo.lock artifact found in trails.lock. Run `trails compile` first.'
318
+ )
319
+ );
320
+ }
321
+
322
+ const committedTopo = committedLock.topoGraph;
323
+ const committedHash = deriveTopoGraphHash(committedTopo);
324
+ if (committedHash !== topoArtifact.sha256) {
325
+ return Result.err(
326
+ new ValidationError(
327
+ 'trails.lock graph hash does not match its embedded TopoGraph. Run `trails compile` to refresh it.'
328
+ )
329
+ );
330
+ }
331
+
332
+ if (topoArtifact.sha256 !== currentHash) {
333
+ const forceStrippedHash = deriveTopoGraphHash(
334
+ stripTopoGraphForces(committedTopo)
335
+ );
336
+ if (forceStrippedHash === currentHash) {
337
+ return Result.ok({
338
+ committedHash: topoArtifact.sha256,
339
+ currentHash,
340
+ lockPath: LOCK_PATH,
341
+ stale: false,
342
+ });
343
+ }
344
+ const breakingSummary = (() => {
345
+ const diff = deriveTopoGraphDiff(committedTopo, currentTopo);
346
+ return diff.breaking.length > 0
347
+ ? ` Breaking changes detected: ${diff.breaking.length}.`
348
+ : '';
349
+ })();
350
+ return Result.err(
351
+ new ConflictError(
352
+ `trails.lock is stale. Run \`trails compile\` to refresh it.${breakingSummary}`
353
+ )
354
+ );
355
+ }
356
+
357
+ return Result.ok({
358
+ committedHash: topoArtifact.sha256,
359
+ currentHash,
360
+ lockPath: LOCK_PATH,
361
+ stale: false,
362
+ });
363
+ };