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