@ontrails/trails 1.0.0-beta.2 → 1.0.0-beta.22
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 +647 -0
- package/README.md +26 -0
- package/package.json +28 -7
- package/src/app.ts +86 -2
- package/src/clack.ts +22 -0
- package/src/cli.ts +330 -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 +153 -0
- package/src/mcp-app.ts +30 -0
- package/src/mcp-options.ts +77 -0
- package/src/mcp.ts +8 -0
- package/src/project-writes.ts +377 -0
- package/src/release/bindings.ts +39 -0
- package/src/release/check.ts +818 -0
- package/src/release/config.ts +63 -0
- package/src/release/contract-facts.ts +425 -0
- package/src/release/index.ts +85 -0
- package/src/release/native-bun-publish.ts +651 -0
- package/src/release/native-bun-registry.ts +350 -0
- package/src/release/packed-artifacts-smoke.ts +236 -0
- package/src/release/smoke.ts +46 -0
- package/src/release/wayfinder-dogfood-smoke.ts +226 -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-trace.ts +273 -0
- package/src/run-warden.ts +39 -0
- package/src/run-watch.ts +432 -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 +94 -40
- package/src/trails/add-trail.ts +79 -41
- package/src/trails/add-verify.ts +95 -25
- package/src/trails/compile.ts +67 -0
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-adapter.ts +1084 -0
- package/src/trails/create-scaffold.ts +399 -104
- package/src/trails/create-versions.ts +62 -0
- package/src/trails/create.ts +185 -71
- 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 +340 -0
- package/src/trails/doctor.ts +56 -0
- package/src/trails/draft-promote.ts +949 -0
- package/src/trails/guide.ts +74 -68
- package/src/trails/load-app.ts +1143 -15
- package/src/trails/project.ts +17 -3
- package/src/trails/release-check.ts +104 -0
- package/src/trails/release-smoke.ts +48 -0
- package/src/trails/revise.ts +53 -0
- package/src/trails/root-dir.ts +21 -0
- package/src/trails/run-example.ts +491 -0
- package/src/trails/run-examples.ts +145 -0
- package/src/trails/run.ts +410 -0
- package/src/trails/scaffold-json.ts +58 -0
- package/src/trails/survey.ts +881 -226
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-constants.ts +2 -0
- package/src/trails/topo-history.ts +47 -0
- package/src/trails/topo-output-schemas.ts +248 -0
- package/src/trails/topo-pin.ts +52 -0
- package/src/trails/topo-read-support.ts +313 -0
- package/src/trails/topo-reports.ts +807 -0
- package/src/trails/topo-store-support.ts +174 -0
- package/src/trails/topo-support.ts +220 -0
- package/src/trails/topo-unpin.ts +61 -0
- package/src/trails/topo.ts +106 -0
- package/src/trails/validate.ts +38 -0
- package/src/trails/version-lifecycle-support.ts +945 -0
- package/src/trails/warden-guide.ts +129 -0
- package/src/trails/warden.ts +165 -58
- 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 -62
- 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 -11
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -85
- 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 -33
- package/dist/src/trails/survey.d.ts.map +0 -1
- package/dist/src/trails/survey.js +0 -225
- 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 -161
- package/src/__tests__/warden.test.ts +0 -74
- package/tsconfig.json +0 -9
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `warden` trail -- Governance checks.
|
|
3
|
-
*
|
|
4
|
-
* Thin wrapper around `runWarden` and `formatWardenReport` from @ontrails/warden.
|
|
5
|
-
*/
|
|
6
|
-
import { Result, trail } from '@ontrails/core';
|
|
7
|
-
import { formatGitHubAnnotations, formatJson, formatSummary, formatWardenReport, runWarden, } from '@ontrails/warden';
|
|
8
|
-
import { z } from 'zod';
|
|
9
|
-
import { loadApp } from './load-app.js';
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
// Trail definition
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
export const wardenTrail = trail('warden', {
|
|
14
|
-
description: 'Run governance checks (lint + drift)',
|
|
15
|
-
examples: [
|
|
16
|
-
{
|
|
17
|
-
input: {
|
|
18
|
-
driftOnly: false,
|
|
19
|
-
lintOnly: false,
|
|
20
|
-
},
|
|
21
|
-
name: 'Default warden run',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
input: {
|
|
25
|
-
format: 'github',
|
|
26
|
-
},
|
|
27
|
-
name: 'GitHub Actions annotations',
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
implementation: async (input, ctx) => {
|
|
31
|
-
const rootDir = input.rootDir ?? ctx.cwd ?? process.cwd();
|
|
32
|
-
// oxlint-disable-next-line prefer-await-to-then -- catch converts rejection to undefined cleanly
|
|
33
|
-
const topo = await loadApp('./src/app.ts', rootDir).catch(() => undefined);
|
|
34
|
-
const report = await runWarden({
|
|
35
|
-
driftOnly: input.driftOnly,
|
|
36
|
-
lintOnly: input.lintOnly,
|
|
37
|
-
rootDir,
|
|
38
|
-
topo,
|
|
39
|
-
});
|
|
40
|
-
const formatters = {
|
|
41
|
-
github: formatGitHubAnnotations,
|
|
42
|
-
json: formatJson,
|
|
43
|
-
summary: formatSummary,
|
|
44
|
-
text: formatWardenReport,
|
|
45
|
-
};
|
|
46
|
-
const formatter = formatters[input.format] ?? formatWardenReport;
|
|
47
|
-
const formatted = formatter(report);
|
|
48
|
-
return Result.ok({
|
|
49
|
-
diagnostics: report.diagnostics,
|
|
50
|
-
drift: report.drift,
|
|
51
|
-
errorCount: report.errorCount,
|
|
52
|
-
formatted,
|
|
53
|
-
passed: report.passed,
|
|
54
|
-
warnCount: report.warnCount,
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
input: z.object({
|
|
58
|
-
driftOnly: z.boolean().default(false).describe('Only run drift detection'),
|
|
59
|
-
format: z
|
|
60
|
-
.enum(['text', 'json', 'github', 'summary'])
|
|
61
|
-
.default('text')
|
|
62
|
-
.describe('Output format: text, json, github, or summary'),
|
|
63
|
-
lintOnly: z.boolean().default(false).describe('Only run lint rules'),
|
|
64
|
-
rootDir: z.string().optional().describe('Root directory to scan'),
|
|
65
|
-
}),
|
|
66
|
-
output: z.object({
|
|
67
|
-
diagnostics: z.array(z.object({
|
|
68
|
-
filePath: z.string(),
|
|
69
|
-
line: z.number(),
|
|
70
|
-
message: z.string(),
|
|
71
|
-
rule: z.string(),
|
|
72
|
-
severity: z.enum(['error', 'warn']),
|
|
73
|
-
})),
|
|
74
|
-
drift: z
|
|
75
|
-
.object({
|
|
76
|
-
committedHash: z.string().nullable(),
|
|
77
|
-
currentHash: z.string(),
|
|
78
|
-
stale: z.boolean(),
|
|
79
|
-
})
|
|
80
|
-
.nullable(),
|
|
81
|
-
errorCount: z.number(),
|
|
82
|
-
formatted: z.string(),
|
|
83
|
-
passed: z.boolean(),
|
|
84
|
-
warnCount: z.number(),
|
|
85
|
-
}),
|
|
86
|
-
readOnly: true,
|
|
87
|
-
});
|
|
88
|
-
//# sourceMappingURL=warden.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"warden.js","sourceRoot":"","sources":["../../../src/trails/warden.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"root":["../src/app.ts","../src/clack.ts","../src/cli.ts","../src/trails/add-surface.ts","../src/trails/add-trail.ts","../src/trails/add-verify.ts","../src/trails/create-scaffold.ts","../src/trails/create.ts","../src/trails/guide.ts","../src/trails/load-app.ts","../src/trails/project.ts","../src/trails/survey.ts","../src/trails/warden.ts","../bin/trails.ts"],"version":"5.9.3"}
|
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import {
|
|
3
|
-
existsSync,
|
|
4
|
-
mkdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
rmSync,
|
|
7
|
-
writeFileSync,
|
|
8
|
-
} from 'node:fs';
|
|
9
|
-
import { tmpdir } from 'node:os';
|
|
10
|
-
import { basename, dirname, join } from 'node:path';
|
|
11
|
-
|
|
12
|
-
import { Result } from '@ontrails/core';
|
|
13
|
-
|
|
14
|
-
import { addSurface } from '../trails/add-surface.js';
|
|
15
|
-
import { addVerify } from '../trails/add-verify.js';
|
|
16
|
-
import { createRoute } from '../trails/create.js';
|
|
17
|
-
import { createScaffold } from '../trails/create-scaffold.js';
|
|
18
|
-
import { isInsideProject } from '../trails/project.js';
|
|
19
|
-
|
|
20
|
-
type Starter = 'empty' | 'entity' | 'hello';
|
|
21
|
-
type Surface = 'cli' | 'mcp';
|
|
22
|
-
|
|
23
|
-
const makeTempProject = (): string =>
|
|
24
|
-
join(
|
|
25
|
-
tmpdir(),
|
|
26
|
-
`trails-blaze-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const readJson = (dir: string, relativePath: string): Record<string, unknown> =>
|
|
30
|
-
JSON.parse(readFileSync(join(dir, relativePath), 'utf8')) as Record<
|
|
31
|
-
string,
|
|
32
|
-
unknown
|
|
33
|
-
>;
|
|
34
|
-
|
|
35
|
-
const readText = (dir: string, relativePath: string): string =>
|
|
36
|
-
readFileSync(join(dir, relativePath), 'utf8');
|
|
37
|
-
|
|
38
|
-
const expectPaths = (
|
|
39
|
-
dir: string,
|
|
40
|
-
relativePaths: readonly string[],
|
|
41
|
-
expected: boolean
|
|
42
|
-
): void => {
|
|
43
|
-
for (const relativePath of relativePaths) {
|
|
44
|
-
expect(existsSync(join(dir, relativePath))).toBe(expected);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const expectContainsAll = (
|
|
49
|
-
content: string,
|
|
50
|
-
snippets: readonly string[]
|
|
51
|
-
): void => {
|
|
52
|
-
for (const snippet of snippets) {
|
|
53
|
-
expect(content).toContain(snippet);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const expectOk = <T>(result: Result<T, Error>): T => {
|
|
58
|
-
if (result.isErr()) {
|
|
59
|
-
throw result.error;
|
|
60
|
-
}
|
|
61
|
-
return result.value;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const expectErr = <T>(result: Result<T, Error>): Error => {
|
|
65
|
-
if (result.isOk()) {
|
|
66
|
-
throw new Error('Expected error result');
|
|
67
|
-
}
|
|
68
|
-
return result.error;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const runFollow = async (
|
|
72
|
-
id: string,
|
|
73
|
-
input: unknown
|
|
74
|
-
): Promise<Result<unknown, Error>> => {
|
|
75
|
-
switch (id) {
|
|
76
|
-
case 'create.scaffold': {
|
|
77
|
-
return await createScaffold.implementation(input as never, {} as never);
|
|
78
|
-
}
|
|
79
|
-
case 'add.surface': {
|
|
80
|
-
return await addSurface.implementation(input as never, {} as never);
|
|
81
|
-
}
|
|
82
|
-
case 'add.verify': {
|
|
83
|
-
return await addVerify.implementation(input as never, {} as never);
|
|
84
|
-
}
|
|
85
|
-
default: {
|
|
86
|
-
return Result.err(new Error(`Unknown follow target: ${id}`));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const runBlaze = (
|
|
92
|
-
projectDir: string,
|
|
93
|
-
overrides?: Partial<{
|
|
94
|
-
starter: Starter;
|
|
95
|
-
surfaces: readonly Surface[];
|
|
96
|
-
verify: boolean;
|
|
97
|
-
}>
|
|
98
|
-
) =>
|
|
99
|
-
createRoute.implementation(
|
|
100
|
-
{
|
|
101
|
-
dir: dirname(projectDir),
|
|
102
|
-
name: basename(projectDir),
|
|
103
|
-
starter: overrides?.starter ?? 'hello',
|
|
104
|
-
surfaces: [...(overrides?.surfaces ?? ['cli'])],
|
|
105
|
-
verify: overrides?.verify ?? true,
|
|
106
|
-
},
|
|
107
|
-
{ follow: runFollow } as never
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const setupMinimalProject = (dir: string): void => {
|
|
111
|
-
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
112
|
-
mkdirSync(join(dir, '.trails'), { recursive: true });
|
|
113
|
-
writeFileSync(
|
|
114
|
-
join(dir, 'src', 'app.ts'),
|
|
115
|
-
"import { topo } from '@ontrails/core';\nexport const app = topo('test');\n"
|
|
116
|
-
);
|
|
117
|
-
writeFileSync(
|
|
118
|
-
join(dir, 'package.json'),
|
|
119
|
-
JSON.stringify(
|
|
120
|
-
{ dependencies: { '@ontrails/core': 'workspace:*' }, name: 'test' },
|
|
121
|
-
null,
|
|
122
|
-
2
|
|
123
|
-
)
|
|
124
|
-
);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const assertDefaultProjectFiles = (dir: string): void => {
|
|
128
|
-
expectPaths(
|
|
129
|
-
dir,
|
|
130
|
-
[
|
|
131
|
-
'package.json',
|
|
132
|
-
'tsconfig.json',
|
|
133
|
-
'.gitignore',
|
|
134
|
-
'.oxlintrc.json',
|
|
135
|
-
'.oxfmtrc.jsonc',
|
|
136
|
-
'src/app.ts',
|
|
137
|
-
'.trails',
|
|
138
|
-
'src/cli.ts',
|
|
139
|
-
'src/trails/hello.ts',
|
|
140
|
-
'__tests__/examples.test.ts',
|
|
141
|
-
'lefthook.yml',
|
|
142
|
-
],
|
|
143
|
-
true
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const assertCliPackage = (dir: string): void => {
|
|
148
|
-
const pkg = readJson(dir, 'package.json');
|
|
149
|
-
expect(pkg['name']).toBe(basename(dir));
|
|
150
|
-
|
|
151
|
-
const deps = pkg['dependencies'] as Record<string, string>;
|
|
152
|
-
expect(deps['@ontrails/core']).toBe('workspace:*');
|
|
153
|
-
expect(deps['@ontrails/cli']).toBe('workspace:*');
|
|
154
|
-
expect(deps['commander']).toBeDefined();
|
|
155
|
-
|
|
156
|
-
const devDeps = pkg['devDependencies'] as Record<string, string>;
|
|
157
|
-
expect(devDeps['@ontrails/testing']).toBe('workspace:*');
|
|
158
|
-
expect(devDeps['@ontrails/warden']).toBe('workspace:*');
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const assertHelloApp = (dir: string): void => {
|
|
162
|
-
expectContainsAll(readText(dir, 'src/app.ts'), [
|
|
163
|
-
'topo',
|
|
164
|
-
`'${basename(dir)}'`,
|
|
165
|
-
'hello',
|
|
166
|
-
]);
|
|
167
|
-
expectContainsAll(readText(dir, 'src/trails/hello.ts'), [
|
|
168
|
-
"import { Result, trail } from '@ontrails/core'",
|
|
169
|
-
'return Result.ok({ message:',
|
|
170
|
-
]);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const assertEntityStarter = (dir: string): void => {
|
|
174
|
-
expectPaths(
|
|
175
|
-
dir,
|
|
176
|
-
[
|
|
177
|
-
'src/trails/entity.ts',
|
|
178
|
-
'src/trails/search.ts',
|
|
179
|
-
'src/trails/onboard.ts',
|
|
180
|
-
'src/events/entity-events.ts',
|
|
181
|
-
'src/store.ts',
|
|
182
|
-
],
|
|
183
|
-
true
|
|
184
|
-
);
|
|
185
|
-
expectPaths(dir, ['src/trails/hello.ts'], false);
|
|
186
|
-
expectContainsAll(readText(dir, 'src/app.ts'), [
|
|
187
|
-
"import * as entity from './trails/entity.js'",
|
|
188
|
-
"import * as search from './trails/search.js'",
|
|
189
|
-
"import * as onboard from './trails/onboard.js'",
|
|
190
|
-
"import * as entityEvents from './events/entity-events.js'",
|
|
191
|
-
]);
|
|
192
|
-
expectContainsAll(readText(dir, 'src/trails/entity.ts'), [
|
|
193
|
-
"import { Result, trail } from '@ontrails/core'",
|
|
194
|
-
'return Result.ok({ id: input.id, name:',
|
|
195
|
-
"return Result.ok({ id: '1', name: input.name })",
|
|
196
|
-
]);
|
|
197
|
-
expectContainsAll(readText(dir, 'src/trails/search.ts'), [
|
|
198
|
-
"import { Result, trail } from '@ontrails/core'",
|
|
199
|
-
'return Result.ok({ results: [] })',
|
|
200
|
-
]);
|
|
201
|
-
expectContainsAll(readText(dir, 'src/trails/onboard.ts'), [
|
|
202
|
-
"import { Result, hike } from '@ontrails/core'",
|
|
203
|
-
'return Result.ok({ onboarded: true })',
|
|
204
|
-
]);
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
const assertMcpSurface = (dir: string): void => {
|
|
208
|
-
expectPaths(dir, ['src/mcp.ts'], true);
|
|
209
|
-
expectPaths(dir, ['src/cli.ts'], false);
|
|
210
|
-
expectContainsAll(readText(dir, 'src/mcp.ts'), [
|
|
211
|
-
"import { blaze } from '@ontrails/mcp'",
|
|
212
|
-
'await blaze(app)',
|
|
213
|
-
]);
|
|
214
|
-
|
|
215
|
-
const deps = readJson(dir, 'package.json')['dependencies'] as Record<
|
|
216
|
-
string,
|
|
217
|
-
string
|
|
218
|
-
>;
|
|
219
|
-
expect(deps['@ontrails/mcp']).toBe('workspace:*');
|
|
220
|
-
expect(deps['@ontrails/cli']).toBeUndefined();
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
const assertVerifySkipped = (dir: string): void => {
|
|
224
|
-
expectPaths(dir, ['__tests__/examples.test.ts', 'lefthook.yml'], false);
|
|
225
|
-
expect(readJson(dir, 'package.json')['devDependencies']).toBeUndefined();
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const assertEmptyStarter = (dir: string): void => {
|
|
229
|
-
expectPaths(dir, ['src/trails/.gitkeep'], true);
|
|
230
|
-
expectPaths(dir, ['src/trails/hello.ts'], false);
|
|
231
|
-
const appContent = readText(dir, 'src/app.ts');
|
|
232
|
-
expect(appContent).toContain(`topo('${basename(dir)}')`);
|
|
233
|
-
expect(appContent).not.toContain('import * as');
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
const withTempProject = async (
|
|
237
|
-
assertion: (dir: string) => Promise<void>
|
|
238
|
-
): Promise<void> => {
|
|
239
|
-
const dir = makeTempProject();
|
|
240
|
-
try {
|
|
241
|
-
await assertion(dir);
|
|
242
|
-
} finally {
|
|
243
|
-
rmSync(dir, { force: true, recursive: true });
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
describe('trails blaze', () => {
|
|
248
|
-
describe('create mode', () => {
|
|
249
|
-
test('generates project structure with defaults', async () => {
|
|
250
|
-
await withTempProject(async (dir) => {
|
|
251
|
-
expectOk(await runBlaze(dir));
|
|
252
|
-
assertDefaultProjectFiles(dir);
|
|
253
|
-
assertCliPackage(dir);
|
|
254
|
-
assertHelloApp(dir);
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
test('generates with entity starter', async () => {
|
|
259
|
-
await withTempProject(async (dir) => {
|
|
260
|
-
expectOk(await runBlaze(dir, { starter: 'entity' }));
|
|
261
|
-
assertEntityStarter(dir);
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
test('generates with MCP surface', async () => {
|
|
266
|
-
await withTempProject(async (dir) => {
|
|
267
|
-
expectOk(await runBlaze(dir, { surfaces: ['mcp'] }));
|
|
268
|
-
assertMcpSurface(dir);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
test('skips verification when verify is false', async () => {
|
|
273
|
-
await withTempProject(async (dir) => {
|
|
274
|
-
expectOk(await runBlaze(dir, { verify: false }));
|
|
275
|
-
assertVerifySkipped(dir);
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
test('generates with empty starter', async () => {
|
|
280
|
-
await withTempProject(async (dir) => {
|
|
281
|
-
expectOk(await runBlaze(dir, { starter: 'empty' }));
|
|
282
|
-
assertEmptyStarter(dir);
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
describe('add-surface mode', () => {
|
|
288
|
-
test('adds MCP to existing project', async () => {
|
|
289
|
-
await withTempProject(async (dir) => {
|
|
290
|
-
setupMinimalProject(dir);
|
|
291
|
-
const result = expectOk(
|
|
292
|
-
await addSurface.implementation({ dir, surface: 'mcp' }, {} as never)
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
expect(result.created).toBe('src/mcp.ts');
|
|
296
|
-
expect(result.dependency).toBe('@ontrails/mcp');
|
|
297
|
-
expectPaths(dir, ['src/mcp.ts'], true);
|
|
298
|
-
expectContainsAll(readText(dir, 'src/mcp.ts'), [
|
|
299
|
-
"import { blaze } from '@ontrails/mcp'",
|
|
300
|
-
]);
|
|
301
|
-
const deps = readJson(dir, 'package.json')['dependencies'] as Record<
|
|
302
|
-
string,
|
|
303
|
-
string
|
|
304
|
-
>;
|
|
305
|
-
expect(deps['@ontrails/mcp']).toBe('workspace:*');
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
test('detects already-blazed surface', async () => {
|
|
310
|
-
await withTempProject(async (dir) => {
|
|
311
|
-
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
312
|
-
mkdirSync(join(dir, '.trails'), { recursive: true });
|
|
313
|
-
writeFileSync(join(dir, 'src', 'mcp.ts'), 'existing content');
|
|
314
|
-
|
|
315
|
-
const error = expectErr(
|
|
316
|
-
await addSurface.implementation({ dir, surface: 'mcp' }, {} as never)
|
|
317
|
-
);
|
|
318
|
-
expect(error.message).toBe('MCP is already blazed. Nothing to do.');
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
describe('isInsideProject', () => {
|
|
325
|
-
test('detects .trails directory', async () => {
|
|
326
|
-
await withTempProject(async (dir) => {
|
|
327
|
-
mkdirSync(join(dir, '.trails'), { recursive: true });
|
|
328
|
-
expect(await isInsideProject(dir)).toBe(true);
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
test('detects topo in src/', async () => {
|
|
333
|
-
await withTempProject(async (dir) => {
|
|
334
|
-
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
335
|
-
writeFileSync(
|
|
336
|
-
join(dir, 'src', 'app.ts'),
|
|
337
|
-
"import { topo } from '@ontrails/core';\nexport const app = topo('app');\n"
|
|
338
|
-
);
|
|
339
|
-
expect(await isInsideProject(dir)).toBe(true);
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
test('returns false for empty directory', async () => {
|
|
344
|
-
await withTempProject(async (dir) => {
|
|
345
|
-
mkdirSync(dir, { recursive: true });
|
|
346
|
-
expect(await isInsideProject(dir)).toBe(false);
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
});
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
|
|
3
|
-
import type { AnyTrail } from '@ontrails/core';
|
|
4
|
-
import { trail, topo, Result } from '@ontrails/core';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Test fixtures
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
const helloTrail = trail('hello', {
|
|
12
|
-
description: 'Say hello',
|
|
13
|
-
detours: {
|
|
14
|
-
NotFoundError: ['search'],
|
|
15
|
-
},
|
|
16
|
-
examples: [
|
|
17
|
-
{
|
|
18
|
-
expected: { message: 'Hello, world!' },
|
|
19
|
-
input: {},
|
|
20
|
-
name: 'Default greeting',
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
expected: { message: 'Hello, Trails!' },
|
|
24
|
-
input: { name: 'Trails' },
|
|
25
|
-
name: 'Named greeting',
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
implementation: (input) => {
|
|
29
|
-
const name = input.name ?? 'world';
|
|
30
|
-
return Result.ok({ message: `Hello, ${name}!` });
|
|
31
|
-
},
|
|
32
|
-
input: z.object({ name: z.string().optional() }),
|
|
33
|
-
output: z.object({ message: z.string() }),
|
|
34
|
-
readOnly: true,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const app = topo('test-app', { hello: helloTrail });
|
|
38
|
-
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
// Tests
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
|
|
43
|
-
describe('trails guide', () => {
|
|
44
|
-
test('lists trails with descriptions', () => {
|
|
45
|
-
const items = app.list();
|
|
46
|
-
expect(items.length).toBe(1);
|
|
47
|
-
|
|
48
|
-
const [hello] = items;
|
|
49
|
-
expect(hello).toBeDefined();
|
|
50
|
-
expect(hello?.id).toBe('hello');
|
|
51
|
-
|
|
52
|
-
expect((hello as AnyTrail).description).toBe('Say hello');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('trail detail includes examples', () => {
|
|
56
|
-
const item = app.get('hello') as AnyTrail;
|
|
57
|
-
expect(item).toBeDefined();
|
|
58
|
-
expect(item.examples).toBeDefined();
|
|
59
|
-
expect(item.examples?.length).toBe(2);
|
|
60
|
-
expect(item.examples?.[0]?.name).toBe('Default greeting');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('JSON output for trail is valid', () => {
|
|
64
|
-
const t = app.get('hello') as AnyTrail;
|
|
65
|
-
expect(t).toBeDefined();
|
|
66
|
-
|
|
67
|
-
const json = JSON.stringify({
|
|
68
|
-
description: t.description,
|
|
69
|
-
detours: t.detours,
|
|
70
|
-
examples: t.examples,
|
|
71
|
-
id: t.id,
|
|
72
|
-
kind: t.kind,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const parsed = JSON.parse(json) as Record<string, unknown>;
|
|
76
|
-
expect(parsed['id']).toBe('hello');
|
|
77
|
-
expect(parsed['kind']).toBe('trail');
|
|
78
|
-
expect(parsed['description']).toBe('Say hello');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('non-existent trail returns undefined from topo', () => {
|
|
82
|
-
const item = app.get('does-not-exist');
|
|
83
|
-
expect(item).toBeUndefined();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('detours are accessible on trail', () => {
|
|
87
|
-
const t = app.get('hello') as AnyTrail;
|
|
88
|
-
expect(t).toBeDefined();
|
|
89
|
-
expect(t.detours?.['NotFoundError']).toEqual(['search']);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { loadApp } from '../trails/load-app.js';
|
|
5
|
-
|
|
6
|
-
describe('loadApp', () => {
|
|
7
|
-
test('resolves relative module paths from cwd', async () => {
|
|
8
|
-
// import.meta.dir is src/__tests__/, go up two to get apps/trails/
|
|
9
|
-
const cwd = resolve(import.meta.dir, '../..');
|
|
10
|
-
const app = await loadApp('./src/app.ts', cwd);
|
|
11
|
-
|
|
12
|
-
expect(app.name).toBe('trails');
|
|
13
|
-
expect(app.get('survey')).toBeDefined();
|
|
14
|
-
});
|
|
15
|
-
});
|