@ontrails/trails 1.0.0-beta.14 → 1.0.0-beta.16
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 +208 -0
- package/README.md +27 -0
- package/package.json +19 -8
- package/src/app.ts +17 -7
- package/src/clack.ts +1 -1
- package/src/cli.ts +304 -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 +172 -0
- package/src/trails/add-trail.ts +73 -27
- package/src/trails/add-verify.ts +68 -23
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-scaffold.ts +101 -35
- package/src/trails/create.ts +87 -74
- package/src/trails/dev-clean.ts +31 -22
- package/src/trails/dev-reset.ts +9 -3
- package/src/trails/dev-stats.ts +28 -20
- package/src/trails/dev-support.ts +109 -95
- package/src/trails/draft-promote.ts +351 -107
- package/src/trails/guide.ts +55 -38
- package/src/trails/load-app.ts +712 -38
- 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 +517 -186
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-compile.ts +55 -0
- package/src/trails/topo-history.ts +14 -11
- package/src/trails/topo-output-schemas.ts +175 -0
- package/src/trails/topo-pin.ts +25 -16
- package/src/trails/topo-read-support.ts +178 -238
- package/src/trails/topo-reports.ts +445 -63
- package/src/trails/topo-store-support.ts +67 -35
- package/src/trails/topo-support.ts +93 -147
- package/src/trails/topo-unpin.ts +17 -7
- package/src/trails/topo-verify.ts +19 -10
- package/src/trails/topo.ts +64 -31
- package/src/trails/warden-guide.ts +121 -0
- package/src/trails/warden.ts +137 -47
- package/src/versions.ts +28 -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 -20
- 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 -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-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 -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/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 -65
- 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 -38
- 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 -178
- 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 -386
- 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 -64
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -6
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -67
- 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 -212
- 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 -18
- package/dist/src/trails/topo-export.d.ts.map +0 -1
- package/dist/src/trails/topo-export.js +0 -34
- package/dist/src/trails/topo-export.js.map +0 -1
- package/dist/src/trails/topo-history.d.ts +0 -24
- package/dist/src/trails/topo-history.d.ts.map +0 -1
- package/dist/src/trails/topo-history.js +0 -33
- package/dist/src/trails/topo-history.js.map +0 -1
- package/dist/src/trails/topo-pin.d.ts +0 -21
- package/dist/src/trails/topo-pin.d.ts.map +0 -1
- package/dist/src/trails/topo-pin.js +0 -35
- package/dist/src/trails/topo-pin.js.map +0 -1
- package/dist/src/trails/topo-read-support.d.ts +0 -54
- package/dist/src/trails/topo-read-support.d.ts.map +0 -1
- package/dist/src/trails/topo-read-support.js +0 -178
- package/dist/src/trails/topo-read-support.js.map +0 -1
- package/dist/src/trails/topo-reports.d.ts +0 -50
- package/dist/src/trails/topo-reports.d.ts.map +0 -1
- package/dist/src/trails/topo-reports.js +0 -122
- 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 -53
- 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 -87
- package/dist/src/trails/topo-support.d.ts.map +0 -1
- package/dist/src/trails/topo-support.js +0 -165
- package/dist/src/trails/topo-support.js.map +0 -1
- package/dist/src/trails/topo-unpin.d.ts +0 -15
- package/dist/src/trails/topo-unpin.d.ts.map +0 -1
- package/dist/src/trails/topo-unpin.js +0 -39
- 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 -28
- 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 -67
- package/dist/src/trails/topo.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 -89
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/create.test.ts +0 -351
- package/src/__tests__/draft-promote.test.ts +0 -144
- package/src/__tests__/guide.test.ts +0 -91
- package/src/__tests__/load-app.test.ts +0 -58
- package/src/__tests__/survey.test.ts +0 -301
- package/src/__tests__/topo-dev.test.ts +0 -424
- package/src/__tests__/warden.test.ts +0 -74
- package/src/trails/add-trailhead.ts +0 -121
- package/src/trails/topo-export.ts +0 -39
- package/src/trails/topo-show.ts +0 -58
- package/tsconfig.json +0 -9
|
@@ -4,12 +4,27 @@
|
|
|
4
4
|
* Generates package.json, tsconfig, app.ts, starter trails, and .trails/ directory.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import { dirname, join, resolve } from 'node:path';
|
|
7
|
+
import { resolve } from 'node:path';
|
|
9
8
|
|
|
10
|
-
import { Result, trail } from '@ontrails/core';
|
|
9
|
+
import { Result, trail, WORKSPACE_GITIGNORE_CONTENT } from '@ontrails/core';
|
|
11
10
|
import { z } from 'zod';
|
|
12
11
|
|
|
12
|
+
import {
|
|
13
|
+
applyProjectOperations,
|
|
14
|
+
planProjectOperations,
|
|
15
|
+
PROJECT_NAME_MESSAGE,
|
|
16
|
+
PROJECT_NAME_PATTERN,
|
|
17
|
+
resolveProjectDir,
|
|
18
|
+
} from '../project-writes.js';
|
|
19
|
+
import type {
|
|
20
|
+
PlannedProjectOperation,
|
|
21
|
+
ProjectWriteOperation,
|
|
22
|
+
} from '../project-writes.js';
|
|
23
|
+
import {
|
|
24
|
+
ontrailsPackageRange,
|
|
25
|
+
scaffoldDependencyVersions,
|
|
26
|
+
} from '../versions.js';
|
|
27
|
+
|
|
13
28
|
// ---------------------------------------------------------------------------
|
|
14
29
|
// Types
|
|
15
30
|
// ---------------------------------------------------------------------------
|
|
@@ -19,7 +34,9 @@ type Starter = 'empty' | 'entity' | 'hello';
|
|
|
19
34
|
interface ScaffoldResult {
|
|
20
35
|
readonly created: string[];
|
|
21
36
|
readonly dir: string;
|
|
37
|
+
readonly dryRun: boolean;
|
|
22
38
|
readonly name: string;
|
|
39
|
+
readonly plannedOperations: PlannedProjectOperation[];
|
|
23
40
|
}
|
|
24
41
|
|
|
25
42
|
// ---------------------------------------------------------------------------
|
|
@@ -28,17 +45,28 @@ interface ScaffoldResult {
|
|
|
28
45
|
|
|
29
46
|
const generatePackageJson = (name: string): string => {
|
|
30
47
|
const deps: Record<string, string> = {
|
|
31
|
-
'@ontrails/core':
|
|
32
|
-
zod:
|
|
48
|
+
'@ontrails/core': ontrailsPackageRange,
|
|
49
|
+
zod: scaffoldDependencyVersions.zod,
|
|
33
50
|
};
|
|
34
51
|
|
|
35
52
|
const pkg: Record<string, unknown> = {
|
|
36
53
|
dependencies: Object.fromEntries(
|
|
37
54
|
Object.entries(deps).toSorted(([a], [b]) => a.localeCompare(b))
|
|
38
55
|
),
|
|
56
|
+
devDependencies: Object.fromEntries(
|
|
57
|
+
Object.entries({
|
|
58
|
+
'@types/bun': scaffoldDependencyVersions.bunTypes,
|
|
59
|
+
oxfmt: scaffoldDependencyVersions.oxfmt,
|
|
60
|
+
oxlint: scaffoldDependencyVersions.oxlint,
|
|
61
|
+
typescript: scaffoldDependencyVersions.typescript,
|
|
62
|
+
ultracite: scaffoldDependencyVersions.ultracite,
|
|
63
|
+
}).toSorted(([a], [b]) => a.localeCompare(b))
|
|
64
|
+
),
|
|
39
65
|
name,
|
|
40
66
|
scripts: {
|
|
41
67
|
build: 'tsc -b',
|
|
68
|
+
'format:check': 'bunx ultracite check .',
|
|
69
|
+
'format:fix': 'bunx ultracite fix .',
|
|
42
70
|
lint: 'oxlint ./src',
|
|
43
71
|
test: 'bun test',
|
|
44
72
|
typecheck: 'tsc --noEmit',
|
|
@@ -73,16 +101,19 @@ const TSCONFIG_CONTENT = JSON.stringify(
|
|
|
73
101
|
const GITIGNORE_CONTENT = `node_modules/
|
|
74
102
|
dist/
|
|
75
103
|
*.tsbuildinfo
|
|
76
|
-
.trails/
|
|
104
|
+
.trails/cache/
|
|
105
|
+
.trails/state/
|
|
106
|
+
.trails/config.local.js
|
|
107
|
+
.trails/config.local.ts
|
|
77
108
|
`;
|
|
78
109
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
110
|
+
const OXLINT_CONFIG_CONTENT = `import { defineConfig } from 'oxlint';
|
|
111
|
+
import ultracite from 'ultracite/oxlint/core';
|
|
112
|
+
|
|
113
|
+
export default defineConfig({
|
|
114
|
+
extends: [ultracite],
|
|
115
|
+
});
|
|
116
|
+
`;
|
|
86
117
|
|
|
87
118
|
const OXFMTRC_CONTENT = `{
|
|
88
119
|
// ultracite defaults
|
|
@@ -260,8 +291,11 @@ const starterImports: Record<
|
|
|
260
291
|
|
|
261
292
|
const generateAppTs = (name: string, starter: Starter): string => {
|
|
262
293
|
const { imports, modules } = starterImports[starter];
|
|
294
|
+
const appNameLiteral = JSON.stringify(name);
|
|
263
295
|
const topoArgs =
|
|
264
|
-
modules.length > 0
|
|
296
|
+
modules.length > 0
|
|
297
|
+
? `${appNameLiteral}, ${modules.join(', ')}`
|
|
298
|
+
: appNameLiteral;
|
|
265
299
|
|
|
266
300
|
return [
|
|
267
301
|
"import { topo } from '@ontrails/core';",
|
|
@@ -296,25 +330,21 @@ const collectScaffoldFiles = (
|
|
|
296
330
|
['package.json', generatePackageJson(name)],
|
|
297
331
|
['tsconfig.json', TSCONFIG_CONTENT],
|
|
298
332
|
['.gitignore', GITIGNORE_CONTENT],
|
|
299
|
-
['.
|
|
333
|
+
['oxlint.config.ts', OXLINT_CONFIG_CONTENT],
|
|
300
334
|
['.oxfmtrc.jsonc', OXFMTRC_CONTENT],
|
|
335
|
+
['.trails/.gitignore', WORKSPACE_GITIGNORE_CONTENT],
|
|
301
336
|
['src/app.ts', generateAppTs(name, starter)],
|
|
302
337
|
...starterFileGenerators[starter](),
|
|
303
338
|
]);
|
|
304
339
|
|
|
305
|
-
const
|
|
306
|
-
projectDir: string,
|
|
340
|
+
const collectScaffoldOperations = (
|
|
307
341
|
fileMap: Map<string, string>
|
|
308
|
-
):
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
files.push(relativePath);
|
|
315
|
-
}
|
|
316
|
-
return files;
|
|
317
|
-
};
|
|
342
|
+
): ProjectWriteOperation[] =>
|
|
343
|
+
[...fileMap].map(([path, content]) => ({
|
|
344
|
+
content,
|
|
345
|
+
kind: 'write' as const,
|
|
346
|
+
path,
|
|
347
|
+
}));
|
|
318
348
|
|
|
319
349
|
// ---------------------------------------------------------------------------
|
|
320
350
|
// Trail definition
|
|
@@ -322,31 +352,67 @@ const writeScaffoldFiles = async (
|
|
|
322
352
|
|
|
323
353
|
export const createScaffold = trail('create.scaffold', {
|
|
324
354
|
blaze: async (input) => {
|
|
325
|
-
const
|
|
355
|
+
const projectDirResult = resolveProjectDir(input.dir ?? '.', input.name);
|
|
356
|
+
if (projectDirResult.isErr()) {
|
|
357
|
+
return Result.err(projectDirResult.error);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const projectDir = projectDirResult.value;
|
|
326
361
|
const starter = (input.starter ?? 'hello') as Starter;
|
|
362
|
+
const dryRun = input.dryRun === true;
|
|
327
363
|
const fileMap = collectScaffoldFiles(input.name, starter);
|
|
328
|
-
const
|
|
329
|
-
|
|
364
|
+
const operations = collectScaffoldOperations(fileMap);
|
|
365
|
+
const plannedOperations = dryRun
|
|
366
|
+
? planProjectOperations(projectDir, operations)
|
|
367
|
+
: await applyProjectOperations(projectDir, operations);
|
|
368
|
+
if (plannedOperations.isErr()) {
|
|
369
|
+
return Result.err(plannedOperations.error);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const created = dryRun ? [] : [...fileMap.keys()];
|
|
330
373
|
|
|
331
374
|
return Result.ok({
|
|
332
|
-
created
|
|
333
|
-
dir: projectDir,
|
|
375
|
+
created,
|
|
376
|
+
dir: resolve(projectDir),
|
|
377
|
+
dryRun,
|
|
334
378
|
name: input.name,
|
|
379
|
+
plannedOperations: plannedOperations.value,
|
|
335
380
|
} satisfies ScaffoldResult);
|
|
336
381
|
},
|
|
337
382
|
description: 'Scaffold a new Trails project',
|
|
338
383
|
input: z.object({
|
|
339
384
|
dir: z.string().optional().describe('Parent directory'),
|
|
340
|
-
|
|
385
|
+
dryRun: z
|
|
386
|
+
.boolean()
|
|
387
|
+
.default(false)
|
|
388
|
+
.describe('Plan scaffold writes without touching the project directory'),
|
|
389
|
+
name: z
|
|
390
|
+
.string()
|
|
391
|
+
.regex(PROJECT_NAME_PATTERN, PROJECT_NAME_MESSAGE)
|
|
392
|
+
.describe('Project name'),
|
|
341
393
|
starter: z
|
|
342
394
|
.enum(['hello', 'entity', 'empty'])
|
|
343
395
|
.default('hello')
|
|
344
396
|
.describe('Starter trail'),
|
|
345
397
|
}),
|
|
346
|
-
meta: { internal: true },
|
|
347
398
|
output: z.object({
|
|
348
|
-
created: z
|
|
399
|
+
created: z
|
|
400
|
+
.array(z.string())
|
|
401
|
+
.describe('Project-relative paths of files written (empty in dry-run)'),
|
|
349
402
|
dir: z.string(),
|
|
403
|
+
dryRun: z.boolean(),
|
|
350
404
|
name: z.string(),
|
|
405
|
+
plannedOperations: z.array(
|
|
406
|
+
z.discriminatedUnion('kind', [
|
|
407
|
+
z.object({ kind: z.literal('mkdir'), path: z.string() }),
|
|
408
|
+
z.object({
|
|
409
|
+
from: z.string(),
|
|
410
|
+
kind: z.literal('rename'),
|
|
411
|
+
to: z.string(),
|
|
412
|
+
}),
|
|
413
|
+
z.object({ kind: z.literal('write'), path: z.string() }),
|
|
414
|
+
])
|
|
415
|
+
),
|
|
351
416
|
}),
|
|
417
|
+
visibility: 'internal',
|
|
352
418
|
});
|
package/src/trails/create.ts
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `create` route -- Create a new Trails project.
|
|
3
3
|
*
|
|
4
|
-
* Composes create.scaffold, add.
|
|
5
|
-
* via ctx.cross
|
|
4
|
+
* Composes create.scaffold, add.surface, and add.verify sub-trails
|
|
5
|
+
* via ctx.cross.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import { Result, trail } from '@ontrails/core';
|
|
8
|
+
import { InternalError, Result, trail } from '@ontrails/core';
|
|
10
9
|
import { z } from 'zod';
|
|
11
10
|
|
|
11
|
+
import {
|
|
12
|
+
PROJECT_NAME_MESSAGE,
|
|
13
|
+
PROJECT_NAME_PATTERN,
|
|
14
|
+
} from '../project-writes.js';
|
|
15
|
+
|
|
12
16
|
// ---------------------------------------------------------------------------
|
|
13
17
|
// Helpers
|
|
14
18
|
// ---------------------------------------------------------------------------
|
|
15
19
|
|
|
16
20
|
type Starter = 'empty' | 'entity' | 'hello';
|
|
17
|
-
type
|
|
21
|
+
type Surface = 'cli' | 'http' | 'mcp';
|
|
18
22
|
|
|
19
23
|
interface CreateInput {
|
|
20
24
|
readonly dir?: string | undefined;
|
|
21
25
|
readonly name: string;
|
|
22
26
|
readonly starter: Starter;
|
|
23
|
-
readonly
|
|
27
|
+
readonly surfaces: readonly Surface[];
|
|
24
28
|
readonly verify: boolean;
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -42,15 +46,20 @@ interface ScaffoldedProject {
|
|
|
42
46
|
readonly name: string;
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
interface SurfaceResult {
|
|
50
|
+
readonly created: string;
|
|
51
|
+
readonly dependency: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
const buildScaffoldInput = (input: ScaffoldRequest) => ({
|
|
46
55
|
...(input.dir === undefined ? {} : { dir: input.dir }),
|
|
47
56
|
name: input.name,
|
|
48
57
|
starter: input.starter,
|
|
49
58
|
});
|
|
50
59
|
|
|
51
|
-
const
|
|
60
|
+
const buildSurfaceInput = (dir: string, surface: string) => ({
|
|
52
61
|
dir,
|
|
53
|
-
|
|
62
|
+
surface,
|
|
54
63
|
});
|
|
55
64
|
|
|
56
65
|
const buildVerifyInput = (input: VerifyRequest) => ({
|
|
@@ -58,24 +67,14 @@ const buildVerifyInput = (input: VerifyRequest) => ({
|
|
|
58
67
|
name: input.name,
|
|
59
68
|
});
|
|
60
69
|
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
): Promise<Result<ScaffoldedProject, Error>> =>
|
|
65
|
-
cross('create.scaffold', buildScaffoldInput(input));
|
|
66
|
-
|
|
67
|
-
const addTrailheadFiles = async (
|
|
68
|
-
cross: CrossFn,
|
|
69
|
-
dir: string,
|
|
70
|
-
trailheads: readonly string[]
|
|
70
|
+
const collectSurfaceFiles = async (
|
|
71
|
+
surfaces: readonly string[],
|
|
72
|
+
addSurface: (surface: string) => Promise<Result<SurfaceResult, Error>>
|
|
71
73
|
): Promise<Result<string[], Error>> => {
|
|
72
74
|
const created: string[] = [];
|
|
73
75
|
|
|
74
|
-
for (const
|
|
75
|
-
const result = await
|
|
76
|
-
'add.trailhead',
|
|
77
|
-
buildTrailheadInput(dir, trailhead)
|
|
78
|
-
);
|
|
76
|
+
for (const surface of surfaces) {
|
|
77
|
+
const result = await addSurface(surface);
|
|
79
78
|
if (result.isErr()) {
|
|
80
79
|
return Result.err(result.error);
|
|
81
80
|
}
|
|
@@ -86,17 +85,14 @@ const addTrailheadFiles = async (
|
|
|
86
85
|
};
|
|
87
86
|
|
|
88
87
|
const collectVerifyFiles = async (
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
shouldVerify: boolean,
|
|
89
|
+
addVerify: () => Promise<Result<{ created: string[] }, Error>>
|
|
91
90
|
): Promise<Result<string[], Error>> => {
|
|
92
|
-
if (!
|
|
91
|
+
if (!shouldVerify) {
|
|
93
92
|
return Result.ok([]);
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
const result = await
|
|
97
|
-
'add.verify',
|
|
98
|
-
buildVerifyInput(input)
|
|
99
|
-
);
|
|
95
|
+
const result = await addVerify();
|
|
100
96
|
return result.isErr()
|
|
101
97
|
? Result.err(result.error)
|
|
102
98
|
: Result.ok(result.value.created);
|
|
@@ -104,43 +100,9 @@ const collectVerifyFiles = async (
|
|
|
104
100
|
|
|
105
101
|
const collectCreatedFiles = (
|
|
106
102
|
scaffolded: readonly string[],
|
|
107
|
-
|
|
103
|
+
surfaces: readonly string[],
|
|
108
104
|
verify: readonly string[]
|
|
109
|
-
): string[] => [...scaffolded, ...
|
|
110
|
-
|
|
111
|
-
const runCreate = async (
|
|
112
|
-
cross: CrossFn,
|
|
113
|
-
input: CreateInput
|
|
114
|
-
): Promise<Result<{ created: string[]; dir: string; name: string }, Error>> => {
|
|
115
|
-
const scaffolded = await scaffoldProject(cross, input);
|
|
116
|
-
if (scaffolded.isErr()) {
|
|
117
|
-
return Result.err(scaffolded.error);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const trailheadResults = await addTrailheadFiles(
|
|
121
|
-
cross,
|
|
122
|
-
scaffolded.value.dir,
|
|
123
|
-
input.trailheads
|
|
124
|
-
);
|
|
125
|
-
if (trailheadResults.isErr()) {
|
|
126
|
-
return Result.err(trailheadResults.error);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const verifyFiles = await collectVerifyFiles(cross, input);
|
|
130
|
-
if (verifyFiles.isErr()) {
|
|
131
|
-
return Result.err(verifyFiles.error);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return Result.ok({
|
|
135
|
-
created: collectCreatedFiles(
|
|
136
|
-
scaffolded.value.created,
|
|
137
|
-
trailheadResults.value,
|
|
138
|
-
verifyFiles.value
|
|
139
|
-
),
|
|
140
|
-
dir: scaffolded.value.dir,
|
|
141
|
-
name: input.name,
|
|
142
|
-
});
|
|
143
|
-
};
|
|
105
|
+
): string[] => [...scaffolded, ...surfaces, ...verify];
|
|
144
106
|
|
|
145
107
|
// ---------------------------------------------------------------------------
|
|
146
108
|
// Route definition
|
|
@@ -149,11 +111,54 @@ const runCreate = async (
|
|
|
149
111
|
export const createRoute = trail('create', {
|
|
150
112
|
blaze: async (input: CreateInput, ctx) => {
|
|
151
113
|
if (!ctx.cross) {
|
|
152
|
-
return Result.err(new
|
|
114
|
+
return Result.err(new InternalError('create route requires ctx.cross'));
|
|
153
115
|
}
|
|
154
|
-
|
|
116
|
+
const { cross } = ctx;
|
|
117
|
+
|
|
118
|
+
const scaffolded = await cross<ScaffoldedProject>(
|
|
119
|
+
'create.scaffold',
|
|
120
|
+
buildScaffoldInput(input)
|
|
121
|
+
);
|
|
122
|
+
if (scaffolded.isErr()) {
|
|
123
|
+
return Result.err(scaffolded.error);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const finishCreate = async (): Promise<
|
|
127
|
+
Result<{ created: string[]; dir: string; name: string }, Error>
|
|
128
|
+
> => {
|
|
129
|
+
const surfaceFiles = await collectSurfaceFiles(
|
|
130
|
+
input.surfaces,
|
|
131
|
+
(surface) =>
|
|
132
|
+
cross<SurfaceResult>(
|
|
133
|
+
'add.surface',
|
|
134
|
+
buildSurfaceInput(scaffolded.value.dir, surface)
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
if (surfaceFiles.isErr()) {
|
|
138
|
+
return Result.err(surfaceFiles.error);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const verifyFiles = await collectVerifyFiles(input.verify, () =>
|
|
142
|
+
cross<{ created: string[] }>('add.verify', buildVerifyInput(input))
|
|
143
|
+
);
|
|
144
|
+
if (verifyFiles.isErr()) {
|
|
145
|
+
return Result.err(verifyFiles.error);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return Result.ok({
|
|
149
|
+
created: collectCreatedFiles(
|
|
150
|
+
scaffolded.value.created,
|
|
151
|
+
surfaceFiles.value,
|
|
152
|
+
verifyFiles.value
|
|
153
|
+
),
|
|
154
|
+
dir: scaffolded.value.dir,
|
|
155
|
+
name: input.name,
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return finishCreate();
|
|
155
160
|
},
|
|
156
|
-
crosses: ['create.scaffold', 'add.
|
|
161
|
+
crosses: ['create.scaffold', 'add.surface', 'add.verify'],
|
|
157
162
|
description: 'Create a new Trails project',
|
|
158
163
|
fields: {
|
|
159
164
|
starter: {
|
|
@@ -171,7 +176,7 @@ export const createRoute = trail('create', {
|
|
|
171
176
|
{ hint: 'Just the structure', label: 'Empty', value: 'empty' },
|
|
172
177
|
],
|
|
173
178
|
},
|
|
174
|
-
|
|
179
|
+
surfaces: {
|
|
175
180
|
options: [
|
|
176
181
|
{ hint: 'Commander-based command line', label: 'CLI', value: 'cli' },
|
|
177
182
|
{
|
|
@@ -179,20 +184,28 @@ export const createRoute = trail('create', {
|
|
|
179
184
|
label: 'MCP',
|
|
180
185
|
value: 'mcp',
|
|
181
186
|
},
|
|
187
|
+
{
|
|
188
|
+
hint: 'Hono-powered HTTP endpoints',
|
|
189
|
+
label: 'HTTP',
|
|
190
|
+
value: 'http',
|
|
191
|
+
},
|
|
182
192
|
],
|
|
183
193
|
},
|
|
184
194
|
},
|
|
185
195
|
input: z.object({
|
|
186
196
|
dir: z.string().optional().describe('Parent directory'),
|
|
187
|
-
name: z
|
|
197
|
+
name: z
|
|
198
|
+
.string()
|
|
199
|
+
.regex(PROJECT_NAME_PATTERN, PROJECT_NAME_MESSAGE)
|
|
200
|
+
.describe('Project name'),
|
|
188
201
|
starter: z
|
|
189
202
|
.enum(['hello', 'entity', 'empty'])
|
|
190
203
|
.default('hello')
|
|
191
204
|
.describe('Starter trail'),
|
|
192
|
-
|
|
193
|
-
.array(z.enum(['cli', 'mcp']))
|
|
205
|
+
surfaces: z
|
|
206
|
+
.array(z.enum(['cli', 'http', 'mcp']))
|
|
194
207
|
.default(['cli'])
|
|
195
|
-
.describe('
|
|
208
|
+
.describe('Surfaces'),
|
|
196
209
|
verify: z.boolean().default(true).describe('Include testing + warden'),
|
|
197
210
|
}),
|
|
198
211
|
output: z.object({
|
package/src/trails/dev-clean.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { Result, ValidationError, trail } from '@ontrails/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
cleanDevState,
|
|
6
|
+
DEFAULT_TOPO_SNAPSHOT_RETENTION,
|
|
7
|
+
} from './dev-support.js';
|
|
8
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
9
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
6
10
|
|
|
7
11
|
export const devCleanTrail = trail('dev.clean', {
|
|
8
12
|
blaze: (input, ctx) => {
|
|
@@ -14,23 +18,27 @@ export const devCleanTrail = trail('dev.clean', {
|
|
|
14
18
|
);
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
const
|
|
21
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
22
|
+
if (rootDirResult.isErr()) {
|
|
23
|
+
return Result.err(rootDirResult.error);
|
|
24
|
+
}
|
|
25
|
+
const rootDir = rootDirResult.value;
|
|
18
26
|
return Result.ok(
|
|
19
27
|
cleanDevState({
|
|
20
28
|
dryRun: input.dryRun,
|
|
21
|
-
maxAge: input.
|
|
22
|
-
maxRecords: input.
|
|
29
|
+
maxAge: input.traceAgeMs,
|
|
30
|
+
maxRecords: input.traces,
|
|
23
31
|
rootDir,
|
|
24
|
-
|
|
32
|
+
snapshotRetention: input.snapshots,
|
|
25
33
|
})
|
|
26
34
|
);
|
|
27
35
|
},
|
|
28
|
-
description: 'Prune unpinned topo
|
|
36
|
+
description: 'Prune unpinned topo snapshots and old trace records',
|
|
29
37
|
examples: [
|
|
30
38
|
{
|
|
31
39
|
input: {
|
|
32
40
|
dryRun: true,
|
|
33
|
-
rootDir:
|
|
41
|
+
rootDir: createIsolatedExampleInput('dev-clean').rootDir,
|
|
34
42
|
},
|
|
35
43
|
name: 'Preview local cleanup',
|
|
36
44
|
},
|
|
@@ -41,33 +49,34 @@ export const devCleanTrail = trail('dev.clean', {
|
|
|
41
49
|
.default(true)
|
|
42
50
|
.describe('Preview cleanup without changing state'),
|
|
43
51
|
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
44
|
-
|
|
52
|
+
snapshots: z
|
|
45
53
|
.number()
|
|
46
|
-
.default(
|
|
47
|
-
.describe('Unpinned topo
|
|
48
|
-
|
|
54
|
+
.default(DEFAULT_TOPO_SNAPSHOT_RETENTION)
|
|
55
|
+
.describe('Unpinned topo snapshots to retain'),
|
|
56
|
+
traceAgeMs: z
|
|
49
57
|
.number()
|
|
50
58
|
.default(7 * 24 * 60 * 60 * 1000)
|
|
51
|
-
.describe('Maximum retained
|
|
52
|
-
|
|
59
|
+
.describe('Maximum retained trace age in milliseconds'),
|
|
60
|
+
traces: z.number().default(10_000).describe('Maximum retained trace count'),
|
|
53
61
|
yes: z.boolean().default(false).describe('Confirm destructive changes'),
|
|
54
62
|
}),
|
|
55
63
|
intent: 'destroy',
|
|
56
64
|
output: z.object({
|
|
57
65
|
dryRun: z.boolean(),
|
|
58
66
|
remaining: z.object({
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
pinnedCount: z.number(),
|
|
68
|
+
snapshotCount: z.number(),
|
|
69
|
+
traceCount: z.number(),
|
|
62
70
|
}),
|
|
63
71
|
removed: z.object({
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
topoSnapshots: z.number(),
|
|
73
|
+
traceRecords: z.number(),
|
|
66
74
|
}),
|
|
67
75
|
retention: z.object({
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
snapshots: z.number(),
|
|
77
|
+
traceAgeMs: z.number(),
|
|
78
|
+
traces: z.number(),
|
|
71
79
|
}),
|
|
72
80
|
}),
|
|
81
|
+
permit: { scopes: ['dev:clean'] },
|
|
73
82
|
});
|
package/src/trails/dev-reset.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { Result, ValidationError, trail } from '@ontrails/core';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
import { resetDevState } from './dev-support.js';
|
|
5
|
-
import {
|
|
5
|
+
import { resolveTrailRootDir } from './root-dir.js';
|
|
6
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
6
7
|
|
|
7
8
|
export const devResetTrail = trail('dev.reset', {
|
|
8
9
|
blaze: (input, ctx) => {
|
|
@@ -14,7 +15,11 @@ export const devResetTrail = trail('dev.reset', {
|
|
|
14
15
|
);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const
|
|
18
|
+
const rootDirResult = resolveTrailRootDir(input.rootDir, ctx.cwd);
|
|
19
|
+
if (rootDirResult.isErr()) {
|
|
20
|
+
return Result.err(rootDirResult.error);
|
|
21
|
+
}
|
|
22
|
+
const rootDir = rootDirResult.value;
|
|
18
23
|
return Result.ok(resetDevState({ dryRun: input.dryRun, rootDir }));
|
|
19
24
|
},
|
|
20
25
|
description: 'Remove local Trails database artifacts',
|
|
@@ -22,7 +27,7 @@ export const devResetTrail = trail('dev.reset', {
|
|
|
22
27
|
{
|
|
23
28
|
input: {
|
|
24
29
|
dryRun: true,
|
|
25
|
-
rootDir:
|
|
30
|
+
rootDir: createIsolatedExampleInput('dev-reset').rootDir,
|
|
26
31
|
},
|
|
27
32
|
name: 'Preview local reset',
|
|
28
33
|
},
|
|
@@ -41,4 +46,5 @@ export const devResetTrail = trail('dev.reset', {
|
|
|
41
46
|
removedCount: z.number(),
|
|
42
47
|
removedFiles: z.array(z.string()),
|
|
43
48
|
}),
|
|
49
|
+
permit: { scopes: ['dev:reset'] },
|
|
44
50
|
});
|