@ontrails/trails 1.0.0-beta.2 → 1.0.0-beta.22

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 (150) hide show
  1. package/CHANGELOG.md +647 -0
  2. package/README.md +26 -0
  3. package/package.json +28 -7
  4. package/src/app.ts +86 -2
  5. package/src/clack.ts +22 -0
  6. package/src/cli.ts +330 -11
  7. package/src/completions.ts +240 -0
  8. package/src/lifecycle-source-io.ts +33 -0
  9. package/src/load-app-mirror.ts +202 -0
  10. package/src/local-state-io.ts +153 -0
  11. package/src/mcp-app.ts +30 -0
  12. package/src/mcp-options.ts +77 -0
  13. package/src/mcp.ts +8 -0
  14. package/src/project-writes.ts +377 -0
  15. package/src/release/bindings.ts +39 -0
  16. package/src/release/check.ts +818 -0
  17. package/src/release/config.ts +63 -0
  18. package/src/release/contract-facts.ts +425 -0
  19. package/src/release/index.ts +85 -0
  20. package/src/release/native-bun-publish.ts +651 -0
  21. package/src/release/native-bun-registry.ts +350 -0
  22. package/src/release/packed-artifacts-smoke.ts +236 -0
  23. package/src/release/smoke.ts +46 -0
  24. package/src/release/wayfinder-dogfood-smoke.ts +226 -0
  25. package/src/retired-topo-command.ts +36 -0
  26. package/src/run-adapter-check.ts +76 -0
  27. package/src/run-collision.ts +126 -0
  28. package/src/run-completions-install.ts +179 -0
  29. package/src/run-example.ts +149 -0
  30. package/src/run-examples.ts +148 -0
  31. package/src/run-quiet.ts +75 -0
  32. package/src/run-release-check.ts +74 -0
  33. package/src/run-trace.ts +273 -0
  34. package/src/run-warden.ts +39 -0
  35. package/src/run-watch.ts +432 -0
  36. package/src/scaffold-version-sync.ts +183 -0
  37. package/src/scaffold-versions.generated.ts +12 -0
  38. package/src/trails/adapter-check.ts +244 -0
  39. package/src/trails/add-surface.ts +94 -40
  40. package/src/trails/add-trail.ts +79 -41
  41. package/src/trails/add-verify.ts +95 -25
  42. package/src/trails/compile.ts +67 -0
  43. package/src/trails/completions-complete.ts +165 -0
  44. package/src/trails/completions.ts +47 -0
  45. package/src/trails/create-adapter.ts +1084 -0
  46. package/src/trails/create-scaffold.ts +399 -104
  47. package/src/trails/create-versions.ts +62 -0
  48. package/src/trails/create.ts +185 -71
  49. package/src/trails/deprecate.ts +59 -0
  50. package/src/trails/dev-clean.ts +82 -0
  51. package/src/trails/dev-reset.ts +50 -0
  52. package/src/trails/dev-stats.ts +72 -0
  53. package/src/trails/dev-support.ts +340 -0
  54. package/src/trails/doctor.ts +56 -0
  55. package/src/trails/draft-promote.ts +949 -0
  56. package/src/trails/guide.ts +74 -68
  57. package/src/trails/load-app.ts +1143 -15
  58. package/src/trails/project.ts +17 -3
  59. package/src/trails/release-check.ts +104 -0
  60. package/src/trails/release-smoke.ts +48 -0
  61. package/src/trails/revise.ts +53 -0
  62. package/src/trails/root-dir.ts +21 -0
  63. package/src/trails/run-example.ts +491 -0
  64. package/src/trails/run-examples.ts +145 -0
  65. package/src/trails/run.ts +410 -0
  66. package/src/trails/scaffold-json.ts +58 -0
  67. package/src/trails/survey.ts +881 -226
  68. package/src/trails/topo-activation.ts +385 -0
  69. package/src/trails/topo-constants.ts +2 -0
  70. package/src/trails/topo-history.ts +47 -0
  71. package/src/trails/topo-output-schemas.ts +248 -0
  72. package/src/trails/topo-pin.ts +52 -0
  73. package/src/trails/topo-read-support.ts +313 -0
  74. package/src/trails/topo-reports.ts +807 -0
  75. package/src/trails/topo-store-support.ts +174 -0
  76. package/src/trails/topo-support.ts +220 -0
  77. package/src/trails/topo-unpin.ts +61 -0
  78. package/src/trails/topo.ts +106 -0
  79. package/src/trails/validate.ts +38 -0
  80. package/src/trails/version-lifecycle-support.ts +945 -0
  81. package/src/trails/warden-guide.ts +129 -0
  82. package/src/trails/warden.ts +165 -58
  83. package/src/versions.ts +31 -0
  84. package/.turbo/turbo-build.log +0 -1
  85. package/.turbo/turbo-lint.log +0 -3
  86. package/.turbo/turbo-typecheck.log +0 -1
  87. package/__tests__/examples.test.ts +0 -6
  88. package/dist/bin/trails.d.ts +0 -3
  89. package/dist/bin/trails.d.ts.map +0 -1
  90. package/dist/bin/trails.js +0 -4
  91. package/dist/bin/trails.js.map +0 -1
  92. package/dist/src/app.d.ts +0 -2
  93. package/dist/src/app.d.ts.map +0 -1
  94. package/dist/src/app.js +0 -11
  95. package/dist/src/app.js.map +0 -1
  96. package/dist/src/clack.d.ts +0 -9
  97. package/dist/src/clack.d.ts.map +0 -1
  98. package/dist/src/clack.js +0 -62
  99. package/dist/src/clack.js.map +0 -1
  100. package/dist/src/cli.d.ts +0 -2
  101. package/dist/src/cli.d.ts.map +0 -1
  102. package/dist/src/cli.js +0 -13
  103. package/dist/src/cli.js.map +0 -1
  104. package/dist/src/trails/add-surface.d.ts +0 -13
  105. package/dist/src/trails/add-surface.d.ts.map +0 -1
  106. package/dist/src/trails/add-surface.js +0 -88
  107. package/dist/src/trails/add-surface.js.map +0 -1
  108. package/dist/src/trails/add-trail.d.ts +0 -11
  109. package/dist/src/trails/add-trail.d.ts.map +0 -1
  110. package/dist/src/trails/add-trail.js +0 -85
  111. package/dist/src/trails/add-trail.js.map +0 -1
  112. package/dist/src/trails/add-verify.d.ts +0 -10
  113. package/dist/src/trails/add-verify.d.ts.map +0 -1
  114. package/dist/src/trails/add-verify.js +0 -67
  115. package/dist/src/trails/add-verify.js.map +0 -1
  116. package/dist/src/trails/create-scaffold.d.ts +0 -15
  117. package/dist/src/trails/create-scaffold.d.ts.map +0 -1
  118. package/dist/src/trails/create-scaffold.js +0 -288
  119. package/dist/src/trails/create-scaffold.js.map +0 -1
  120. package/dist/src/trails/create.d.ts +0 -22
  121. package/dist/src/trails/create.d.ts.map +0 -1
  122. package/dist/src/trails/create.js +0 -121
  123. package/dist/src/trails/create.js.map +0 -1
  124. package/dist/src/trails/guide.d.ts +0 -11
  125. package/dist/src/trails/guide.d.ts.map +0 -1
  126. package/dist/src/trails/guide.js +0 -80
  127. package/dist/src/trails/guide.js.map +0 -1
  128. package/dist/src/trails/load-app.d.ts +0 -4
  129. package/dist/src/trails/load-app.d.ts.map +0 -1
  130. package/dist/src/trails/load-app.js +0 -24
  131. package/dist/src/trails/load-app.js.map +0 -1
  132. package/dist/src/trails/project.d.ts +0 -8
  133. package/dist/src/trails/project.d.ts.map +0 -1
  134. package/dist/src/trails/project.js +0 -43
  135. package/dist/src/trails/project.js.map +0 -1
  136. package/dist/src/trails/survey.d.ts +0 -33
  137. package/dist/src/trails/survey.d.ts.map +0 -1
  138. package/dist/src/trails/survey.js +0 -225
  139. package/dist/src/trails/survey.js.map +0 -1
  140. package/dist/src/trails/warden.d.ts +0 -19
  141. package/dist/src/trails/warden.d.ts.map +0 -1
  142. package/dist/src/trails/warden.js +0 -88
  143. package/dist/src/trails/warden.js.map +0 -1
  144. package/dist/tsconfig.tsbuildinfo +0 -1
  145. package/src/__tests__/create.test.ts +0 -349
  146. package/src/__tests__/guide.test.ts +0 -91
  147. package/src/__tests__/load-app.test.ts +0 -15
  148. package/src/__tests__/survey.test.ts +0 -161
  149. package/src/__tests__/warden.test.ts +0 -74
  150. package/tsconfig.json +0 -9
@@ -0,0 +1,52 @@
1
+ import { Result, trail } from '@ontrails/core';
2
+ import { z } from 'zod';
3
+
4
+ import { tryLoadFreshAppLease } from './load-app.js';
5
+ import { resolveTrailRootDir } from './root-dir.js';
6
+ import {
7
+ createIsolatedExampleInput,
8
+ pinCurrentTopoSnapshot,
9
+ topoSnapshotOutput,
10
+ } from './topo-support.js';
11
+
12
+ export const topoPinTrail = trail('topo.pin', {
13
+ blaze: async (input, ctx) => {
14
+ const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
15
+ if (rootDirResult.isErr()) {
16
+ return rootDirResult;
17
+ }
18
+ const rootDir = rootDirResult.value;
19
+ const leaseResult = await tryLoadFreshAppLease(input.module, rootDir);
20
+ if (leaseResult.isErr()) {
21
+ return leaseResult;
22
+ }
23
+ const lease = leaseResult.value;
24
+ try {
25
+ return Result.ok(
26
+ pinCurrentTopoSnapshot(lease.app, { name: input.name, rootDir })
27
+ );
28
+ } finally {
29
+ lease.release();
30
+ }
31
+ },
32
+ description: 'Pin the current topo under a durable name',
33
+ examples: [
34
+ {
35
+ input: {
36
+ ...createIsolatedExampleInput('topo-pin'),
37
+ name: 'before-auth-refactor',
38
+ },
39
+ name: 'Pin the current topo',
40
+ },
41
+ ],
42
+ input: z.object({
43
+ module: z.string().optional().describe('Path to the app module'),
44
+ name: z.string().describe('Pin name'),
45
+ rootDir: z.string().optional().describe('Workspace root directory'),
46
+ }),
47
+ intent: 'write',
48
+ output: z.object({
49
+ snapshot: topoSnapshotOutput,
50
+ }),
51
+ permit: { scopes: ['topo:write'] },
52
+ });
@@ -0,0 +1,313 @@
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 { 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
+ stripTopoGraphForces,
28
+ } from '@ontrails/topographer';
29
+ import type { TopoGraph } from '@ontrails/topographer';
30
+
31
+ import type {
32
+ BriefReport,
33
+ SignalDetailReport,
34
+ SurfaceLayerNames,
35
+ SurveyListReport,
36
+ TrailDetailReport,
37
+ } from './topo-reports.js';
38
+ import {
39
+ countTrailExamples,
40
+ deriveBriefReport,
41
+ deriveResourceDetail,
42
+ deriveSignalDetail,
43
+ deriveSurveyList,
44
+ deriveTrailDetail,
45
+ } from './topo-reports.js';
46
+ import type { ActivationGraphReport } from './topo-activation.js';
47
+ import { deriveActivationGraph } from './topo-activation.js';
48
+ import type { TopoSummaryReport, TopoValidateReport } from './topo-support.js';
49
+ import { deriveRootDir, LOCK_PATH } from './topo-support.js';
50
+ import { deriveCurrentTopoExport } from './topo-store-support.js';
51
+
52
+ export type CurrentTrailDetail = TrailDetailReport;
53
+
54
+ export interface CurrentResourceDetail {
55
+ readonly description: string | null;
56
+ readonly health: 'available' | 'none';
57
+ readonly id: string;
58
+ readonly kind: 'resource';
59
+ readonly lifetime: 'singleton';
60
+ readonly usedBy: readonly string[];
61
+ }
62
+
63
+ export type CurrentTopoDetail =
64
+ | CurrentResourceDetail
65
+ | CurrentTrailDetail
66
+ | SignalDetailReport;
67
+
68
+ export interface CurrentTopoMatch {
69
+ readonly kind: CurrentTopoDetail['kind'];
70
+ readonly detail: CurrentTopoDetail;
71
+ }
72
+
73
+ export interface CurrentTopoReadOptions {
74
+ readonly rootDir?: string | undefined;
75
+ readonly surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined;
76
+ }
77
+
78
+ const isStringArray = (value: unknown): value is readonly string[] =>
79
+ Array.isArray(value) && value.every((item) => typeof item === 'string');
80
+
81
+ export const readSurfaceLayerNamesFromContext = (
82
+ ctx: TrailContext
83
+ ): Partial<SurfaceLayerNames> => {
84
+ const value = ctx.extensions?.[SURFACE_LAYER_NAMES_KEY];
85
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
86
+ return {};
87
+ }
88
+ const raw = value as Record<string, unknown>;
89
+ return {
90
+ ...(isStringArray(raw['cli']) ? { cli: raw['cli'] } : {}),
91
+ ...(isStringArray(raw['http']) ? { http: raw['http'] } : {}),
92
+ ...(isStringArray(raw['mcp']) ? { mcp: raw['mcp'] } : {}),
93
+ };
94
+ };
95
+
96
+ const hasCommittedLock = (trailsDir: string): boolean =>
97
+ existsSync(join(trailsDir, 'trails.lock'));
98
+
99
+ // ---------------------------------------------------------------------------
100
+ // Public read-only consumers
101
+ // ---------------------------------------------------------------------------
102
+
103
+ export const buildTopoSummary = (
104
+ app: Topo,
105
+ options?: { readonly rootDir?: string }
106
+ ): TopoSummaryReport => {
107
+ const rootDir = deriveRootDir(options?.rootDir);
108
+ const trailsDir = deriveTrailsDir({ rootDir });
109
+ return {
110
+ app: deriveBriefReport(app),
111
+ dbPath: deriveTrailsDbPath({ rootDir }),
112
+ list: deriveSurveyList(app),
113
+ lockExists: hasCommittedLock(trailsDir),
114
+ lockPath: LOCK_PATH,
115
+ };
116
+ };
117
+
118
+ export const buildCurrentTopoBrief = (
119
+ app: Topo,
120
+ _options?: { readonly rootDir?: string }
121
+ ): BriefReport => deriveBriefReport(app);
122
+
123
+ export const buildCurrentTopoList = (
124
+ app: Topo,
125
+ _options?: { readonly rootDir?: string }
126
+ ): SurveyListReport => deriveSurveyList(app);
127
+
128
+ export const buildCurrentGuideEntries = (
129
+ app: Topo,
130
+ _options?: { readonly rootDir?: string }
131
+ ): readonly {
132
+ readonly description: string;
133
+ readonly exampleCount: number;
134
+ readonly id: string;
135
+ readonly kind: 'trail';
136
+ }[] =>
137
+ app
138
+ .list()
139
+ .map((trail) => ({
140
+ description: trail.description ?? '(no description)',
141
+ exampleCount: countTrailExamples(trail),
142
+ id: trail.id,
143
+ kind: 'trail' as const,
144
+ }))
145
+ .toSorted((a, b) => a.id.localeCompare(b.id));
146
+
147
+ export const buildCurrentTrailDetail = (
148
+ app: Topo,
149
+ id: string,
150
+ options?: CurrentTopoReadOptions
151
+ ): CurrentTrailDetail | undefined => {
152
+ const trail = app.get(id);
153
+ return trail === undefined
154
+ ? undefined
155
+ : deriveTrailDetail(trail, app, undefined, {
156
+ surfaceLayerNames: options?.surfaceLayerNames,
157
+ });
158
+ };
159
+
160
+ export const buildCurrentResourceDetail = (
161
+ app: Topo,
162
+ id: string,
163
+ _options?: { readonly rootDir?: string }
164
+ ): CurrentResourceDetail | undefined =>
165
+ app.getResource(id) === undefined
166
+ ? undefined
167
+ : (deriveResourceDetail(app, id) as CurrentResourceDetail);
168
+
169
+ export const buildCurrentSignalDetail = (
170
+ app: Topo,
171
+ id: string,
172
+ _options?: { readonly rootDir?: string }
173
+ ): SignalDetailReport | undefined => deriveSignalDetail(app, id);
174
+
175
+ export const buildCurrentTopoDetail = (
176
+ app: Topo,
177
+ id: string,
178
+ options?: CurrentTopoReadOptions
179
+ ): CurrentTopoDetail | undefined =>
180
+ buildCurrentTrailDetail(app, id, options) ??
181
+ buildCurrentResourceDetail(app, id) ??
182
+ buildCurrentSignalDetail(app, id);
183
+
184
+ export const buildCurrentTopoMatches = (
185
+ app: Topo,
186
+ id: string,
187
+ options?: CurrentTopoReadOptions
188
+ ): readonly CurrentTopoMatch[] => {
189
+ const matches: CurrentTopoMatch[] = [];
190
+ let activationGraph: ActivationGraphReport | undefined;
191
+ const getActivationGraph = (): ActivationGraphReport =>
192
+ (activationGraph ??= deriveActivationGraph(app));
193
+ let topoGraph: ReturnType<typeof deriveTopoGraph> | undefined;
194
+ const getTopoGraph = (): ReturnType<typeof deriveTopoGraph> =>
195
+ (topoGraph ??= deriveTopoGraph(app));
196
+
197
+ const trail = app.get(id);
198
+ if (trail !== undefined) {
199
+ matches.push({
200
+ detail: deriveTrailDetail(trail, app, getActivationGraph(), {
201
+ surfaceLayerNames: options?.surfaceLayerNames,
202
+ topoGraph: getTopoGraph(),
203
+ }),
204
+ kind: 'trail',
205
+ });
206
+ }
207
+
208
+ const resource = buildCurrentResourceDetail(app, id);
209
+ if (resource !== undefined) {
210
+ matches.push({ detail: resource, kind: 'resource' });
211
+ }
212
+
213
+ const signal = deriveSignalDetail(app, id, activationGraph);
214
+ if (signal !== undefined) {
215
+ matches.push({ detail: signal, kind: 'signal' });
216
+ }
217
+
218
+ return matches;
219
+ };
220
+
221
+ export const validateCurrentTopo = async (
222
+ app: Topo,
223
+ options?: { readonly rootDir?: string }
224
+ ): Promise<Result<TopoValidateReport, Error>> => {
225
+ const rootDir = deriveRootDir(options?.rootDir);
226
+ let lockManifest: Awaited<ReturnType<typeof readLockManifest>>;
227
+ try {
228
+ lockManifest = await readLockManifest({
229
+ dir: deriveTrailsDir({ rootDir }),
230
+ });
231
+ } catch (error) {
232
+ const message =
233
+ error instanceof Error
234
+ ? error.message
235
+ : 'Unable to read committed trails.lock manifest.';
236
+ return Result.err(
237
+ error instanceof Error
238
+ ? new ValidationError(message, { cause: error })
239
+ : new ValidationError(message)
240
+ );
241
+ }
242
+
243
+ if (lockManifest === null) {
244
+ return Result.err(
245
+ new NotFoundError(
246
+ 'No committed trails.lock found. Run `trails compile` first.'
247
+ )
248
+ );
249
+ }
250
+
251
+ const currentExport = deriveCurrentTopoExport(app, { rootDir });
252
+ if (currentExport.isErr()) {
253
+ return currentExport;
254
+ }
255
+ const currentTopo = JSON.parse(
256
+ currentExport.value.topoGraphJson
257
+ ) as TopoGraph;
258
+ const currentHash = currentExport.value.topoGraphHash;
259
+ const topoArtifact = lockManifest.artifacts.find(
260
+ (artifact) => artifact.role === 'topo' && artifact.path === 'topo.lock'
261
+ );
262
+ if (topoArtifact === undefined) {
263
+ return Result.err(
264
+ new NotFoundError(
265
+ 'No topo.lock artifact found in trails.lock. Run `trails compile` first.'
266
+ )
267
+ );
268
+ }
269
+
270
+ if (topoArtifact.sha256 !== currentHash) {
271
+ const committedTopo = await readTopoGraph({
272
+ dir: deriveTrailsDir({ rootDir }),
273
+ });
274
+ if (committedTopo !== null) {
275
+ const committedHash = deriveTopoGraphHash(committedTopo);
276
+ const forceStrippedHash = deriveTopoGraphHash(
277
+ stripTopoGraphForces(committedTopo)
278
+ );
279
+ if (
280
+ committedHash === topoArtifact.sha256 &&
281
+ forceStrippedHash === currentHash
282
+ ) {
283
+ return Result.ok({
284
+ committedHash: topoArtifact.sha256,
285
+ currentHash,
286
+ lockPath: LOCK_PATH,
287
+ stale: false,
288
+ });
289
+ }
290
+ }
291
+ const breakingSummary =
292
+ committedTopo === null
293
+ ? ''
294
+ : (() => {
295
+ const diff = deriveTopoGraphDiff(committedTopo, currentTopo);
296
+ return diff.breaking.length > 0
297
+ ? ` Breaking changes detected: ${diff.breaking.length}.`
298
+ : '';
299
+ })();
300
+ return Result.err(
301
+ new ConflictError(
302
+ `trails.lock is stale. Run \`trails compile\` to refresh it.${breakingSummary}`
303
+ )
304
+ );
305
+ }
306
+
307
+ return Result.ok({
308
+ committedHash: topoArtifact.sha256,
309
+ currentHash,
310
+ lockPath: LOCK_PATH,
311
+ stale: false,
312
+ });
313
+ };