@ontrails/trails 1.0.0-beta.13 → 1.0.0-beta.15
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +29 -0
- package/__tests__/examples.test.ts +39 -0
- package/dist/src/app.d.ts.map +1 -1
- package/dist/src/app.js +12 -1
- package/dist/src/app.js.map +1 -1
- package/dist/src/cli.js +4 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/trails/add-surface.d.ts +3 -3
- package/dist/src/trails/add-surface.d.ts.map +1 -1
- package/dist/src/trails/add-surface.js +46 -24
- package/dist/src/trails/add-surface.js.map +1 -1
- package/dist/src/trails/add-trail.d.ts +3 -1
- package/dist/src/trails/add-trail.d.ts.map +1 -1
- package/dist/src/trails/add-trail.js +49 -22
- package/dist/src/trails/add-trail.js.map +1 -1
- package/dist/src/trails/add-trailhead.d.ts +13 -0
- package/dist/src/trails/add-trailhead.d.ts.map +1 -0
- package/dist/src/trails/add-trailhead.js +88 -0
- package/dist/src/trails/add-trailhead.js.map +1 -0
- package/dist/src/trails/add-verify.d.ts +1 -1
- package/dist/src/trails/add-verify.d.ts.map +1 -1
- package/dist/src/trails/add-verify.js +17 -16
- package/dist/src/trails/add-verify.js.map +1 -1
- package/dist/src/trails/create-scaffold.d.ts +1 -1
- package/dist/src/trails/create-scaffold.d.ts.map +1 -1
- package/dist/src/trails/create-scaffold.js +34 -27
- package/dist/src/trails/create-scaffold.js.map +1 -1
- package/dist/src/trails/create.d.ts +9 -13
- package/dist/src/trails/create.d.ts.map +1 -1
- package/dist/src/trails/create.js +40 -35
- package/dist/src/trails/create.js.map +1 -1
- package/dist/src/trails/dev-clean.d.ts +9 -0
- package/dist/src/trails/dev-clean.d.ts.map +1 -0
- package/dist/src/trails/dev-clean.js +66 -0
- package/dist/src/trails/dev-clean.js.map +1 -0
- package/dist/src/trails/dev-reset.d.ts +6 -0
- package/dist/src/trails/dev-reset.d.ts.map +1 -0
- package/dist/src/trails/dev-reset.js +39 -0
- package/dist/src/trails/dev-reset.js.map +1 -0
- package/dist/src/trails/dev-stats.d.ts +7 -0
- package/dist/src/trails/dev-stats.d.ts.map +1 -0
- package/dist/src/trails/dev-stats.js +61 -0
- package/dist/src/trails/dev-stats.js.map +1 -0
- package/dist/src/trails/dev-support.d.ts +64 -0
- package/dist/src/trails/dev-support.d.ts.map +1 -0
- package/dist/src/trails/dev-support.js +181 -0
- package/dist/src/trails/dev-support.js.map +1 -0
- package/dist/src/trails/draft-promote.d.ts +18 -0
- package/dist/src/trails/draft-promote.d.ts.map +1 -0
- package/dist/src/trails/draft-promote.js +400 -0
- package/dist/src/trails/draft-promote.js.map +1 -0
- package/dist/src/trails/guide.d.ts +14 -4
- package/dist/src/trails/guide.d.ts.map +1 -1
- package/dist/src/trails/guide.js +22 -41
- package/dist/src/trails/guide.js.map +1 -1
- package/dist/src/trails/load-app.d.ts +9 -1
- package/dist/src/trails/load-app.d.ts.map +1 -1
- package/dist/src/trails/load-app.js +404 -13
- package/dist/src/trails/load-app.js.map +1 -1
- package/dist/src/trails/project.d.ts.map +1 -1
- package/dist/src/trails/project.js +14 -3
- package/dist/src/trails/project.js.map +1 -1
- package/dist/src/trails/survey.d.ts +6 -60
- package/dist/src/trails/survey.d.ts.map +1 -1
- package/dist/src/trails/survey.js +83 -182
- package/dist/src/trails/survey.js.map +1 -1
- package/dist/src/trails/topo-constants.d.ts +3 -0
- package/dist/src/trails/topo-constants.d.ts.map +1 -0
- package/dist/src/trails/topo-constants.js +3 -0
- package/dist/src/trails/topo-constants.js.map +1 -0
- package/dist/src/trails/topo-export.d.ts +19 -0
- package/dist/src/trails/topo-export.d.ts.map +1 -0
- package/dist/src/trails/topo-export.js +31 -0
- package/dist/src/trails/topo-export.js.map +1 -0
- package/dist/src/trails/topo-history.d.ts +20 -0
- package/dist/src/trails/topo-history.d.ts.map +1 -0
- package/dist/src/trails/topo-history.js +32 -0
- package/dist/src/trails/topo-history.js.map +1 -0
- package/dist/src/trails/topo-pin.d.ts +17 -0
- package/dist/src/trails/topo-pin.d.ts.map +1 -0
- package/dist/src/trails/topo-pin.js +31 -0
- package/dist/src/trails/topo-pin.js.map +1 -0
- package/dist/src/trails/topo-read-support.d.ts +58 -0
- package/dist/src/trails/topo-read-support.d.ts.map +1 -0
- package/dist/src/trails/topo-read-support.js +167 -0
- package/dist/src/trails/topo-read-support.js.map +1 -0
- package/dist/src/trails/topo-reports.d.ts +54 -0
- package/dist/src/trails/topo-reports.d.ts.map +1 -0
- package/dist/src/trails/topo-reports.js +128 -0
- package/dist/src/trails/topo-reports.js.map +1 -0
- package/dist/src/trails/topo-show.d.ts +23 -0
- package/dist/src/trails/topo-show.d.ts.map +1 -0
- package/dist/src/trails/topo-show.js +49 -0
- package/dist/src/trails/topo-show.js.map +1 -0
- package/dist/src/trails/topo-store-support.d.ts +13 -0
- package/dist/src/trails/topo-store-support.d.ts.map +1 -0
- package/dist/src/trails/topo-store-support.js +55 -0
- package/dist/src/trails/topo-store-support.js.map +1 -0
- package/dist/src/trails/topo-support.d.ts +76 -0
- package/dist/src/trails/topo-support.d.ts.map +1 -0
- package/dist/src/trails/topo-support.js +132 -0
- package/dist/src/trails/topo-support.js.map +1 -0
- package/dist/src/trails/topo-unpin.d.ts +20 -0
- package/dist/src/trails/topo-unpin.d.ts.map +1 -0
- package/dist/src/trails/topo-unpin.js +44 -0
- package/dist/src/trails/topo-unpin.js.map +1 -0
- package/dist/src/trails/topo-verify.d.ts +5 -0
- package/dist/src/trails/topo-verify.d.ts.map +1 -0
- package/dist/src/trails/topo-verify.js +24 -0
- package/dist/src/trails/topo-verify.js.map +1 -0
- package/dist/src/trails/topo.d.ts +5 -0
- package/dist/src/trails/topo.d.ts.map +1 -0
- package/dist/src/trails/topo.js +63 -0
- package/dist/src/trails/topo.js.map +1 -0
- package/dist/src/trails/warden.d.ts +3 -2
- package/dist/src/trails/warden.d.ts.map +1 -1
- package/dist/src/trails/warden.js +37 -27
- package/dist/src/trails/warden.js.map +1 -1
- package/dist/src/versions.d.ts +12 -0
- package/dist/src/versions.d.ts.map +1 -0
- package/dist/src/versions.js +23 -0
- package/dist/src/versions.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -7
- package/src/__tests__/add-trail.test.ts +97 -0
- package/src/__tests__/create.test.ts +91 -27
- package/src/__tests__/draft-promote.test.ts +144 -0
- package/src/__tests__/guide.test.ts +10 -5
- package/src/__tests__/load-app.test.ts +406 -2
- package/src/__tests__/survey.test.ts +221 -60
- package/src/__tests__/topo-dev.test.ts +426 -0
- package/src/app.ts +24 -2
- package/src/clack.ts +1 -1
- package/src/cli.ts +4 -3
- package/src/trails/add-surface.ts +150 -0
- package/src/trails/add-trail.ts +46 -10
- package/src/trails/add-verify.ts +11 -6
- package/src/trails/create-scaffold.ts +16 -3
- package/src/trails/create.ts +76 -71
- package/src/trails/dev-clean.ts +77 -0
- package/src/trails/dev-reset.ts +45 -0
- package/src/trails/dev-stats.ts +67 -0
- package/src/trails/dev-support.ts +328 -0
- package/src/trails/draft-promote.ts +739 -0
- package/src/trails/guide.ts +23 -41
- package/src/trails/load-app.ts +556 -14
- package/src/trails/project.ts +17 -3
- package/src/trails/survey.ts +110 -285
- package/src/trails/topo-constants.ts +2 -0
- package/src/trails/topo-export.ts +35 -0
- package/src/trails/topo-history.ts +38 -0
- package/src/trails/topo-pin.ts +38 -0
- package/src/trails/topo-read-support.ts +329 -0
- package/src/trails/topo-reports.ts +228 -0
- package/src/trails/topo-show.ts +54 -0
- package/src/trails/topo-store-support.ts +104 -0
- package/src/trails/topo-support.ts +230 -0
- package/src/trails/topo-unpin.ts +56 -0
- package/src/trails/topo-verify.ts +25 -0
- package/src/trails/topo.ts +69 -0
- package/src/trails/warden.ts +13 -3
- package/src/versions.ts +43 -0
- package/tsconfig.tests.json +10 -0
- package/src/trails/add-trailhead.ts +0 -121
package/src/trails/add-trail.ts
CHANGED
|
@@ -12,40 +12,53 @@ import { z } from 'zod';
|
|
|
12
12
|
// Helpers
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
|
|
15
|
+
const literal = (value: string): string => JSON.stringify(value);
|
|
16
|
+
|
|
17
|
+
const deriveExampleMessage = (id: string): string => `${id} completed`;
|
|
18
|
+
|
|
15
19
|
const generateTrailFile = (
|
|
16
20
|
id: string,
|
|
21
|
+
description: string,
|
|
22
|
+
exampleName: string,
|
|
17
23
|
intent: 'read' | 'write' | 'destroy'
|
|
18
24
|
): string => {
|
|
19
25
|
const intentLine = intent === 'write' ? '' : `\n intent: '${intent}',`;
|
|
26
|
+
const exampleMessage = deriveExampleMessage(id);
|
|
20
27
|
|
|
21
28
|
return `import { Result, trail } from '@ontrails/core';
|
|
22
29
|
import { z } from 'zod';
|
|
23
30
|
|
|
24
31
|
export const ${id.replaceAll('.', '_')} = trail('${id}', {
|
|
25
|
-
|
|
32
|
+
blaze: async () => {
|
|
33
|
+
return Result.ok({ message: ${literal(exampleMessage)} });
|
|
34
|
+
},
|
|
35
|
+
description: ${literal(description)},
|
|
26
36
|
examples: [
|
|
27
37
|
{
|
|
38
|
+
expected: { message: ${literal(exampleMessage)} },
|
|
28
39
|
input: {},
|
|
29
|
-
name:
|
|
40
|
+
name: ${literal(exampleName)},
|
|
30
41
|
},
|
|
31
42
|
],
|
|
32
|
-
blaze: async (input) => {
|
|
33
|
-
return Result.ok({ message: 'TODO' });
|
|
34
|
-
},
|
|
35
43
|
input: z.object({}),${intentLine}
|
|
36
44
|
output: z.object({ message: z.string() }),
|
|
37
45
|
});
|
|
38
46
|
`;
|
|
39
47
|
};
|
|
40
48
|
|
|
41
|
-
const generateTestFile = (id: string): string => {
|
|
49
|
+
const generateTestFile = (id: string, exampleName: string): string => {
|
|
42
50
|
const moduleName = id.replaceAll('.', '-');
|
|
43
51
|
const trailName = id.replaceAll('.', '_');
|
|
52
|
+
const exampleMessage = deriveExampleMessage(id);
|
|
44
53
|
return `import { testTrail } from '@ontrails/testing';
|
|
45
54
|
import { ${trailName} } from '../src/trails/${moduleName}.js';
|
|
46
55
|
|
|
47
56
|
testTrail(${trailName}, [
|
|
48
|
-
{
|
|
57
|
+
{
|
|
58
|
+
description: ${literal(exampleName)},
|
|
59
|
+
expectValue: { message: ${literal(exampleMessage)} },
|
|
60
|
+
input: {},
|
|
61
|
+
},
|
|
49
62
|
]);
|
|
50
63
|
`;
|
|
51
64
|
};
|
|
@@ -64,14 +77,26 @@ const writeWithDirs = async (
|
|
|
64
77
|
};
|
|
65
78
|
|
|
66
79
|
export const addTrail = trail('add.trail', {
|
|
80
|
+
args: ['id'],
|
|
67
81
|
blaze: async (input, ctx) => {
|
|
68
82
|
const { id } = input;
|
|
69
83
|
const moduleName = id.replaceAll('.', '-');
|
|
70
84
|
const cwd = resolve(ctx.cwd ?? '.');
|
|
71
85
|
|
|
72
86
|
const files = new Map<string, string>([
|
|
73
|
-
[
|
|
74
|
-
|
|
87
|
+
[
|
|
88
|
+
`src/trails/${moduleName}.ts`,
|
|
89
|
+
generateTrailFile(
|
|
90
|
+
id,
|
|
91
|
+
input.description,
|
|
92
|
+
input.exampleName,
|
|
93
|
+
input.intent
|
|
94
|
+
),
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
`__tests__/${moduleName}.test.ts`,
|
|
98
|
+
generateTestFile(id, input.exampleName),
|
|
99
|
+
],
|
|
75
100
|
]);
|
|
76
101
|
|
|
77
102
|
for (const [relativePath, content] of files) {
|
|
@@ -82,7 +107,18 @@ export const addTrail = trail('add.trail', {
|
|
|
82
107
|
},
|
|
83
108
|
description: 'Scaffold a new trail with tests and examples',
|
|
84
109
|
input: z.object({
|
|
85
|
-
|
|
110
|
+
description: z
|
|
111
|
+
.string()
|
|
112
|
+
.min(1, 'Trail description is required')
|
|
113
|
+
.describe('Trail description'),
|
|
114
|
+
exampleName: z
|
|
115
|
+
.string()
|
|
116
|
+
.min(1, 'Starter example name is required')
|
|
117
|
+
.describe('Starter example name'),
|
|
118
|
+
id: z
|
|
119
|
+
.string()
|
|
120
|
+
.min(1, 'Trail ID is required')
|
|
121
|
+
.describe('Trail ID (e.g., entity.update)'),
|
|
86
122
|
intent: z
|
|
87
123
|
.enum(['read', 'write', 'destroy'])
|
|
88
124
|
.default('write')
|
package/src/trails/add-verify.ts
CHANGED
|
@@ -8,30 +8,35 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
8
8
|
import { Result, trail } from '@ontrails/core';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
|
|
11
|
+
import {
|
|
12
|
+
ontrailsPackageRange,
|
|
13
|
+
scaffoldDependencyVersions,
|
|
14
|
+
} from '../versions.js';
|
|
15
|
+
|
|
11
16
|
// ---------------------------------------------------------------------------
|
|
12
17
|
// Content generators
|
|
13
18
|
// ---------------------------------------------------------------------------
|
|
14
19
|
|
|
15
20
|
const generateTestFile = (): string =>
|
|
16
|
-
`import {
|
|
21
|
+
`import { testAllEstablished } from '@ontrails/testing';
|
|
17
22
|
import { app } from '../src/app.js';
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
testAllEstablished(app);
|
|
20
25
|
`;
|
|
21
26
|
|
|
22
27
|
const generateLefthookYml = (): string =>
|
|
23
28
|
`pre-push:
|
|
24
29
|
commands:
|
|
25
30
|
warden:
|
|
26
|
-
run: bunx trails warden
|
|
31
|
+
run: bunx trails warden
|
|
27
32
|
`;
|
|
28
33
|
|
|
29
34
|
/** Add testing and warden devDependencies to package.json when present. */
|
|
30
35
|
const patchVerifyDeps = (pkg: Record<string, unknown>): void => {
|
|
31
36
|
const devDeps = (pkg['devDependencies'] ?? {}) as Record<string, string>;
|
|
32
|
-
devDeps['@ontrails/testing'] =
|
|
33
|
-
devDeps['@ontrails/warden'] =
|
|
34
|
-
devDeps['lefthook'] =
|
|
37
|
+
devDeps['@ontrails/testing'] = ontrailsPackageRange;
|
|
38
|
+
devDeps['@ontrails/warden'] = ontrailsPackageRange;
|
|
39
|
+
devDeps['lefthook'] = scaffoldDependencyVersions.lefthook;
|
|
35
40
|
pkg['devDependencies'] = Object.fromEntries(
|
|
36
41
|
Object.entries(devDeps).toSorted(([a], [b]) => a.localeCompare(b))
|
|
37
42
|
);
|
|
@@ -10,6 +10,11 @@ import { dirname, join, resolve } from 'node:path';
|
|
|
10
10
|
import { Result, trail } from '@ontrails/core';
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
|
|
13
|
+
import {
|
|
14
|
+
ontrailsPackageRange,
|
|
15
|
+
scaffoldDependencyVersions,
|
|
16
|
+
} from '../versions.js';
|
|
17
|
+
|
|
13
18
|
// ---------------------------------------------------------------------------
|
|
14
19
|
// Types
|
|
15
20
|
// ---------------------------------------------------------------------------
|
|
@@ -28,14 +33,22 @@ interface ScaffoldResult {
|
|
|
28
33
|
|
|
29
34
|
const generatePackageJson = (name: string): string => {
|
|
30
35
|
const deps: Record<string, string> = {
|
|
31
|
-
'@ontrails/core':
|
|
32
|
-
zod:
|
|
36
|
+
'@ontrails/core': ontrailsPackageRange,
|
|
37
|
+
zod: scaffoldDependencyVersions.zod,
|
|
33
38
|
};
|
|
34
39
|
|
|
35
40
|
const pkg: Record<string, unknown> = {
|
|
36
41
|
dependencies: Object.fromEntries(
|
|
37
42
|
Object.entries(deps).toSorted(([a], [b]) => a.localeCompare(b))
|
|
38
43
|
),
|
|
44
|
+
devDependencies: Object.fromEntries(
|
|
45
|
+
Object.entries({
|
|
46
|
+
'@types/bun': scaffoldDependencyVersions.bunTypes,
|
|
47
|
+
oxlint: scaffoldDependencyVersions.oxlint,
|
|
48
|
+
typescript: scaffoldDependencyVersions.typescript,
|
|
49
|
+
ultracite: scaffoldDependencyVersions.ultracite,
|
|
50
|
+
}).toSorted(([a], [b]) => a.localeCompare(b))
|
|
51
|
+
),
|
|
39
52
|
name,
|
|
40
53
|
scripts: {
|
|
41
54
|
build: 'tsc -b',
|
|
@@ -73,7 +86,7 @@ const TSCONFIG_CONTENT = JSON.stringify(
|
|
|
73
86
|
const GITIGNORE_CONTENT = `node_modules/
|
|
74
87
|
dist/
|
|
75
88
|
*.tsbuildinfo
|
|
76
|
-
.trails/
|
|
89
|
+
.trails/_surface.json
|
|
77
90
|
`;
|
|
78
91
|
|
|
79
92
|
const OXLINTRC_CONTENT = JSON.stringify(
|
package/src/trails/create.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
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 type { CrossFn } from '@ontrails/core';
|
|
9
8
|
import { Result, trail } from '@ontrails/core';
|
|
10
9
|
import { z } from 'zod';
|
|
11
10
|
|
|
@@ -14,13 +13,13 @@ import { z } from 'zod';
|
|
|
14
13
|
// ---------------------------------------------------------------------------
|
|
15
14
|
|
|
16
15
|
type Starter = 'empty' | 'entity' | 'hello';
|
|
17
|
-
type
|
|
16
|
+
type Surface = 'cli' | 'http' | 'mcp';
|
|
18
17
|
|
|
19
18
|
interface CreateInput {
|
|
20
19
|
readonly dir?: string | undefined;
|
|
21
20
|
readonly name: string;
|
|
22
21
|
readonly starter: Starter;
|
|
23
|
-
readonly
|
|
22
|
+
readonly surfaces: readonly Surface[];
|
|
24
23
|
readonly verify: boolean;
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -42,15 +41,20 @@ interface ScaffoldedProject {
|
|
|
42
41
|
readonly name: string;
|
|
43
42
|
}
|
|
44
43
|
|
|
44
|
+
interface SurfaceResult {
|
|
45
|
+
readonly created: string;
|
|
46
|
+
readonly dependency: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
const buildScaffoldInput = (input: ScaffoldRequest) => ({
|
|
46
50
|
...(input.dir === undefined ? {} : { dir: input.dir }),
|
|
47
51
|
name: input.name,
|
|
48
52
|
starter: input.starter,
|
|
49
53
|
});
|
|
50
54
|
|
|
51
|
-
const
|
|
55
|
+
const buildSurfaceInput = (dir: string, surface: string) => ({
|
|
52
56
|
dir,
|
|
53
|
-
|
|
57
|
+
surface,
|
|
54
58
|
});
|
|
55
59
|
|
|
56
60
|
const buildVerifyInput = (input: VerifyRequest) => ({
|
|
@@ -58,24 +62,14 @@ const buildVerifyInput = (input: VerifyRequest) => ({
|
|
|
58
62
|
name: input.name,
|
|
59
63
|
});
|
|
60
64
|
|
|
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[]
|
|
65
|
+
const collectSurfaceFiles = async (
|
|
66
|
+
surfaces: readonly string[],
|
|
67
|
+
addSurface: (surface: string) => Promise<Result<SurfaceResult, Error>>
|
|
71
68
|
): Promise<Result<string[], Error>> => {
|
|
72
69
|
const created: string[] = [];
|
|
73
70
|
|
|
74
|
-
for (const
|
|
75
|
-
const result = await
|
|
76
|
-
'add.trailhead',
|
|
77
|
-
buildTrailheadInput(dir, trailhead)
|
|
78
|
-
);
|
|
71
|
+
for (const surface of surfaces) {
|
|
72
|
+
const result = await addSurface(surface);
|
|
79
73
|
if (result.isErr()) {
|
|
80
74
|
return Result.err(result.error);
|
|
81
75
|
}
|
|
@@ -86,17 +80,14 @@ const addTrailheadFiles = async (
|
|
|
86
80
|
};
|
|
87
81
|
|
|
88
82
|
const collectVerifyFiles = async (
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
shouldVerify: boolean,
|
|
84
|
+
addVerify: () => Promise<Result<{ created: string[] }, Error>>
|
|
91
85
|
): Promise<Result<string[], Error>> => {
|
|
92
|
-
if (!
|
|
86
|
+
if (!shouldVerify) {
|
|
93
87
|
return Result.ok([]);
|
|
94
88
|
}
|
|
95
89
|
|
|
96
|
-
const result = await
|
|
97
|
-
'add.verify',
|
|
98
|
-
buildVerifyInput(input)
|
|
99
|
-
);
|
|
90
|
+
const result = await addVerify();
|
|
100
91
|
return result.isErr()
|
|
101
92
|
? Result.err(result.error)
|
|
102
93
|
: Result.ok(result.value.created);
|
|
@@ -104,43 +95,9 @@ const collectVerifyFiles = async (
|
|
|
104
95
|
|
|
105
96
|
const collectCreatedFiles = (
|
|
106
97
|
scaffolded: readonly string[],
|
|
107
|
-
|
|
98
|
+
surfaces: readonly string[],
|
|
108
99
|
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
|
-
};
|
|
100
|
+
): string[] => [...scaffolded, ...surfaces, ...verify];
|
|
144
101
|
|
|
145
102
|
// ---------------------------------------------------------------------------
|
|
146
103
|
// Route definition
|
|
@@ -151,9 +108,52 @@ export const createRoute = trail('create', {
|
|
|
151
108
|
if (!ctx.cross) {
|
|
152
109
|
return Result.err(new Error('create route requires ctx.cross'));
|
|
153
110
|
}
|
|
154
|
-
|
|
111
|
+
const { cross } = ctx;
|
|
112
|
+
|
|
113
|
+
const scaffolded = await cross<ScaffoldedProject>(
|
|
114
|
+
'create.scaffold',
|
|
115
|
+
buildScaffoldInput(input)
|
|
116
|
+
);
|
|
117
|
+
if (scaffolded.isErr()) {
|
|
118
|
+
return Result.err(scaffolded.error);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const finishCreate = async (): Promise<
|
|
122
|
+
Result<{ created: string[]; dir: string; name: string }, Error>
|
|
123
|
+
> => {
|
|
124
|
+
const surfaceFiles = await collectSurfaceFiles(
|
|
125
|
+
input.surfaces,
|
|
126
|
+
(surface) =>
|
|
127
|
+
cross<SurfaceResult>(
|
|
128
|
+
'add.surface',
|
|
129
|
+
buildSurfaceInput(scaffolded.value.dir, surface)
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
if (surfaceFiles.isErr()) {
|
|
133
|
+
return Result.err(surfaceFiles.error);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const verifyFiles = await collectVerifyFiles(input.verify, () =>
|
|
137
|
+
cross<{ created: string[] }>('add.verify', buildVerifyInput(input))
|
|
138
|
+
);
|
|
139
|
+
if (verifyFiles.isErr()) {
|
|
140
|
+
return Result.err(verifyFiles.error);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return Result.ok({
|
|
144
|
+
created: collectCreatedFiles(
|
|
145
|
+
scaffolded.value.created,
|
|
146
|
+
surfaceFiles.value,
|
|
147
|
+
verifyFiles.value
|
|
148
|
+
),
|
|
149
|
+
dir: scaffolded.value.dir,
|
|
150
|
+
name: input.name,
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return finishCreate();
|
|
155
155
|
},
|
|
156
|
-
crosses: ['create.scaffold', 'add.
|
|
156
|
+
crosses: ['create.scaffold', 'add.surface', 'add.verify'],
|
|
157
157
|
description: 'Create a new Trails project',
|
|
158
158
|
fields: {
|
|
159
159
|
starter: {
|
|
@@ -171,7 +171,7 @@ export const createRoute = trail('create', {
|
|
|
171
171
|
{ hint: 'Just the structure', label: 'Empty', value: 'empty' },
|
|
172
172
|
],
|
|
173
173
|
},
|
|
174
|
-
|
|
174
|
+
surfaces: {
|
|
175
175
|
options: [
|
|
176
176
|
{ hint: 'Commander-based command line', label: 'CLI', value: 'cli' },
|
|
177
177
|
{
|
|
@@ -179,6 +179,11 @@ export const createRoute = trail('create', {
|
|
|
179
179
|
label: 'MCP',
|
|
180
180
|
value: 'mcp',
|
|
181
181
|
},
|
|
182
|
+
{
|
|
183
|
+
hint: 'Hono-powered HTTP endpoints',
|
|
184
|
+
label: 'HTTP',
|
|
185
|
+
value: 'http',
|
|
186
|
+
},
|
|
182
187
|
],
|
|
183
188
|
},
|
|
184
189
|
},
|
|
@@ -189,10 +194,10 @@ export const createRoute = trail('create', {
|
|
|
189
194
|
.enum(['hello', 'entity', 'empty'])
|
|
190
195
|
.default('hello')
|
|
191
196
|
.describe('Starter trail'),
|
|
192
|
-
|
|
193
|
-
.array(z.enum(['cli', 'mcp']))
|
|
197
|
+
surfaces: z
|
|
198
|
+
.array(z.enum(['cli', 'http', 'mcp']))
|
|
194
199
|
.default(['cli'])
|
|
195
|
-
.describe('
|
|
200
|
+
.describe('Surfaces'),
|
|
196
201
|
verify: z.boolean().default(true).describe('Include testing + warden'),
|
|
197
202
|
}),
|
|
198
203
|
output: z.object({
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Result, ValidationError, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
cleanDevState,
|
|
6
|
+
DEFAULT_TOPO_SNAPSHOT_RETENTION,
|
|
7
|
+
} from './dev-support.js';
|
|
8
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
9
|
+
|
|
10
|
+
export const devCleanTrail = trail('dev.clean', {
|
|
11
|
+
blaze: (input, ctx) => {
|
|
12
|
+
if (input.dryRun !== true && input.yes !== true) {
|
|
13
|
+
return Result.err(
|
|
14
|
+
new ValidationError(
|
|
15
|
+
'Refusing to clean local state without `--yes` or `--dry-run`.'
|
|
16
|
+
)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const rootDir = input.rootDir ?? ctx.cwd ?? process.cwd();
|
|
21
|
+
return Result.ok(
|
|
22
|
+
cleanDevState({
|
|
23
|
+
dryRun: input.dryRun,
|
|
24
|
+
maxAge: input.traceAgeMs,
|
|
25
|
+
maxRecords: input.traces,
|
|
26
|
+
rootDir,
|
|
27
|
+
snapshotRetention: input.snapshots,
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
description: 'Prune unpinned topo snapshots and old trace records',
|
|
32
|
+
examples: [
|
|
33
|
+
{
|
|
34
|
+
input: {
|
|
35
|
+
dryRun: true,
|
|
36
|
+
rootDir: createIsolatedExampleInput('dev-clean').rootDir,
|
|
37
|
+
},
|
|
38
|
+
name: 'Preview local cleanup',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
input: z.object({
|
|
42
|
+
dryRun: z
|
|
43
|
+
.boolean()
|
|
44
|
+
.default(true)
|
|
45
|
+
.describe('Preview cleanup without changing state'),
|
|
46
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
47
|
+
snapshots: z
|
|
48
|
+
.number()
|
|
49
|
+
.default(DEFAULT_TOPO_SNAPSHOT_RETENTION)
|
|
50
|
+
.describe('Unpinned topo snapshots to retain'),
|
|
51
|
+
traceAgeMs: z
|
|
52
|
+
.number()
|
|
53
|
+
.default(7 * 24 * 60 * 60 * 1000)
|
|
54
|
+
.describe('Maximum retained trace age in milliseconds'),
|
|
55
|
+
traces: z.number().default(10_000).describe('Maximum retained trace count'),
|
|
56
|
+
yes: z.boolean().default(false).describe('Confirm destructive changes'),
|
|
57
|
+
}),
|
|
58
|
+
intent: 'destroy',
|
|
59
|
+
output: z.object({
|
|
60
|
+
dryRun: z.boolean(),
|
|
61
|
+
remaining: z.object({
|
|
62
|
+
pinnedCount: z.number(),
|
|
63
|
+
snapshotCount: z.number(),
|
|
64
|
+
traceCount: z.number(),
|
|
65
|
+
}),
|
|
66
|
+
removed: z.object({
|
|
67
|
+
topoSnapshots: z.number(),
|
|
68
|
+
traceRecords: z.number(),
|
|
69
|
+
}),
|
|
70
|
+
retention: z.object({
|
|
71
|
+
snapshots: z.number(),
|
|
72
|
+
traceAgeMs: z.number(),
|
|
73
|
+
traces: z.number(),
|
|
74
|
+
}),
|
|
75
|
+
}),
|
|
76
|
+
permit: { scopes: ['dev:clean'] },
|
|
77
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Result, ValidationError, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import { resetDevState } from './dev-support.js';
|
|
5
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
6
|
+
|
|
7
|
+
export const devResetTrail = trail('dev.reset', {
|
|
8
|
+
blaze: (input, ctx) => {
|
|
9
|
+
if (input.dryRun !== true && input.yes !== true) {
|
|
10
|
+
return Result.err(
|
|
11
|
+
new ValidationError(
|
|
12
|
+
'Refusing to reset local state without `--yes` or `--dry-run`.'
|
|
13
|
+
)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const rootDir = input.rootDir ?? ctx.cwd ?? process.cwd();
|
|
18
|
+
return Result.ok(resetDevState({ dryRun: input.dryRun, rootDir }));
|
|
19
|
+
},
|
|
20
|
+
description: 'Remove local Trails database artifacts',
|
|
21
|
+
examples: [
|
|
22
|
+
{
|
|
23
|
+
input: {
|
|
24
|
+
dryRun: true,
|
|
25
|
+
rootDir: createIsolatedExampleInput('dev-reset').rootDir,
|
|
26
|
+
},
|
|
27
|
+
name: 'Preview local reset',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
input: z.object({
|
|
31
|
+
dryRun: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.default(true)
|
|
34
|
+
.describe('Preview reset without changing state'),
|
|
35
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
36
|
+
yes: z.boolean().default(false).describe('Confirm destructive changes'),
|
|
37
|
+
}),
|
|
38
|
+
intent: 'destroy',
|
|
39
|
+
output: z.object({
|
|
40
|
+
dryRun: z.boolean(),
|
|
41
|
+
removedCount: z.number(),
|
|
42
|
+
removedFiles: z.array(z.string()),
|
|
43
|
+
}),
|
|
44
|
+
permit: { scopes: ['dev:reset'] },
|
|
45
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Result, trail } from '@ontrails/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildDevStats,
|
|
6
|
+
DEFAULT_TOPO_SNAPSHOT_RETENTION,
|
|
7
|
+
} from './dev-support.js';
|
|
8
|
+
import { createIsolatedExampleInput } from './topo-support.js';
|
|
9
|
+
|
|
10
|
+
export const devStatsTrail = trail('dev.stats', {
|
|
11
|
+
blaze: (input, ctx) => {
|
|
12
|
+
const rootDir = input.rootDir ?? ctx.cwd ?? process.cwd();
|
|
13
|
+
return Result.ok(
|
|
14
|
+
buildDevStats({
|
|
15
|
+
maxAge: input.traceAgeMs,
|
|
16
|
+
maxRecords: input.traces,
|
|
17
|
+
rootDir,
|
|
18
|
+
snapshotRetention: input.snapshots,
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
description: 'Show local Trails workspace state and retention',
|
|
23
|
+
examples: [
|
|
24
|
+
{
|
|
25
|
+
input: { rootDir: createIsolatedExampleInput('dev-stats').rootDir },
|
|
26
|
+
name: 'Show local dev state',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
input: z.object({
|
|
30
|
+
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
31
|
+
snapshots: z
|
|
32
|
+
.number()
|
|
33
|
+
.default(DEFAULT_TOPO_SNAPSHOT_RETENTION)
|
|
34
|
+
.describe('Unpinned topo snapshots to retain'),
|
|
35
|
+
traceAgeMs: z
|
|
36
|
+
.number()
|
|
37
|
+
.default(7 * 24 * 60 * 60 * 1000)
|
|
38
|
+
.describe('Maximum retained trace age in milliseconds'),
|
|
39
|
+
traces: z.number().default(10_000).describe('Maximum retained trace count'),
|
|
40
|
+
}),
|
|
41
|
+
intent: 'read',
|
|
42
|
+
output: z.object({
|
|
43
|
+
db: z.object({
|
|
44
|
+
exists: z.boolean(),
|
|
45
|
+
fileSizeBytes: z.number(),
|
|
46
|
+
path: z.string(),
|
|
47
|
+
}),
|
|
48
|
+
lock: z.object({
|
|
49
|
+
exists: z.boolean(),
|
|
50
|
+
fileSizeBytes: z.number(),
|
|
51
|
+
path: z.string(),
|
|
52
|
+
}),
|
|
53
|
+
retention: z.object({
|
|
54
|
+
snapshots: z.number(),
|
|
55
|
+
traceAgeMs: z.number(),
|
|
56
|
+
traces: z.number(),
|
|
57
|
+
}),
|
|
58
|
+
topo: z.object({
|
|
59
|
+
pinnedCount: z.number(),
|
|
60
|
+
prunableSnapshotCount: z.number(),
|
|
61
|
+
snapshotCount: z.number(),
|
|
62
|
+
}),
|
|
63
|
+
tracing: z.object({
|
|
64
|
+
recordCount: z.number(),
|
|
65
|
+
}),
|
|
66
|
+
}),
|
|
67
|
+
});
|