@ontrails/trails 1.0.0-beta.18 → 1.0.0-beta.19
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 +117 -0
- package/README.md +7 -10
- package/package.json +13 -12
- package/src/app.ts +14 -4
- package/src/cli.ts +16 -0
- package/src/lifecycle-source-io.ts +33 -0
- package/src/project-writes.ts +62 -5
- package/src/retired-topo-command.ts +36 -0
- package/src/run-adapter-check.ts +76 -0
- package/src/run-collision.ts +1 -0
- package/src/trails/adapter-check.ts +244 -0
- package/src/trails/add-surface.ts +18 -18
- package/src/trails/add-trail.ts +3 -2
- package/src/trails/add-verify.ts +30 -6
- package/src/trails/{topo-compile.ts → compile.ts} +16 -8
- package/src/trails/completions-complete.ts +1 -1
- package/src/trails/create-adapter.ts +1084 -0
- package/src/trails/create-scaffold.ts +243 -29
- package/src/trails/create.ts +118 -17
- package/src/trails/deprecate.ts +59 -0
- package/src/trails/dev-clean.ts +2 -2
- package/src/trails/dev-reset.ts +2 -2
- package/src/trails/dev-stats.ts +1 -1
- package/src/trails/doctor.ts +56 -0
- package/src/trails/draft-promote.ts +1 -0
- package/src/trails/guide.ts +2 -2
- package/src/trails/revise.ts +53 -0
- package/src/trails/run-example.ts +12 -7
- package/src/trails/run-examples.ts +3 -3
- package/src/trails/run.ts +7 -4
- package/src/trails/survey.ts +332 -25
- package/src/trails/topo-history.ts +1 -1
- package/src/trails/topo-output-schemas.ts +30 -1
- package/src/trails/topo-pin.ts +3 -2
- package/src/trails/topo-read-support.ts +49 -8
- package/src/trails/topo-reports.ts +39 -22
- package/src/trails/topo-store-support.ts +62 -16
- package/src/trails/topo-support.ts +1 -1
- package/src/trails/topo-unpin.ts +2 -2
- package/src/trails/topo.ts +2 -2
- package/src/trails/{topo-verify.ts → validate.ts} +7 -7
- package/src/trails/version-lifecycle-support.ts +945 -0
- package/src/trails/warden-guide.ts +8 -0
- package/src/trails/warden.ts +18 -2
- package/src/versions.ts +4 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `adapter.check` trail -- Local adapter authoring readiness checks.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { adapterTargetPlacements, checkAdapters } from '@ontrails/adapter-kit';
|
|
6
|
+
import type { AdapterCheckReport } from '@ontrails/adapter-kit';
|
|
7
|
+
import { isPlainObject, Result, trail, ValidationError } from '@ontrails/core';
|
|
8
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
9
|
+
import { join, relative } from 'node:path';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
13
|
+
|
|
14
|
+
const adapterCheckInputSchema = z.object({
|
|
15
|
+
rootDir: z.string().optional().describe('Root directory to scan'),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const adapterPlacementSchema = z.enum(adapterTargetPlacements);
|
|
19
|
+
|
|
20
|
+
const adapterCheckDiagnosticSchema = z.object({
|
|
21
|
+
code: z.string().describe('Stable adapter diagnostic code'),
|
|
22
|
+
message: z.string(),
|
|
23
|
+
packageJsonPath: z.string(),
|
|
24
|
+
packageName: z.string().optional(),
|
|
25
|
+
placement: adapterPlacementSchema.optional(),
|
|
26
|
+
severity: z.enum(['error', 'warn']),
|
|
27
|
+
target: z.string().optional(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const adapterCheckSubjectSchema = z.object({
|
|
31
|
+
conformanceTestPaths: z.array(z.string()).readonly(),
|
|
32
|
+
key: z.string(),
|
|
33
|
+
ownerPackage: z.string(),
|
|
34
|
+
packageJsonPath: z.string(),
|
|
35
|
+
packageName: z.string(),
|
|
36
|
+
packageRoot: z.string(),
|
|
37
|
+
placement: adapterPlacementSchema,
|
|
38
|
+
target: z.string(),
|
|
39
|
+
targetKey: z.string(),
|
|
40
|
+
testingImport: z.string().optional(),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const adapterTargetSchema = z.object({
|
|
44
|
+
conformance: z
|
|
45
|
+
.object({
|
|
46
|
+
adapterType: z.string(),
|
|
47
|
+
casesFactory: z.string(),
|
|
48
|
+
runner: z.string(),
|
|
49
|
+
})
|
|
50
|
+
.optional(),
|
|
51
|
+
key: z.string(),
|
|
52
|
+
ownerPackage: z.string(),
|
|
53
|
+
packageJsonPath: z.string(),
|
|
54
|
+
packageRoot: z.string(),
|
|
55
|
+
placements: z.array(adapterPlacementSchema).readonly(),
|
|
56
|
+
supportExportTarget: z.string().optional(),
|
|
57
|
+
supportImport: z.string().optional(),
|
|
58
|
+
target: z.string(),
|
|
59
|
+
testingExportTarget: z.string().optional(),
|
|
60
|
+
testingImport: z.string().optional(),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const adapterCheckOutputSchema = z.object({
|
|
64
|
+
diagnostics: z.array(adapterCheckDiagnosticSchema).readonly(),
|
|
65
|
+
formatted: z.string(),
|
|
66
|
+
passed: z.boolean(),
|
|
67
|
+
subjects: z.array(adapterCheckSubjectSchema).readonly(),
|
|
68
|
+
targets: z.array(adapterTargetSchema).readonly(),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const relativeToRoot = (rootDir: string, path: string): string => {
|
|
72
|
+
const normalized = relative(rootDir, path).replaceAll('\\', '/');
|
|
73
|
+
return normalized.length === 0 || normalized.startsWith('..')
|
|
74
|
+
? path
|
|
75
|
+
: normalized;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const workspacePatternsFromManifest = (
|
|
79
|
+
manifest: Readonly<Record<string, unknown>>
|
|
80
|
+
): readonly string[] => {
|
|
81
|
+
const { workspaces } = manifest;
|
|
82
|
+
if (Array.isArray(workspaces)) {
|
|
83
|
+
return workspaces.filter(
|
|
84
|
+
(pattern): pattern is string => typeof pattern === 'string'
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const packages = isPlainObject(workspaces)
|
|
89
|
+
? workspaces['packages']
|
|
90
|
+
: undefined;
|
|
91
|
+
return Array.isArray(packages)
|
|
92
|
+
? packages.filter(
|
|
93
|
+
(pattern): pattern is string => typeof pattern === 'string'
|
|
94
|
+
)
|
|
95
|
+
: [];
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const readWorkspaceManifest = (
|
|
99
|
+
packageJsonPath: string
|
|
100
|
+
): Result<Readonly<Record<string, unknown>>, ValidationError> => {
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
103
|
+
return isPlainObject(parsed)
|
|
104
|
+
? Result.ok(parsed)
|
|
105
|
+
: Result.err(
|
|
106
|
+
new ValidationError(
|
|
107
|
+
`adapter.check root package.json must contain a JSON object: "${packageJsonPath}"`
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return Result.err(
|
|
112
|
+
new ValidationError(
|
|
113
|
+
`adapter.check could not read root package.json: "${packageJsonPath}"`,
|
|
114
|
+
error instanceof Error ? { cause: error } : undefined
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const validateAdapterCheckRoot = (
|
|
121
|
+
rootDir: string
|
|
122
|
+
): Result<void, ValidationError> => {
|
|
123
|
+
if (!existsSync(rootDir)) {
|
|
124
|
+
return Result.err(
|
|
125
|
+
new ValidationError(`adapter.check rootDir does not exist: "${rootDir}"`)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!statSync(rootDir).isDirectory()) {
|
|
130
|
+
return Result.err(
|
|
131
|
+
new ValidationError(
|
|
132
|
+
`adapter.check rootDir must be a directory: "${rootDir}"`
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const packageJsonPath = join(rootDir, 'package.json');
|
|
138
|
+
if (!existsSync(packageJsonPath)) {
|
|
139
|
+
return Result.err(
|
|
140
|
+
new ValidationError(
|
|
141
|
+
`adapter.check rootDir must contain a package.json workspace manifest: "${packageJsonPath}"`
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const manifest = readWorkspaceManifest(packageJsonPath);
|
|
147
|
+
if (manifest.isErr()) {
|
|
148
|
+
return Result.err(manifest.error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (workspacePatternsFromManifest(manifest.value).length === 0) {
|
|
152
|
+
return Result.err(
|
|
153
|
+
new ValidationError(
|
|
154
|
+
`adapter.check root package.json must declare workspace packages: "${packageJsonPath}"`
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return Result.ok();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const formatAdapterCheckReport = (
|
|
163
|
+
report: AdapterCheckReport,
|
|
164
|
+
rootDir: string
|
|
165
|
+
): string => {
|
|
166
|
+
const passed = report.diagnostics.length === 0;
|
|
167
|
+
const lines = [
|
|
168
|
+
'## Adapter Check Report',
|
|
169
|
+
'',
|
|
170
|
+
`Result: ${passed ? 'PASS' : 'FAIL'}`,
|
|
171
|
+
`Targets: ${report.targets.length}`,
|
|
172
|
+
`Adapters: ${report.subjects.length}`,
|
|
173
|
+
`Diagnostics: ${report.diagnostics.length}`,
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
if (report.targets.length > 0) {
|
|
177
|
+
lines.push('', '### Targets');
|
|
178
|
+
for (const target of report.targets) {
|
|
179
|
+
lines.push(
|
|
180
|
+
`- ${target.key} (${target.placements.join(', ')}) from ${relativeToRoot(rootDir, target.packageJsonPath)}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (report.subjects.length > 0) {
|
|
186
|
+
lines.push('', '### Adapters');
|
|
187
|
+
for (const subject of report.subjects) {
|
|
188
|
+
const conformance =
|
|
189
|
+
subject.conformanceTestPaths.length === 0
|
|
190
|
+
? 'no conformance tests'
|
|
191
|
+
: `${subject.conformanceTestPaths.length} conformance test(s)`;
|
|
192
|
+
lines.push(
|
|
193
|
+
`- ${subject.packageName} -> ${subject.targetKey} (${subject.placement}, ${conformance})`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (report.diagnostics.length > 0) {
|
|
199
|
+
lines.push('', '### Diagnostics');
|
|
200
|
+
for (const diagnostic of report.diagnostics) {
|
|
201
|
+
lines.push(
|
|
202
|
+
`- ${diagnostic.severity.toUpperCase()} ${diagnostic.code} ${relativeToRoot(rootDir, diagnostic.packageJsonPath)}: ${diagnostic.message}`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return lines.join('\n');
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export const adapterCheckTrail = trail('adapter.check', {
|
|
211
|
+
blaze: (input, ctx) => {
|
|
212
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
213
|
+
if (rootDirResult.isErr()) {
|
|
214
|
+
return rootDirResult;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const rootDir = rootDirResult.value;
|
|
218
|
+
const validRoot = validateAdapterCheckRoot(rootDir);
|
|
219
|
+
if (validRoot.isErr()) {
|
|
220
|
+
return validRoot;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const report = checkAdapters(rootDir);
|
|
224
|
+
|
|
225
|
+
return Result.ok({
|
|
226
|
+
diagnostics: [...report.diagnostics],
|
|
227
|
+
formatted: formatAdapterCheckReport(report, rootDir),
|
|
228
|
+
passed: report.diagnostics.length === 0,
|
|
229
|
+
subjects: [...report.subjects],
|
|
230
|
+
targets: [...report.targets],
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
description: 'Check adapter authoring readiness',
|
|
234
|
+
examples: [
|
|
235
|
+
{
|
|
236
|
+
input: {},
|
|
237
|
+
name: 'Check adapters in the current workspace',
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
input: adapterCheckInputSchema,
|
|
241
|
+
intent: 'read',
|
|
242
|
+
output: adapterCheckOutputSchema,
|
|
243
|
+
permit: 'public',
|
|
244
|
+
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { existsSync } from 'node:fs';
|
|
8
8
|
import { basename, resolve } from 'node:path';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { Result, trail } from '@ontrails/core';
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
|
|
13
13
|
import {
|
|
@@ -21,11 +21,14 @@ import { findTopoPath } from './project.js';
|
|
|
21
21
|
type Surface = 'cli' | 'http' | 'mcp';
|
|
22
22
|
|
|
23
23
|
const generateCliEntry = (appImportPath: string): string =>
|
|
24
|
-
`import {
|
|
24
|
+
`import { devPermitPreset, permitPreset } from '@ontrails/cli';
|
|
25
|
+
import { surface } from '@ontrails/commander';
|
|
25
26
|
|
|
26
27
|
import { app } from '${appImportPath}';
|
|
27
28
|
|
|
28
|
-
await surface(app
|
|
29
|
+
await surface(app, {
|
|
30
|
+
presets: [permitPreset(), devPermitPreset()],
|
|
31
|
+
});
|
|
29
32
|
`;
|
|
30
33
|
|
|
31
34
|
const generateMcpEntry = (appImportPath: string): string =>
|
|
@@ -134,29 +137,25 @@ export const addSurface = trail('add.surface', {
|
|
|
134
137
|
const entryFile = getEntryFile(surface);
|
|
135
138
|
const entryExists = projectPathExists(cwd, entryFile);
|
|
136
139
|
if (entryExists.isErr()) {
|
|
137
|
-
return
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (entryExists.value) {
|
|
141
|
-
return Result.err(
|
|
142
|
-
new AlreadyExistsError(
|
|
143
|
-
`${surface.toUpperCase()} surface already exists. Nothing to do.`
|
|
144
|
-
)
|
|
145
|
-
);
|
|
140
|
+
return entryExists;
|
|
146
141
|
}
|
|
147
142
|
|
|
148
|
-
|
|
149
|
-
if (
|
|
150
|
-
|
|
143
|
+
let created: string | null = null;
|
|
144
|
+
if (!entryExists.value) {
|
|
145
|
+
const written = await writeSurfaceEntry(cwd, surface);
|
|
146
|
+
if (written.isErr()) {
|
|
147
|
+
return written;
|
|
148
|
+
}
|
|
149
|
+
created = entryFile;
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
const dependency = await updatePkgJsonForSurface(cwd, surface);
|
|
154
153
|
if (dependency.isErr()) {
|
|
155
|
-
return
|
|
154
|
+
return dependency;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
return Result.ok({
|
|
159
|
-
created
|
|
158
|
+
created,
|
|
160
159
|
dependency: dependency.value,
|
|
161
160
|
});
|
|
162
161
|
},
|
|
@@ -166,7 +165,8 @@ export const addSurface = trail('add.surface', {
|
|
|
166
165
|
surface: z.enum(['cli', 'http', 'mcp']).describe('Surface to add'),
|
|
167
166
|
}),
|
|
168
167
|
output: z.object({
|
|
169
|
-
created: z.string(),
|
|
168
|
+
created: z.string().nullable(),
|
|
170
169
|
dependency: z.string(),
|
|
171
170
|
}),
|
|
171
|
+
permit: { scopes: ['project:write'] },
|
|
172
172
|
});
|
package/src/trails/add-trail.ts
CHANGED
|
@@ -83,7 +83,7 @@ export const addTrail = trail('add.trail', {
|
|
|
83
83
|
const { id } = input;
|
|
84
84
|
const validated = validateTrailId(id);
|
|
85
85
|
if (validated.isErr()) {
|
|
86
|
-
return
|
|
86
|
+
return validated;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
const moduleName = trailIdToModuleName(validated.value);
|
|
@@ -108,7 +108,7 @@ export const addTrail = trail('add.trail', {
|
|
|
108
108
|
for (const [relativePath, content] of files) {
|
|
109
109
|
const written = await writeProjectFile(cwd, relativePath, content);
|
|
110
110
|
if (written.isErr()) {
|
|
111
|
-
return
|
|
111
|
+
return written;
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -137,4 +137,5 @@ export const addTrail = trail('add.trail', {
|
|
|
137
137
|
output: z.object({
|
|
138
138
|
created: z.array(z.string()),
|
|
139
139
|
}),
|
|
140
|
+
permit: { scopes: ['project:write'] },
|
|
140
141
|
});
|
package/src/trails/add-verify.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { z } from 'zod';
|
|
|
10
10
|
import {
|
|
11
11
|
PROJECT_NAME_MESSAGE,
|
|
12
12
|
PROJECT_NAME_PATTERN,
|
|
13
|
+
projectPathExists,
|
|
13
14
|
resolveProjectDir,
|
|
14
15
|
resolveProjectPath,
|
|
15
16
|
writeProjectFile,
|
|
@@ -24,10 +25,24 @@ import {
|
|
|
24
25
|
// ---------------------------------------------------------------------------
|
|
25
26
|
|
|
26
27
|
const generateTestFile = (): string =>
|
|
27
|
-
`import { testAllEstablished } from '@ontrails/testing';
|
|
28
|
+
`import { testAllEstablished } from '@ontrails/testing/established';
|
|
28
29
|
import { app } from '../src/app.js';
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
const permitScopes = [
|
|
32
|
+
...new Set(
|
|
33
|
+
app
|
|
34
|
+
.list()
|
|
35
|
+
.flatMap((trail) =>
|
|
36
|
+
trail.permit && trail.permit !== 'public' ? trail.permit.scopes : []
|
|
37
|
+
)
|
|
38
|
+
),
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
testAllEstablished(app, {
|
|
42
|
+
ctx: {
|
|
43
|
+
permit: { id: 'test-permit', scopes: permitScopes },
|
|
44
|
+
},
|
|
45
|
+
});
|
|
31
46
|
`;
|
|
32
47
|
|
|
33
48
|
const generateLefthookYml = (): string =>
|
|
@@ -79,7 +94,7 @@ export const addVerify = trail('add.verify', {
|
|
|
79
94
|
blaze: async (input) => {
|
|
80
95
|
const projectDirResult = resolveProjectDir(input.dir ?? '.', input.name);
|
|
81
96
|
if (projectDirResult.isErr()) {
|
|
82
|
-
return
|
|
97
|
+
return projectDirResult;
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
const projectDir = projectDirResult.value;
|
|
@@ -89,6 +104,14 @@ export const addVerify = trail('add.verify', {
|
|
|
89
104
|
relativePath: string,
|
|
90
105
|
content: string
|
|
91
106
|
): Promise<Result<void, Error>> => {
|
|
107
|
+
const exists = projectPathExists(projectDir, relativePath);
|
|
108
|
+
if (exists.isErr()) {
|
|
109
|
+
return Result.err(exists.error);
|
|
110
|
+
}
|
|
111
|
+
if (exists.value) {
|
|
112
|
+
return Result.ok();
|
|
113
|
+
}
|
|
114
|
+
|
|
92
115
|
const written = await writeProjectFile(projectDir, relativePath, content);
|
|
93
116
|
if (written.isErr()) {
|
|
94
117
|
return Result.err(written.error);
|
|
@@ -102,17 +125,17 @@ export const addVerify = trail('add.verify', {
|
|
|
102
125
|
generateTestFile()
|
|
103
126
|
);
|
|
104
127
|
if (testFile.isErr()) {
|
|
105
|
-
return
|
|
128
|
+
return testFile;
|
|
106
129
|
}
|
|
107
130
|
|
|
108
131
|
const lefthookFile = await writeFile('lefthook.yml', generateLefthookYml());
|
|
109
132
|
if (lefthookFile.isErr()) {
|
|
110
|
-
return
|
|
133
|
+
return lefthookFile;
|
|
111
134
|
}
|
|
112
135
|
|
|
113
136
|
const packageResult = await updatePackageJsonForVerify(projectDir);
|
|
114
137
|
if (packageResult.isErr()) {
|
|
115
|
-
return
|
|
138
|
+
return packageResult;
|
|
116
139
|
}
|
|
117
140
|
|
|
118
141
|
return Result.ok({ created: files });
|
|
@@ -128,5 +151,6 @@ export const addVerify = trail('add.verify', {
|
|
|
128
151
|
output: z.object({
|
|
129
152
|
created: z.array(z.string()),
|
|
130
153
|
}),
|
|
154
|
+
permit: { scopes: ['project:write'] },
|
|
131
155
|
visibility: 'internal',
|
|
132
156
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Topo } from '@ontrails/core';
|
|
1
|
+
import { trail } from '@ontrails/core';
|
|
2
|
+
import type { Result, Topo } from '@ontrails/core';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
import { tryLoadFreshAppLease } from './load-app.js';
|
|
@@ -13,23 +13,26 @@ import {
|
|
|
13
13
|
|
|
14
14
|
export const compileCurrentTopo = async (
|
|
15
15
|
app: Topo,
|
|
16
|
-
options?: { readonly rootDir?: string }
|
|
16
|
+
options?: { readonly force?: boolean | undefined; readonly rootDir?: string }
|
|
17
17
|
): Promise<Result<TopoExportReport, Error>> => exportCurrentTopo(app, options);
|
|
18
18
|
|
|
19
|
-
export const
|
|
19
|
+
export const compileTrail = trail('compile', {
|
|
20
20
|
blaze: async (input, ctx) => {
|
|
21
21
|
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
22
22
|
if (rootDirResult.isErr()) {
|
|
23
|
-
return
|
|
23
|
+
return rootDirResult;
|
|
24
24
|
}
|
|
25
25
|
const rootDir = rootDirResult.value;
|
|
26
26
|
const leaseResult = await tryLoadFreshAppLease(input.module, rootDir);
|
|
27
27
|
if (leaseResult.isErr()) {
|
|
28
|
-
return
|
|
28
|
+
return leaseResult;
|
|
29
29
|
}
|
|
30
30
|
const lease = leaseResult.value;
|
|
31
31
|
try {
|
|
32
|
-
return await compileCurrentTopo(lease.app, {
|
|
32
|
+
return await compileCurrentTopo(lease.app, {
|
|
33
|
+
force: input.force,
|
|
34
|
+
rootDir,
|
|
35
|
+
});
|
|
33
36
|
} finally {
|
|
34
37
|
lease.release();
|
|
35
38
|
}
|
|
@@ -37,11 +40,15 @@ export const topoCompileTrail = trail('topo.compile', {
|
|
|
37
40
|
description: 'Compile the current topo to .trails artifacts',
|
|
38
41
|
examples: [
|
|
39
42
|
{
|
|
40
|
-
input: createIsolatedExampleInput('
|
|
43
|
+
input: createIsolatedExampleInput('compile'),
|
|
41
44
|
name: 'Compile the current topo artifacts',
|
|
42
45
|
},
|
|
43
46
|
],
|
|
44
47
|
input: z.object({
|
|
48
|
+
force: z
|
|
49
|
+
.boolean()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Record graph-only force events for breaking changes'),
|
|
45
52
|
module: z.string().optional().describe('Path to the app module'),
|
|
46
53
|
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
47
54
|
}),
|
|
@@ -52,4 +59,5 @@ export const topoCompileTrail = trail('topo.compile', {
|
|
|
52
59
|
snapshot: topoSnapshotOutput,
|
|
53
60
|
topoPath: z.string(),
|
|
54
61
|
}),
|
|
62
|
+
permit: { scopes: ['topo:write'] },
|
|
55
63
|
});
|
|
@@ -121,7 +121,7 @@ export const completionsCompleteTrail = trail('completions.__complete', {
|
|
|
121
121
|
blaze: async (input, ctx) => {
|
|
122
122
|
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
123
123
|
if (rootDirResult.isErr()) {
|
|
124
|
-
return
|
|
124
|
+
return rootDirResult;
|
|
125
125
|
}
|
|
126
126
|
const rootDir = rootDirResult.value;
|
|
127
127
|
|