@ontrails/trails 1.0.0-beta.15 → 1.0.0-beta.17
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 +215 -2
- package/README.md +27 -0
- package/package.json +21 -8
- package/src/app.ts +15 -5
- package/src/cli.ts +303 -10
- package/src/completions.ts +240 -0
- package/src/load-app-mirror.ts +160 -0
- package/src/local-state-io.ts +153 -0
- package/src/project-writes.ts +320 -0
- package/src/run-collision.ts +125 -0
- package/src/run-completions-install.ts +179 -0
- package/src/run-example.ts +149 -0
- package/src/run-examples.ts +148 -0
- package/src/run-quiet.ts +75 -0
- package/src/run-trace.ts +273 -0
- package/src/run-warden.ts +39 -0
- package/src/run-watch.ts +432 -0
- package/src/scaffold-versions.generated.ts +12 -0
- package/src/trails/add-surface.ts +45 -23
- package/src/trails/add-trail.ts +27 -17
- package/src/trails/add-verify.ts +57 -17
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-scaffold.ts +86 -33
- package/src/trails/create.ts +11 -3
- package/src/trails/dev-clean.ts +6 -1
- package/src/trails/dev-reset.ts +6 -1
- package/src/trails/dev-stats.ts +6 -1
- package/src/trails/dev-support.ts +29 -17
- package/src/trails/draft-promote.ts +289 -80
- package/src/trails/guide.ts +54 -34
- package/src/trails/load-app.ts +251 -56
- package/src/trails/root-dir.ts +21 -0
- package/src/trails/run-example.ts +482 -0
- package/src/trails/run-examples.ts +141 -0
- package/src/trails/run.ts +403 -0
- package/src/trails/survey.ts +534 -200
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-compile.ts +55 -0
- package/src/trails/topo-history.ts +6 -1
- package/src/trails/topo-output-schemas.ts +219 -0
- package/src/trails/topo-pin.ts +19 -6
- package/src/trails/topo-read-support.ts +171 -228
- package/src/trails/topo-reports.ts +587 -25
- package/src/trails/topo-store-support.ts +43 -19
- package/src/trails/topo-support.ts +18 -28
- package/src/trails/topo-unpin.ts +6 -1
- package/src/trails/topo-verify.ts +18 -5
- package/src/trails/topo.ts +60 -23
- package/src/trails/warden-guide.ts +121 -0
- package/src/trails/warden.ts +137 -56
- package/src/versions.ts +3 -18
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/__tests__/examples.test.ts +0 -45
- package/dist/bin/trails.d.ts +0 -3
- package/dist/bin/trails.d.ts.map +0 -1
- package/dist/bin/trails.js +0 -4
- package/dist/bin/trails.js.map +0 -1
- package/dist/src/app.d.ts +0 -2
- package/dist/src/app.d.ts.map +0 -1
- package/dist/src/app.js +0 -22
- package/dist/src/app.js.map +0 -1
- package/dist/src/clack.d.ts +0 -9
- package/dist/src/clack.d.ts.map +0 -1
- package/dist/src/clack.js +0 -84
- package/dist/src/clack.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -14
- package/dist/src/cli.js.map +0 -1
- package/dist/src/trails/add-surface.d.ts +0 -13
- package/dist/src/trails/add-surface.d.ts.map +0 -1
- package/dist/src/trails/add-surface.js +0 -110
- package/dist/src/trails/add-surface.js.map +0 -1
- package/dist/src/trails/add-trail.d.ts +0 -12
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -104
- package/dist/src/trails/add-trail.js.map +0 -1
- package/dist/src/trails/add-trailhead.d.ts +0 -13
- package/dist/src/trails/add-trailhead.d.ts.map +0 -1
- package/dist/src/trails/add-trailhead.js +0 -88
- package/dist/src/trails/add-trailhead.js.map +0 -1
- package/dist/src/trails/add-verify.d.ts +0 -10
- package/dist/src/trails/add-verify.d.ts.map +0 -1
- package/dist/src/trails/add-verify.js +0 -68
- package/dist/src/trails/add-verify.js.map +0 -1
- package/dist/src/trails/create-scaffold.d.ts +0 -15
- package/dist/src/trails/create-scaffold.d.ts.map +0 -1
- package/dist/src/trails/create-scaffold.js +0 -295
- package/dist/src/trails/create-scaffold.js.map +0 -1
- package/dist/src/trails/create.d.ts +0 -18
- package/dist/src/trails/create.d.ts.map +0 -1
- package/dist/src/trails/create.js +0 -126
- package/dist/src/trails/create.js.map +0 -1
- package/dist/src/trails/dev-clean.d.ts +0 -9
- package/dist/src/trails/dev-clean.d.ts.map +0 -1
- package/dist/src/trails/dev-clean.js +0 -66
- package/dist/src/trails/dev-clean.js.map +0 -1
- package/dist/src/trails/dev-reset.d.ts +0 -6
- package/dist/src/trails/dev-reset.d.ts.map +0 -1
- package/dist/src/trails/dev-reset.js +0 -39
- package/dist/src/trails/dev-reset.js.map +0 -1
- package/dist/src/trails/dev-stats.d.ts +0 -7
- package/dist/src/trails/dev-stats.d.ts.map +0 -1
- package/dist/src/trails/dev-stats.js +0 -61
- package/dist/src/trails/dev-stats.js.map +0 -1
- package/dist/src/trails/dev-support.d.ts +0 -64
- package/dist/src/trails/dev-support.d.ts.map +0 -1
- package/dist/src/trails/dev-support.js +0 -181
- package/dist/src/trails/dev-support.js.map +0 -1
- package/dist/src/trails/draft-promote.d.ts +0 -18
- package/dist/src/trails/draft-promote.d.ts.map +0 -1
- package/dist/src/trails/draft-promote.js +0 -400
- package/dist/src/trails/draft-promote.js.map +0 -1
- package/dist/src/trails/guide.d.ts +0 -21
- package/dist/src/trails/guide.d.ts.map +0 -1
- package/dist/src/trails/guide.js +0 -61
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -12
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -415
- package/dist/src/trails/load-app.js.map +0 -1
- package/dist/src/trails/project.d.ts +0 -8
- package/dist/src/trails/project.d.ts.map +0 -1
- package/dist/src/trails/project.js +0 -54
- package/dist/src/trails/project.js.map +0 -1
- package/dist/src/trails/survey.d.ts +0 -18
- package/dist/src/trails/survey.d.ts.map +0 -1
- package/dist/src/trails/survey.js +0 -234
- package/dist/src/trails/survey.js.map +0 -1
- package/dist/src/trails/topo-constants.d.ts +0 -3
- package/dist/src/trails/topo-constants.d.ts.map +0 -1
- package/dist/src/trails/topo-constants.js +0 -3
- package/dist/src/trails/topo-constants.js.map +0 -1
- package/dist/src/trails/topo-export.d.ts +0 -19
- package/dist/src/trails/topo-export.d.ts.map +0 -1
- package/dist/src/trails/topo-export.js +0 -31
- package/dist/src/trails/topo-export.js.map +0 -1
- package/dist/src/trails/topo-history.d.ts +0 -20
- package/dist/src/trails/topo-history.d.ts.map +0 -1
- package/dist/src/trails/topo-history.js +0 -32
- package/dist/src/trails/topo-history.js.map +0 -1
- package/dist/src/trails/topo-pin.d.ts +0 -17
- package/dist/src/trails/topo-pin.d.ts.map +0 -1
- package/dist/src/trails/topo-pin.js +0 -31
- package/dist/src/trails/topo-pin.js.map +0 -1
- package/dist/src/trails/topo-read-support.d.ts +0 -58
- package/dist/src/trails/topo-read-support.d.ts.map +0 -1
- package/dist/src/trails/topo-read-support.js +0 -167
- package/dist/src/trails/topo-read-support.js.map +0 -1
- package/dist/src/trails/topo-reports.d.ts +0 -54
- package/dist/src/trails/topo-reports.d.ts.map +0 -1
- package/dist/src/trails/topo-reports.js +0 -128
- package/dist/src/trails/topo-reports.js.map +0 -1
- package/dist/src/trails/topo-show.d.ts +0 -23
- package/dist/src/trails/topo-show.d.ts.map +0 -1
- package/dist/src/trails/topo-show.js +0 -49
- package/dist/src/trails/topo-show.js.map +0 -1
- package/dist/src/trails/topo-store-support.d.ts +0 -13
- package/dist/src/trails/topo-store-support.d.ts.map +0 -1
- package/dist/src/trails/topo-store-support.js +0 -55
- package/dist/src/trails/topo-store-support.js.map +0 -1
- package/dist/src/trails/topo-support.d.ts +0 -76
- package/dist/src/trails/topo-support.d.ts.map +0 -1
- package/dist/src/trails/topo-support.js +0 -132
- package/dist/src/trails/topo-support.js.map +0 -1
- package/dist/src/trails/topo-unpin.d.ts +0 -20
- package/dist/src/trails/topo-unpin.d.ts.map +0 -1
- package/dist/src/trails/topo-unpin.js +0 -44
- package/dist/src/trails/topo-unpin.js.map +0 -1
- package/dist/src/trails/topo-verify.d.ts +0 -5
- package/dist/src/trails/topo-verify.d.ts.map +0 -1
- package/dist/src/trails/topo-verify.js +0 -24
- package/dist/src/trails/topo-verify.js.map +0 -1
- package/dist/src/trails/topo.d.ts +0 -5
- package/dist/src/trails/topo.d.ts.map +0 -1
- package/dist/src/trails/topo.js +0 -63
- package/dist/src/trails/topo.js.map +0 -1
- package/dist/src/trails/warden.d.ts +0 -20
- package/dist/src/trails/warden.d.ts.map +0 -1
- package/dist/src/trails/warden.js +0 -98
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/src/versions.d.ts +0 -12
- package/dist/src/versions.d.ts.map +0 -1
- package/dist/src/versions.js +0 -23
- package/dist/src/versions.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/add-trail.test.ts +0 -97
- package/src/__tests__/create.test.ts +0 -415
- package/src/__tests__/draft-promote.test.ts +0 -144
- package/src/__tests__/guide.test.ts +0 -96
- package/src/__tests__/load-app.test.ts +0 -419
- package/src/__tests__/survey.test.ts +0 -377
- package/src/__tests__/topo-dev.test.ts +0 -426
- package/src/__tests__/warden.test.ts +0 -74
- package/src/trails/topo-export.ts +0 -35
- package/src/trails/topo-show.ts +0 -54
- package/tsconfig.json +0 -9
- package/tsconfig.tests.json +0 -10
package/src/trails/survey.ts
CHANGED
|
@@ -1,245 +1,471 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `survey` trail -- Full topo introspection.
|
|
3
3
|
*
|
|
4
|
-
* Lists trails,
|
|
5
|
-
*
|
|
4
|
+
* Lists trails, looks up trails/resources/signals, and diffs against previous
|
|
5
|
+
* versions.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { join } from 'node:path';
|
|
8
|
+
import { basename, extname, join } from 'node:path';
|
|
9
9
|
|
|
10
10
|
import type { Topo } from '@ontrails/core';
|
|
11
|
-
import { NotFoundError, Result, trail } from '@ontrails/core';
|
|
12
|
-
import type { DiffResult } from '@ontrails/schema';
|
|
13
11
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
deriveSafePath,
|
|
13
|
+
NotFoundError,
|
|
14
|
+
Result,
|
|
15
|
+
trail,
|
|
16
|
+
ValidationError,
|
|
17
|
+
} from '@ontrails/core';
|
|
18
|
+
import type { DiffEntry, DiffResult, TopoGraph } from '@ontrails/topographer';
|
|
19
|
+
import {
|
|
20
|
+
createTopoStore,
|
|
21
|
+
deriveTopoGraphDiff,
|
|
22
|
+
deriveTopoGraph,
|
|
23
|
+
TOPO_GRAPH_SCHEMA_VERSION,
|
|
24
|
+
readTopoGraph,
|
|
25
|
+
} from '@ontrails/topographer';
|
|
19
26
|
import { z } from 'zod';
|
|
20
27
|
|
|
21
|
-
import {
|
|
28
|
+
import { writeIsolatedExampleJsonFile } from '../local-state-io.js';
|
|
29
|
+
|
|
30
|
+
import { tryLoadFreshAppLease } from './load-app.js';
|
|
31
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
22
32
|
import {
|
|
23
33
|
buildCurrentTopoBrief,
|
|
24
|
-
buildCurrentTopoDetail,
|
|
25
34
|
buildCurrentTopoList,
|
|
35
|
+
buildCurrentTopoMatches,
|
|
36
|
+
buildCurrentTrailDetail,
|
|
37
|
+
buildCurrentResourceDetail,
|
|
38
|
+
buildCurrentSignalDetail,
|
|
39
|
+
readSurfaceLayerNamesFromContext,
|
|
26
40
|
} from './topo-read-support.js';
|
|
27
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
activationOverviewOutput,
|
|
43
|
+
resourceDetailOutput,
|
|
44
|
+
shippedSurfaceInventoryOutput,
|
|
45
|
+
signalDetailOutput,
|
|
46
|
+
trailDetailOutput,
|
|
47
|
+
} from './topo-output-schemas.js';
|
|
48
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
49
|
+
import {
|
|
50
|
+
briefReportSchema,
|
|
51
|
+
deriveShippedSurfaceProjectionInventory,
|
|
52
|
+
} from './topo-reports.js';
|
|
53
|
+
import type { SurfaceLayerNames } from './topo-reports.js';
|
|
28
54
|
|
|
29
55
|
export {
|
|
56
|
+
briefReportSchema,
|
|
30
57
|
deriveBriefReport,
|
|
31
58
|
deriveResourceDetail,
|
|
59
|
+
deriveShippedSurfaceProjectionInventory,
|
|
60
|
+
deriveSignalDetail,
|
|
32
61
|
deriveSurveyList,
|
|
33
62
|
deriveTrailDetail,
|
|
34
63
|
} from './topo-reports.js';
|
|
35
64
|
export type {
|
|
36
65
|
BriefReport,
|
|
66
|
+
ShippedSurfaceInventoryReport,
|
|
67
|
+
ShippedSurfaceProjection,
|
|
68
|
+
SignalDetailReport,
|
|
69
|
+
SurfaceLayerNames,
|
|
37
70
|
SurveyListReport,
|
|
38
71
|
TrailDetailReport,
|
|
39
72
|
} from './topo-reports.js';
|
|
40
73
|
|
|
41
74
|
// ---------------------------------------------------------------------------
|
|
42
|
-
//
|
|
75
|
+
// Survey diff helpers
|
|
43
76
|
// ---------------------------------------------------------------------------
|
|
44
77
|
|
|
45
|
-
|
|
78
|
+
interface SurveyDiffReport {
|
|
79
|
+
readonly against: string;
|
|
80
|
+
readonly breaking: readonly DiffEntry[];
|
|
81
|
+
readonly hasBreaking: boolean;
|
|
82
|
+
readonly info: readonly DiffEntry[];
|
|
83
|
+
readonly mode: 'diff';
|
|
84
|
+
readonly warnings: readonly DiffEntry[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const formatDiff = (diff: DiffResult, against: string): SurveyDiffReport => ({
|
|
88
|
+
against,
|
|
46
89
|
breaking: diff.breaking,
|
|
47
90
|
hasBreaking: diff.hasBreaking,
|
|
48
91
|
info: diff.info,
|
|
92
|
+
mode: 'diff',
|
|
49
93
|
warnings: diff.warnings,
|
|
50
94
|
});
|
|
51
95
|
|
|
96
|
+
const createDiffExampleInput = (): {
|
|
97
|
+
readonly against: string;
|
|
98
|
+
readonly module: string;
|
|
99
|
+
readonly rootDir: string;
|
|
100
|
+
} => {
|
|
101
|
+
const input = createIsolatedExampleInput('survey-diff');
|
|
102
|
+
writeIsolatedExampleJsonFile(input.rootDir, 'baseline/topo.lock', {
|
|
103
|
+
activationGraph: {
|
|
104
|
+
edgeCount: 0,
|
|
105
|
+
edges: [],
|
|
106
|
+
sourceCount: 0,
|
|
107
|
+
sourceKeys: [],
|
|
108
|
+
trailIds: [],
|
|
109
|
+
},
|
|
110
|
+
activationSources: {},
|
|
111
|
+
entries: [],
|
|
112
|
+
generatedAt: '2026-01-01T00:00:00.000Z',
|
|
113
|
+
topoGraphSchemaVersion: TOPO_GRAPH_SCHEMA_VERSION,
|
|
114
|
+
} satisfies TopoGraph);
|
|
115
|
+
return { ...input, against: 'baseline' };
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const isNotFound = (error: unknown): boolean =>
|
|
119
|
+
typeof error === 'object' &&
|
|
120
|
+
error !== null &&
|
|
121
|
+
(error as NodeJS.ErrnoException).code === 'ENOENT';
|
|
122
|
+
|
|
123
|
+
const readTopoGraphFile = async (
|
|
124
|
+
filePath: string
|
|
125
|
+
): Promise<TopoGraph | null> => {
|
|
126
|
+
try {
|
|
127
|
+
return (await Bun.file(filePath).json()) as TopoGraph;
|
|
128
|
+
} catch (error: unknown) {
|
|
129
|
+
if (isNotFound(error)) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const readStoredTopoGraph = (
|
|
137
|
+
rootDir: string,
|
|
138
|
+
against: string
|
|
139
|
+
): TopoGraph | undefined => {
|
|
140
|
+
try {
|
|
141
|
+
const store = createTopoStore({ rootDir });
|
|
142
|
+
const stored =
|
|
143
|
+
store.exports.get({ pin: against }) ??
|
|
144
|
+
store.exports.get({ snapshotId: against });
|
|
145
|
+
return stored === undefined
|
|
146
|
+
? undefined
|
|
147
|
+
: (JSON.parse(stored.topoGraphJson) as TopoGraph);
|
|
148
|
+
} catch (error: unknown) {
|
|
149
|
+
if (error instanceof NotFoundError) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const readPathTopoGraph = async (
|
|
157
|
+
rootDir: string,
|
|
158
|
+
against: string
|
|
159
|
+
): Promise<Result<TopoGraph | null, Error>> => {
|
|
160
|
+
const safePath = deriveSafePath(rootDir, against);
|
|
161
|
+
if (safePath.isErr()) {
|
|
162
|
+
return safePath;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return Result.ok(
|
|
166
|
+
basename(safePath.value) === 'topo.lock' ||
|
|
167
|
+
extname(safePath.value) === '.json'
|
|
168
|
+
? await readTopoGraphFile(safePath.value)
|
|
169
|
+
: await readTopoGraph({ dir: safePath.value })
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const describeAgainstPathTarget = (against: string): string =>
|
|
174
|
+
basename(against) === 'topo.lock' || extname(against) === '.json'
|
|
175
|
+
? 'workspace-relative TopoGraph file'
|
|
176
|
+
: 'workspace-relative directory containing topo.lock';
|
|
177
|
+
|
|
178
|
+
const topoGraphNotFound = (against: string): NotFoundError =>
|
|
179
|
+
new NotFoundError(
|
|
180
|
+
`No TopoGraph found for: ${against}. Tried ${describeAgainstPathTarget(
|
|
181
|
+
against
|
|
182
|
+
)}, then topo-store pin and snapshot references.`
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const readAgainstTopoGraph = async (
|
|
186
|
+
rootDir: string,
|
|
187
|
+
against?: string | undefined
|
|
188
|
+
): Promise<Result<{ against: string; map: TopoGraph }, Error>> => {
|
|
189
|
+
if (against === undefined || against === 'saved') {
|
|
190
|
+
const map = await readTopoGraph({ dir: join(rootDir, '.trails') });
|
|
191
|
+
return map === null
|
|
192
|
+
? Result.err(
|
|
193
|
+
new NotFoundError(
|
|
194
|
+
'No saved TopoGraph found. Run `trails topo compile` first.'
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
: Result.ok({ against: 'saved', map });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Treat explicit filesystem targets as the most local user intent; stored
|
|
201
|
+
// pins and snapshot ids are fallback references when no path exists.
|
|
202
|
+
const pathMap = await readPathTopoGraph(rootDir, against);
|
|
203
|
+
if (pathMap.isErr()) {
|
|
204
|
+
return pathMap;
|
|
205
|
+
}
|
|
206
|
+
if (pathMap.value !== null) {
|
|
207
|
+
return Result.ok({ against, map: pathMap.value });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const storedMap = readStoredTopoGraph(rootDir, against);
|
|
211
|
+
if (storedMap !== undefined) {
|
|
212
|
+
return Result.ok({ against, map: storedMap });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return Result.err(topoGraphNotFound(against));
|
|
216
|
+
};
|
|
217
|
+
|
|
52
218
|
const buildSurveyDiff = async (
|
|
53
219
|
app: Topo,
|
|
54
220
|
rootDir: string,
|
|
55
|
-
breakingOnly: boolean
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
'No saved surface map found. Run `trails topo export` first.'
|
|
63
|
-
)
|
|
64
|
-
);
|
|
221
|
+
breakingOnly: boolean,
|
|
222
|
+
against?: string | undefined
|
|
223
|
+
): Promise<Result<SurveyDiffReport, Error>> => {
|
|
224
|
+
const currentMap = deriveTopoGraph(app);
|
|
225
|
+
const previous = await readAgainstTopoGraph(rootDir, against);
|
|
226
|
+
if (previous.isErr()) {
|
|
227
|
+
return previous;
|
|
65
228
|
}
|
|
66
229
|
|
|
67
|
-
const diff =
|
|
230
|
+
const diff = deriveTopoGraphDiff(previous.value.map, currentMap);
|
|
68
231
|
return Result.ok(
|
|
69
232
|
breakingOnly
|
|
70
|
-
? formatDiff({
|
|
71
|
-
|
|
72
|
-
entries: diff.breaking,
|
|
73
|
-
info: [],
|
|
74
|
-
warnings: [],
|
|
75
|
-
})
|
|
76
|
-
: formatDiff(diff)
|
|
233
|
+
? formatDiff({ ...diff, info: [], warnings: [] }, previous.value.against)
|
|
234
|
+
: formatDiff(diff, previous.value.against)
|
|
77
235
|
);
|
|
78
236
|
};
|
|
79
237
|
|
|
80
|
-
const
|
|
238
|
+
const buildSurveyLookup = (
|
|
239
|
+
app: Topo,
|
|
240
|
+
entityId: string,
|
|
241
|
+
rootDir: string,
|
|
242
|
+
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
243
|
+
): Result<object, Error> => {
|
|
244
|
+
const matches = buildCurrentTopoMatches(app, entityId, {
|
|
245
|
+
rootDir,
|
|
246
|
+
surfaceLayerNames,
|
|
247
|
+
});
|
|
248
|
+
return Result.ok({ matches });
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const buildSurveyTrailDetail = (
|
|
252
|
+
app: Topo,
|
|
253
|
+
id: string,
|
|
254
|
+
rootDir: string,
|
|
255
|
+
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
256
|
+
): Result<object, Error> => {
|
|
257
|
+
const detail = buildCurrentTrailDetail(app, id, {
|
|
258
|
+
rootDir,
|
|
259
|
+
surfaceLayerNames,
|
|
260
|
+
});
|
|
261
|
+
return detail === undefined
|
|
262
|
+
? Result.err(new NotFoundError(`Trail not found: ${id}`))
|
|
263
|
+
: Result.ok(detail);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const buildSurveyResourceDetail = (
|
|
81
267
|
app: Topo,
|
|
82
|
-
|
|
268
|
+
id: string,
|
|
83
269
|
rootDir: string
|
|
84
270
|
): Result<object, Error> => {
|
|
85
|
-
const detail =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return Result.err(
|
|
90
|
-
new NotFoundError(`Trail or resource not found: ${trailId}`)
|
|
91
|
-
);
|
|
271
|
+
const detail = buildCurrentResourceDetail(app, id, { rootDir });
|
|
272
|
+
return detail === undefined
|
|
273
|
+
? Result.err(new NotFoundError(`Resource not found: ${id}`))
|
|
274
|
+
: Result.ok(detail);
|
|
92
275
|
};
|
|
93
276
|
|
|
94
|
-
const
|
|
277
|
+
const buildSurveySignalDetail = (
|
|
95
278
|
app: Topo,
|
|
279
|
+
id: string,
|
|
96
280
|
rootDir: string
|
|
97
|
-
):
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return Result.ok({
|
|
103
|
-
hash: exported.value.hash,
|
|
104
|
-
lockPath: exported.value.lockPath,
|
|
105
|
-
mapPath: exported.value.mapPath,
|
|
106
|
-
});
|
|
281
|
+
): Result<object, Error> => {
|
|
282
|
+
const detail = buildCurrentSignalDetail(app, id, { rootDir });
|
|
283
|
+
return detail === undefined
|
|
284
|
+
? Result.err(new NotFoundError(`Signal not found: ${id}`))
|
|
285
|
+
: Result.ok(detail);
|
|
107
286
|
};
|
|
108
287
|
|
|
288
|
+
const buildSurveySurfaceInventory = (app: Topo): Result<object, Error> =>
|
|
289
|
+
Result.ok(deriveShippedSurfaceProjectionInventory(app));
|
|
290
|
+
|
|
109
291
|
interface SurveyInput {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
generate: boolean;
|
|
114
|
-
openapi: boolean;
|
|
115
|
-
trailId?: string | undefined;
|
|
292
|
+
id?: string | undefined;
|
|
293
|
+
module?: string | undefined;
|
|
294
|
+
rootDir?: string | undefined;
|
|
116
295
|
}
|
|
117
296
|
|
|
118
|
-
type SurveyMode = '
|
|
297
|
+
type SurveyMode = 'lookup' | 'overview';
|
|
119
298
|
|
|
120
|
-
|
|
121
|
-
const modeChecks: readonly [(input: SurveyInput) => boolean, SurveyMode][] = [
|
|
122
|
-
[(i) => i.brief, 'brief'],
|
|
123
|
-
[(i) => i.diffSaved, 'diff'],
|
|
124
|
-
[(i) => Boolean(i.trailId), 'detail'],
|
|
125
|
-
[(i) => i.generate, 'generate'],
|
|
126
|
-
[(i) => i.openapi, 'openapi'],
|
|
127
|
-
];
|
|
299
|
+
type SurveyEnvelope = { readonly mode: SurveyMode } & Record<string, unknown>;
|
|
128
300
|
|
|
129
|
-
/** Determine which survey mode was requested, falling back to '
|
|
301
|
+
/** Determine which survey mode was requested, falling back to 'overview'. */
|
|
130
302
|
const deriveSurveyMode = (input: SurveyInput): SurveyMode =>
|
|
131
|
-
|
|
303
|
+
input.id === undefined || input.id === '' ? 'overview' : 'lookup';
|
|
132
304
|
|
|
133
305
|
type SurveyHandler = (
|
|
134
306
|
app: Topo,
|
|
135
307
|
input: SurveyInput,
|
|
136
|
-
rootDir: string
|
|
308
|
+
rootDir: string,
|
|
309
|
+
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
137
310
|
) => Result<object, Error> | Promise<Result<object, Error>>;
|
|
138
311
|
|
|
139
312
|
/** Handlers keyed by survey mode. */
|
|
140
313
|
const surveyHandlers: Record<SurveyMode, SurveyHandler> = {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
buildSurveyDiff(app, rootDir, input.breakingOnly),
|
|
147
|
-
generate: (app, _input, rootDir) => buildSurveyGenerate(app, rootDir),
|
|
148
|
-
list: (app, _input, rootDir) =>
|
|
314
|
+
lookup: (app, input, rootDir, surfaceLayerNames) =>
|
|
315
|
+
input.id === undefined || input.id === ''
|
|
316
|
+
? Result.err(new ValidationError('Survey lookup requires an id'))
|
|
317
|
+
: buildSurveyLookup(app, input.id, rootDir, surfaceLayerNames),
|
|
318
|
+
overview: (app, _input, rootDir) =>
|
|
149
319
|
Result.ok(buildCurrentTopoList(app, { rootDir })),
|
|
150
|
-
openapi: (app) => Result.ok(deriveOpenApiSpec(app)),
|
|
151
320
|
};
|
|
152
321
|
|
|
322
|
+
const envelopeSurveyValue = (
|
|
323
|
+
mode: SurveyMode,
|
|
324
|
+
value: object
|
|
325
|
+
): SurveyEnvelope => ({ ...value, mode });
|
|
326
|
+
|
|
153
327
|
/** Dispatch to the appropriate survey sub-command based on input flags. */
|
|
154
|
-
const dispatchSurvey = (
|
|
328
|
+
const dispatchSurvey = async (
|
|
155
329
|
app: Topo,
|
|
156
330
|
input: SurveyInput,
|
|
157
|
-
rootDir: string
|
|
158
|
-
|
|
331
|
+
rootDir: string,
|
|
332
|
+
surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined
|
|
333
|
+
): Promise<Result<SurveyEnvelope, Error>> => {
|
|
159
334
|
const mode = deriveSurveyMode(input);
|
|
160
335
|
const handler = surveyHandlers[mode];
|
|
161
|
-
|
|
336
|
+
const result = await handler(app, input, rootDir, surfaceLayerNames);
|
|
337
|
+
if (result.isErr()) {
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
return Result.ok(envelopeSurveyValue(mode, result.value));
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const detailInputSchema = z.object({
|
|
344
|
+
id: z.string().describe('Trail, resource, or signal ID'),
|
|
345
|
+
module: z.string().optional().describe('Path to the app module'),
|
|
346
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const withFreshSurveyApp = async <T>(
|
|
350
|
+
input: { readonly module?: string | undefined },
|
|
351
|
+
rootDir: string,
|
|
352
|
+
consume: (app: Topo) => Promise<Result<T, Error>> | Result<T, Error>
|
|
353
|
+
): Promise<Result<T, Error>> => {
|
|
354
|
+
const leaseResult = await tryLoadFreshAppLease(input.module, rootDir);
|
|
355
|
+
if (leaseResult.isErr()) {
|
|
356
|
+
return Result.err(leaseResult.error);
|
|
357
|
+
}
|
|
358
|
+
const lease = leaseResult.value;
|
|
359
|
+
try {
|
|
360
|
+
return await consume(lease.app);
|
|
361
|
+
} finally {
|
|
362
|
+
lease.release();
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const withResolvedSurveyApp = async <T>(
|
|
367
|
+
input: {
|
|
368
|
+
readonly module?: string | undefined;
|
|
369
|
+
readonly rootDir?: string | undefined;
|
|
370
|
+
},
|
|
371
|
+
cwd: string | undefined,
|
|
372
|
+
consume: (
|
|
373
|
+
app: Topo,
|
|
374
|
+
rootDir: string
|
|
375
|
+
) => Promise<Result<T, Error>> | Result<T, Error>
|
|
376
|
+
): Promise<Result<T, Error>> => {
|
|
377
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, cwd);
|
|
378
|
+
if (rootDirResult.isErr()) {
|
|
379
|
+
return Result.err(rootDirResult.error);
|
|
380
|
+
}
|
|
381
|
+
const rootDir = rootDirResult.value;
|
|
382
|
+
return withFreshSurveyApp(input, rootDir, (app) => consume(app, rootDir));
|
|
162
383
|
};
|
|
163
384
|
|
|
385
|
+
const moduleInputSchema = z.object({
|
|
386
|
+
module: z.string().optional().describe('Path to the app module'),
|
|
387
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const diffEntryOutput = z.object({
|
|
391
|
+
change: z.enum(['added', 'removed', 'modified']),
|
|
392
|
+
details: z.array(z.string()).readonly(),
|
|
393
|
+
id: z.string(),
|
|
394
|
+
kind: z.enum(['contour', 'trail', 'signal', 'resource']),
|
|
395
|
+
severity: z.enum(['info', 'warning', 'breaking']),
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const diffOutput = z.object({
|
|
399
|
+
against: z.string(),
|
|
400
|
+
breaking: z.array(diffEntryOutput),
|
|
401
|
+
hasBreaking: z.boolean(),
|
|
402
|
+
info: z.array(diffEntryOutput),
|
|
403
|
+
mode: z.literal('diff'),
|
|
404
|
+
warnings: z.array(diffEntryOutput),
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const surveyMatchOutput = z.discriminatedUnion('kind', [
|
|
408
|
+
z.object({
|
|
409
|
+
detail: trailDetailOutput,
|
|
410
|
+
kind: z.literal('trail'),
|
|
411
|
+
}),
|
|
412
|
+
z.object({
|
|
413
|
+
detail: resourceDetailOutput,
|
|
414
|
+
kind: z.literal('resource'),
|
|
415
|
+
}),
|
|
416
|
+
z.object({
|
|
417
|
+
detail: signalDetailOutput,
|
|
418
|
+
kind: z.literal('signal'),
|
|
419
|
+
}),
|
|
420
|
+
]);
|
|
421
|
+
|
|
164
422
|
// ---------------------------------------------------------------------------
|
|
165
423
|
// Trail definition
|
|
166
424
|
// ---------------------------------------------------------------------------
|
|
167
425
|
|
|
168
426
|
export const surveyTrail = trail('survey', {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// module graph that a prior import may have frozen. Other modes read
|
|
175
|
-
// the in-memory topo and benefit from the standard import cache.
|
|
176
|
-
//
|
|
177
|
-
// For diff specifically, use a disposable lease rather than retained
|
|
178
|
-
// fresh mirrors — the returned diff result is serialisable data, not
|
|
179
|
-
// a Topo reference with deferred imports, so the mirror can be
|
|
180
|
-
// released the moment dispatchSurvey returns. That keeps MCP/dev
|
|
181
|
-
// sessions that poll diff repeatedly from growing .trails-tmp/
|
|
182
|
-
// without bound.
|
|
183
|
-
if (mode === 'diff') {
|
|
184
|
-
const lease = await loadFreshAppLease(input.module, rootDir);
|
|
185
|
-
try {
|
|
186
|
-
return await dispatchSurvey(lease.app, input, rootDir);
|
|
187
|
-
} finally {
|
|
188
|
-
lease.release();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const app = await loadApp(input.module, rootDir);
|
|
193
|
-
return dispatchSurvey(app, input, rootDir);
|
|
194
|
-
},
|
|
427
|
+
args: ['id'],
|
|
428
|
+
blaze: async (input, ctx) =>
|
|
429
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir) =>
|
|
430
|
+
dispatchSurvey(app, input, rootDir, readSurfaceLayerNamesFromContext(ctx))
|
|
431
|
+
),
|
|
195
432
|
description: 'Full topo introspection',
|
|
196
433
|
examples: [
|
|
197
434
|
{
|
|
198
|
-
description: '
|
|
199
|
-
input:
|
|
200
|
-
name: '
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
description: 'Quick capability summary with counts and feature flags',
|
|
204
|
-
input: { brief: true, module: './src/app.ts' },
|
|
205
|
-
name: 'Brief capability report',
|
|
435
|
+
description: 'Show all registered trails, resources, and signals',
|
|
436
|
+
input: createIsolatedExampleInput('survey-overview'),
|
|
437
|
+
name: 'Overview',
|
|
206
438
|
},
|
|
207
439
|
{
|
|
208
|
-
description: '
|
|
209
|
-
input: {
|
|
210
|
-
name: '
|
|
440
|
+
description: 'Find every trail, resource, or signal with a matching ID',
|
|
441
|
+
input: { ...createIsolatedExampleInput('survey-lookup'), id: 'survey' },
|
|
442
|
+
name: 'Lookup by ID',
|
|
211
443
|
},
|
|
212
444
|
],
|
|
213
445
|
input: z.object({
|
|
214
|
-
|
|
215
|
-
.
|
|
216
|
-
.
|
|
217
|
-
.describe('
|
|
218
|
-
brief: z.boolean().default(false).describe('Quick capability summary'),
|
|
219
|
-
diffSaved: z
|
|
220
|
-
.boolean()
|
|
221
|
-
.default(false)
|
|
222
|
-
.describe('Diff against the saved local surface map'),
|
|
223
|
-
generate: z
|
|
224
|
-
.boolean()
|
|
225
|
-
.default(false)
|
|
226
|
-
.describe('Generate surface map and lock file'),
|
|
446
|
+
id: z
|
|
447
|
+
.string()
|
|
448
|
+
.optional()
|
|
449
|
+
.describe('Trail, resource, or signal ID to look up'),
|
|
227
450
|
module: z.string().optional().describe('Path to the app module'),
|
|
228
|
-
|
|
229
|
-
trailId: z.string().optional().describe('Trail ID for detail view'),
|
|
451
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
230
452
|
}),
|
|
231
453
|
intent: 'read',
|
|
232
|
-
output: z.
|
|
454
|
+
output: z.discriminatedUnion('mode', [
|
|
233
455
|
z.object({
|
|
456
|
+
activation: activationOverviewOutput,
|
|
234
457
|
count: z.number(),
|
|
235
458
|
entries: z.array(
|
|
236
459
|
z.object({
|
|
460
|
+
activatedBy: z.array(z.string()).readonly(),
|
|
461
|
+
activates: z.array(z.string()).readonly(),
|
|
237
462
|
examples: z.number(),
|
|
238
463
|
id: z.string(),
|
|
239
464
|
kind: z.string(),
|
|
240
465
|
safety: z.string(),
|
|
241
466
|
})
|
|
242
467
|
),
|
|
468
|
+
mode: z.literal('overview'),
|
|
243
469
|
resourceCount: z.number(),
|
|
244
470
|
resources: z.array(
|
|
245
471
|
z.object({
|
|
@@ -251,71 +477,179 @@ export const surveyTrail = trail('survey', {
|
|
|
251
477
|
usedBy: z.array(z.string()),
|
|
252
478
|
})
|
|
253
479
|
),
|
|
480
|
+
signalCount: z.number(),
|
|
481
|
+
signals: z.array(
|
|
482
|
+
z.object({
|
|
483
|
+
consumers: z.array(z.string()).readonly(),
|
|
484
|
+
description: z.string().nullable(),
|
|
485
|
+
examples: z.number(),
|
|
486
|
+
from: z.array(z.string()).readonly(),
|
|
487
|
+
id: z.string(),
|
|
488
|
+
kind: z.literal('signal'),
|
|
489
|
+
payloadSchema: z.boolean(),
|
|
490
|
+
producers: z.array(z.string()).readonly(),
|
|
491
|
+
})
|
|
492
|
+
),
|
|
254
493
|
}),
|
|
255
494
|
z.object({
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
detours: z.boolean(),
|
|
259
|
-
examples: z.boolean(),
|
|
260
|
-
outputSchemas: z.boolean(),
|
|
261
|
-
resources: z.boolean(),
|
|
262
|
-
signals: z.boolean(),
|
|
263
|
-
}),
|
|
264
|
-
name: z.string(),
|
|
265
|
-
resources: z.number(),
|
|
266
|
-
signals: z.number(),
|
|
267
|
-
trails: z.number(),
|
|
268
|
-
version: z.string(),
|
|
269
|
-
}),
|
|
270
|
-
z.object({
|
|
271
|
-
breaking: z.array(z.unknown()),
|
|
272
|
-
hasBreaking: z.boolean(),
|
|
273
|
-
info: z.array(z.unknown()),
|
|
274
|
-
warnings: z.array(z.unknown()),
|
|
275
|
-
}),
|
|
276
|
-
z.object({
|
|
277
|
-
crosses: z.array(z.string()),
|
|
278
|
-
description: z.unknown().nullable(),
|
|
279
|
-
detours: z.unknown().nullable(),
|
|
280
|
-
examples: z.array(z.unknown()),
|
|
281
|
-
id: z.string(),
|
|
282
|
-
intent: z.enum(['read', 'write', 'destroy']),
|
|
283
|
-
kind: z.string(),
|
|
284
|
-
resources: z.array(z.string()),
|
|
285
|
-
safety: z.string(),
|
|
286
|
-
}),
|
|
287
|
-
z.object({
|
|
288
|
-
description: z.string().nullable(),
|
|
289
|
-
health: z.enum(['available', 'none']),
|
|
290
|
-
id: z.string(),
|
|
291
|
-
kind: z.literal('resource'),
|
|
292
|
-
lifetime: z.literal('singleton'),
|
|
293
|
-
usedBy: z.array(z.string()),
|
|
294
|
-
}),
|
|
295
|
-
z.object({
|
|
296
|
-
hash: z.string(),
|
|
297
|
-
lockPath: z.string(),
|
|
298
|
-
mapPath: z.string(),
|
|
299
|
-
}),
|
|
300
|
-
z.object({
|
|
301
|
-
components: z.object({
|
|
302
|
-
schemas: z.record(z.string(), z.unknown()),
|
|
303
|
-
}),
|
|
304
|
-
info: z.object({
|
|
305
|
-
description: z.string().optional(),
|
|
306
|
-
title: z.string(),
|
|
307
|
-
version: z.string(),
|
|
308
|
-
}),
|
|
309
|
-
openapi: z.literal('3.1.0'),
|
|
310
|
-
paths: z.record(z.string(), z.record(z.string(), z.unknown())),
|
|
311
|
-
servers: z
|
|
312
|
-
.array(
|
|
313
|
-
z.object({
|
|
314
|
-
description: z.string().optional(),
|
|
315
|
-
url: z.string(),
|
|
316
|
-
})
|
|
317
|
-
)
|
|
318
|
-
.optional(),
|
|
495
|
+
matches: z.array(surveyMatchOutput),
|
|
496
|
+
mode: z.literal('lookup'),
|
|
319
497
|
}),
|
|
320
498
|
]),
|
|
321
499
|
});
|
|
500
|
+
|
|
501
|
+
export const surveyBriefTrail = trail('survey.brief', {
|
|
502
|
+
blaze: async (input, ctx) =>
|
|
503
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir) =>
|
|
504
|
+
Result.ok(buildCurrentTopoBrief(app, { rootDir }))
|
|
505
|
+
),
|
|
506
|
+
description: 'Summarize topo capabilities',
|
|
507
|
+
examples: [
|
|
508
|
+
{
|
|
509
|
+
description: 'Show counts and feature flags',
|
|
510
|
+
input: createIsolatedExampleInput('survey-brief'),
|
|
511
|
+
name: 'Brief capability report',
|
|
512
|
+
},
|
|
513
|
+
],
|
|
514
|
+
input: moduleInputSchema,
|
|
515
|
+
intent: 'read',
|
|
516
|
+
output: briefReportSchema,
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
export const surveySurfacesTrail = trail('survey.surfaces', {
|
|
520
|
+
blaze: async (input, ctx) =>
|
|
521
|
+
withResolvedSurveyApp(input, ctx.cwd, (app) =>
|
|
522
|
+
buildSurveySurfaceInventory(app)
|
|
523
|
+
),
|
|
524
|
+
description: 'Inventory shipped surface projections',
|
|
525
|
+
examples: [
|
|
526
|
+
{
|
|
527
|
+
description: 'Show CLI, MCP, and HTTP projections for public trails',
|
|
528
|
+
input: createIsolatedExampleInput('survey-surfaces'),
|
|
529
|
+
name: 'Shipped surface inventory',
|
|
530
|
+
},
|
|
531
|
+
],
|
|
532
|
+
input: moduleInputSchema,
|
|
533
|
+
intent: 'read',
|
|
534
|
+
output: shippedSurfaceInventoryOutput,
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
export const surveyDiffTrail = trail('survey.diff', {
|
|
538
|
+
blaze: async (input, ctx) =>
|
|
539
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir) =>
|
|
540
|
+
buildSurveyDiff(app, rootDir, input.breakingOnly, input.against)
|
|
541
|
+
),
|
|
542
|
+
description: 'Diff the current topo against a saved TopoGraph',
|
|
543
|
+
examples: [
|
|
544
|
+
{
|
|
545
|
+
description: 'Compare current topo to a saved TopoGraph directory',
|
|
546
|
+
input: createDiffExampleInput(),
|
|
547
|
+
name: 'Diff against baseline',
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
description: 'Reject an empty saved map target',
|
|
551
|
+
error: 'ValidationError',
|
|
552
|
+
input: { against: '' },
|
|
553
|
+
name: 'Reject empty diff target',
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
description: 'Reject an empty target before filtering breaking drift',
|
|
557
|
+
error: 'ValidationError',
|
|
558
|
+
input: {
|
|
559
|
+
against: '',
|
|
560
|
+
breakingOnly: true,
|
|
561
|
+
},
|
|
562
|
+
name: 'Reject empty breaking-only target',
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
input: z.object({
|
|
566
|
+
against: z
|
|
567
|
+
.string()
|
|
568
|
+
.min(1)
|
|
569
|
+
.optional()
|
|
570
|
+
.describe(
|
|
571
|
+
'Saved TopoGraph target: "saved", a workspace path (topo.lock, .json file, or directory with topo.lock), then a pin/snapshot id'
|
|
572
|
+
),
|
|
573
|
+
breakingOnly: z
|
|
574
|
+
.boolean()
|
|
575
|
+
.default(false)
|
|
576
|
+
.describe('Only show breaking changes'),
|
|
577
|
+
module: z.string().optional().describe('Path to the app module'),
|
|
578
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
579
|
+
}),
|
|
580
|
+
intent: 'read',
|
|
581
|
+
output: diffOutput,
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
export const surveyTrailDetailTrail = trail('survey.trail', {
|
|
585
|
+
args: ['id'],
|
|
586
|
+
blaze: async (input, ctx) =>
|
|
587
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir) =>
|
|
588
|
+
buildSurveyTrailDetail(
|
|
589
|
+
app,
|
|
590
|
+
input.id,
|
|
591
|
+
rootDir,
|
|
592
|
+
readSurfaceLayerNamesFromContext(ctx)
|
|
593
|
+
)
|
|
594
|
+
),
|
|
595
|
+
description: 'Inspect one trail by ID',
|
|
596
|
+
examples: [
|
|
597
|
+
{
|
|
598
|
+
description: 'Show trail contract detail',
|
|
599
|
+
input: {
|
|
600
|
+
...createIsolatedExampleInput('survey-trail-detail'),
|
|
601
|
+
id: 'survey',
|
|
602
|
+
},
|
|
603
|
+
name: 'Trail detail',
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
input: detailInputSchema,
|
|
607
|
+
intent: 'read',
|
|
608
|
+
output: trailDetailOutput,
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
export const surveyResourceTrail = trail('survey.resource', {
|
|
612
|
+
args: ['id'],
|
|
613
|
+
blaze: async (input, ctx) =>
|
|
614
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir) =>
|
|
615
|
+
buildSurveyResourceDetail(app, input.id, rootDir)
|
|
616
|
+
),
|
|
617
|
+
description: 'Inspect one resource by ID',
|
|
618
|
+
examples: [
|
|
619
|
+
{
|
|
620
|
+
description: 'Show resource usage detail',
|
|
621
|
+
error: 'NotFoundError',
|
|
622
|
+
input: {
|
|
623
|
+
...createIsolatedExampleInput('survey-resource-detail'),
|
|
624
|
+
id: 'db.main',
|
|
625
|
+
},
|
|
626
|
+
name: 'Resource detail',
|
|
627
|
+
},
|
|
628
|
+
],
|
|
629
|
+
input: detailInputSchema,
|
|
630
|
+
intent: 'read',
|
|
631
|
+
output: resourceDetailOutput,
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
export const surveySignalTrail = trail('survey.signal', {
|
|
635
|
+
args: ['id'],
|
|
636
|
+
blaze: async (input, ctx) =>
|
|
637
|
+
withResolvedSurveyApp(input, ctx.cwd, (app, rootDir) =>
|
|
638
|
+
buildSurveySignalDetail(app, input.id, rootDir)
|
|
639
|
+
),
|
|
640
|
+
description: 'Inspect one signal by ID',
|
|
641
|
+
examples: [
|
|
642
|
+
{
|
|
643
|
+
description: 'Show signal producer and consumer detail',
|
|
644
|
+
error: 'NotFoundError',
|
|
645
|
+
input: {
|
|
646
|
+
...createIsolatedExampleInput('survey-signal-detail'),
|
|
647
|
+
id: 'hello.greeted',
|
|
648
|
+
},
|
|
649
|
+
name: 'Signal detail',
|
|
650
|
+
},
|
|
651
|
+
],
|
|
652
|
+
input: detailInputSchema,
|
|
653
|
+
intent: 'read',
|
|
654
|
+
output: signalDetailOutput,
|
|
655
|
+
});
|