@ontrails/trails 1.0.0-beta.15 → 1.0.0-beta.17
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 +215 -2
- package/README.md +27 -0
- package/package.json +21 -8
- package/src/app.ts +15 -5
- package/src/cli.ts +303 -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 +45 -23
- package/src/trails/add-trail.ts +27 -17
- package/src/trails/add-verify.ts +57 -17
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-scaffold.ts +86 -33
- package/src/trails/create.ts +11 -3
- package/src/trails/dev-clean.ts +6 -1
- package/src/trails/dev-reset.ts +6 -1
- package/src/trails/dev-stats.ts +6 -1
- package/src/trails/dev-support.ts +29 -17
- package/src/trails/draft-promote.ts +289 -80
- package/src/trails/guide.ts +54 -34
- package/src/trails/load-app.ts +251 -56
- 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 +534 -200
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-compile.ts +55 -0
- package/src/trails/topo-history.ts +6 -1
- package/src/trails/topo-output-schemas.ts +219 -0
- package/src/trails/topo-pin.ts +19 -6
- package/src/trails/topo-read-support.ts +171 -228
- package/src/trails/topo-reports.ts +587 -25
- package/src/trails/topo-store-support.ts +43 -19
- package/src/trails/topo-support.ts +18 -28
- package/src/trails/topo-unpin.ts +6 -1
- package/src/trails/topo-verify.ts +18 -5
- package/src/trails/topo.ts +60 -23
- package/src/trails/warden-guide.ts +121 -0
- package/src/trails/warden.ts +137 -56
- package/src/versions.ts +3 -18
- 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 -45
- 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 -14
- 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 -110
- package/dist/src/trails/add-surface.js.map +0 -1
- package/dist/src/trails/add-trail.d.ts +0 -12
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -104
- 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 -68
- 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 -295
- package/dist/src/trails/create-scaffold.js.map +0 -1
- package/dist/src/trails/create.d.ts +0 -18
- package/dist/src/trails/create.d.ts.map +0 -1
- package/dist/src/trails/create.js +0 -126
- 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 -66
- 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 -39
- 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 -181
- 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 -400
- 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 -61
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -12
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -415
- 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 -234
- 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 -19
- package/dist/src/trails/topo-export.d.ts.map +0 -1
- package/dist/src/trails/topo-export.js +0 -31
- package/dist/src/trails/topo-export.js.map +0 -1
- package/dist/src/trails/topo-history.d.ts +0 -20
- package/dist/src/trails/topo-history.d.ts.map +0 -1
- package/dist/src/trails/topo-history.js +0 -32
- package/dist/src/trails/topo-history.js.map +0 -1
- package/dist/src/trails/topo-pin.d.ts +0 -17
- package/dist/src/trails/topo-pin.d.ts.map +0 -1
- package/dist/src/trails/topo-pin.js +0 -31
- package/dist/src/trails/topo-pin.js.map +0 -1
- package/dist/src/trails/topo-read-support.d.ts +0 -58
- package/dist/src/trails/topo-read-support.d.ts.map +0 -1
- package/dist/src/trails/topo-read-support.js +0 -167
- package/dist/src/trails/topo-read-support.js.map +0 -1
- package/dist/src/trails/topo-reports.d.ts +0 -54
- package/dist/src/trails/topo-reports.d.ts.map +0 -1
- package/dist/src/trails/topo-reports.js +0 -128
- 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 -49
- 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 -76
- package/dist/src/trails/topo-support.d.ts.map +0 -1
- package/dist/src/trails/topo-support.js +0 -132
- package/dist/src/trails/topo-support.js.map +0 -1
- package/dist/src/trails/topo-unpin.d.ts +0 -20
- package/dist/src/trails/topo-unpin.d.ts.map +0 -1
- package/dist/src/trails/topo-unpin.js +0 -44
- 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 -24
- 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 -63
- package/dist/src/trails/topo.js.map +0 -1
- package/dist/src/trails/warden.d.ts +0 -20
- package/dist/src/trails/warden.d.ts.map +0 -1
- package/dist/src/trails/warden.js +0 -98
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/src/versions.d.ts +0 -12
- package/dist/src/versions.d.ts.map +0 -1
- package/dist/src/versions.js +0 -23
- package/dist/src/versions.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/add-trail.test.ts +0 -97
- package/src/__tests__/create.test.ts +0 -415
- package/src/__tests__/draft-promote.test.ts +0 -144
- package/src/__tests__/guide.test.ts +0 -96
- package/src/__tests__/load-app.test.ts +0 -419
- package/src/__tests__/survey.test.ts +0 -377
- package/src/__tests__/topo-dev.test.ts +0 -426
- package/src/__tests__/warden.test.ts +0 -74
- package/src/trails/topo-export.ts +0 -35
- package/src/trails/topo-show.ts +0 -54
- package/tsconfig.json +0 -9
- package/tsconfig.tests.json +0 -10
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
/* oxlint-disable max-statements */
|
|
2
|
-
|
|
3
|
-
import { describe, expect, test } from 'bun:test';
|
|
4
|
-
import {
|
|
5
|
-
existsSync,
|
|
6
|
-
mkdirSync,
|
|
7
|
-
readFileSync,
|
|
8
|
-
rmSync,
|
|
9
|
-
writeFileSync,
|
|
10
|
-
} from 'node:fs';
|
|
11
|
-
import { join, resolve } from 'node:path';
|
|
12
|
-
|
|
13
|
-
import type { Result } from '@ontrails/core';
|
|
14
|
-
import { openReadTrailsDb } from '@ontrails/core/internal/trails-db';
|
|
15
|
-
import { createDevStore } from '@ontrails/tracing';
|
|
16
|
-
|
|
17
|
-
import { devCleanTrail } from '../trails/dev-clean.js';
|
|
18
|
-
import { devResetTrail } from '../trails/dev-reset.js';
|
|
19
|
-
import { devStatsTrail } from '../trails/dev-stats.js';
|
|
20
|
-
import { guideTrail } from '../trails/guide.js';
|
|
21
|
-
import { surveyTrail } from '../trails/survey.js';
|
|
22
|
-
import { topoExportTrail } from '../trails/topo-export.js';
|
|
23
|
-
import { topoHistoryTrail } from '../trails/topo-history.js';
|
|
24
|
-
import { topoPinTrail } from '../trails/topo-pin.js';
|
|
25
|
-
import { topoShowTrail } from '../trails/topo-show.js';
|
|
26
|
-
import { topoTrail } from '../trails/topo.js';
|
|
27
|
-
import { topoUnpinTrail } from '../trails/topo-unpin.js';
|
|
28
|
-
import { topoVerifyTrail } from '../trails/topo-verify.js';
|
|
29
|
-
|
|
30
|
-
const repoTempDir = (): string =>
|
|
31
|
-
join(
|
|
32
|
-
resolve(import.meta.dir, '../..'),
|
|
33
|
-
'.tmp-tests',
|
|
34
|
-
`topo-dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const expectOk = <T>(result: Result<T, Error>): T => {
|
|
38
|
-
if (result.isErr()) {
|
|
39
|
-
throw result.error;
|
|
40
|
-
}
|
|
41
|
-
return result.value;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const expectErr = <E extends Error>(result: Result<unknown, E>): E => {
|
|
45
|
-
if (result.isOk()) {
|
|
46
|
-
throw new Error('expected result to be an error');
|
|
47
|
-
}
|
|
48
|
-
return result.error;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const moduleInput = { module: './src/app.ts' } as const;
|
|
52
|
-
|
|
53
|
-
const writeAppFixture = (dir: string): void => {
|
|
54
|
-
mkdirSync(join(dir, 'src'), { recursive: true });
|
|
55
|
-
writeFileSync(
|
|
56
|
-
join(dir, 'src', 'app.ts'),
|
|
57
|
-
`import { Result, resource, topo, trail } from '@ontrails/core';
|
|
58
|
-
import { z } from 'zod';
|
|
59
|
-
|
|
60
|
-
const hello = trail('hello', {
|
|
61
|
-
blaze: async (input) => Result.ok({ message: \`Hello, \${input.name ?? 'world'}!\` }),
|
|
62
|
-
crosses: ['goodbye'],
|
|
63
|
-
examples: [{ input: {}, name: 'Default greeting' }],
|
|
64
|
-
input: z.object({ name: z.string().optional() }),
|
|
65
|
-
intent: 'read',
|
|
66
|
-
output: z.object({ message: z.string() }),
|
|
67
|
-
resources: [
|
|
68
|
-
resource('db.main', {
|
|
69
|
-
create: () => Result.ok({ source: 'factory' }),
|
|
70
|
-
}),
|
|
71
|
-
],
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const goodbye = trail('goodbye', {
|
|
75
|
-
blaze: async () => Result.ok({ ok: true }),
|
|
76
|
-
input: z.object({}),
|
|
77
|
-
intent: 'write',
|
|
78
|
-
output: z.object({ ok: z.boolean() }),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const [dbMain] = hello.resources;
|
|
82
|
-
if (!dbMain) {
|
|
83
|
-
throw new Error('expected hello to declare db.main');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export const app = topo('fixture-app', { dbMain, goodbye, hello });
|
|
87
|
-
`
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
describe('topo and dev trails', () => {
|
|
92
|
-
test('topo surfaces current summary, detail, and export/verify flow', async () => {
|
|
93
|
-
const dir = repoTempDir();
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
writeAppFixture(dir);
|
|
97
|
-
|
|
98
|
-
const summary = expectOk(
|
|
99
|
-
await topoTrail.blaze(moduleInput, { cwd: dir } as never)
|
|
100
|
-
);
|
|
101
|
-
expect(summary.app.name).toBe('fixture-app');
|
|
102
|
-
expect(summary.list.count).toBe(2);
|
|
103
|
-
expect(summary.list.resourceCount).toBe(1);
|
|
104
|
-
expect(summary.lockExists).toBe(false);
|
|
105
|
-
|
|
106
|
-
const detail = expectOk(
|
|
107
|
-
await topoShowTrail.blaze({ ...moduleInput, id: 'hello' }, {
|
|
108
|
-
cwd: dir,
|
|
109
|
-
} as never)
|
|
110
|
-
);
|
|
111
|
-
expect(detail.id).toBe('hello');
|
|
112
|
-
expect(detail.resources).toEqual(['db.main']);
|
|
113
|
-
|
|
114
|
-
const exportResult = expectOk(
|
|
115
|
-
await topoExportTrail.blaze(moduleInput, { cwd: dir } as never)
|
|
116
|
-
);
|
|
117
|
-
expect(exportResult.hash).toHaveLength(64);
|
|
118
|
-
expect(existsSync(join(dir, '.trails', '_surface.json'))).toBe(true);
|
|
119
|
-
expect(existsSync(join(dir, '.trails', 'trails.lock'))).toBe(true);
|
|
120
|
-
expect(
|
|
121
|
-
JSON.parse(readFileSync(join(dir, '.trails', 'trails.lock'), 'utf8'))
|
|
122
|
-
).toMatchObject({
|
|
123
|
-
hash: exportResult.hash,
|
|
124
|
-
version: 1,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const summaryAfterExport = expectOk(
|
|
128
|
-
await topoTrail.blaze(moduleInput, { cwd: dir } as never)
|
|
129
|
-
);
|
|
130
|
-
expect(summaryAfterExport.lockExists).toBe(true);
|
|
131
|
-
|
|
132
|
-
const verifyResult = expectOk(
|
|
133
|
-
await topoVerifyTrail.blaze(moduleInput, { cwd: dir } as never)
|
|
134
|
-
);
|
|
135
|
-
expect(verifyResult.stale).toBe(false);
|
|
136
|
-
|
|
137
|
-
writeFileSync(join(dir, '.trails', 'trails.lock'), 'stale\n');
|
|
138
|
-
const verifyError = expectErr(
|
|
139
|
-
await topoVerifyTrail.blaze(moduleInput, { cwd: dir } as never)
|
|
140
|
-
);
|
|
141
|
-
expect(verifyError.message).toContain('trails.lock is stale');
|
|
142
|
-
} finally {
|
|
143
|
-
rmSync(dir, { force: true, recursive: true });
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('survey and guide read current topo state through the shared topo store', async () => {
|
|
148
|
-
const dir = repoTempDir();
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
writeAppFixture(dir);
|
|
152
|
-
|
|
153
|
-
const surveyList = expectOk(
|
|
154
|
-
await surveyTrail.blaze({ module: './src/app.ts' }, {
|
|
155
|
-
cwd: dir,
|
|
156
|
-
} as never)
|
|
157
|
-
);
|
|
158
|
-
expect(surveyList).toMatchObject({
|
|
159
|
-
count: 2,
|
|
160
|
-
resourceCount: 1,
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const surveyBrief = expectOk(
|
|
164
|
-
await surveyTrail.blaze({ brief: true, module: './src/app.ts' }, {
|
|
165
|
-
cwd: dir,
|
|
166
|
-
} as never)
|
|
167
|
-
);
|
|
168
|
-
expect(surveyBrief).toMatchObject({
|
|
169
|
-
features: {
|
|
170
|
-
examples: true,
|
|
171
|
-
outputSchemas: true,
|
|
172
|
-
resources: true,
|
|
173
|
-
},
|
|
174
|
-
name: 'fixture-app',
|
|
175
|
-
trails: 2,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const surveyDetail = expectOk(
|
|
179
|
-
await surveyTrail.blaze({ module: './src/app.ts', trailId: 'hello' }, {
|
|
180
|
-
cwd: dir,
|
|
181
|
-
} as never)
|
|
182
|
-
);
|
|
183
|
-
expect(surveyDetail).toMatchObject({
|
|
184
|
-
id: 'hello',
|
|
185
|
-
resources: ['db.main'],
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const guideList = expectOk(
|
|
189
|
-
await guideTrail.blaze({ module: './src/app.ts' }, {
|
|
190
|
-
cwd: dir,
|
|
191
|
-
} as never)
|
|
192
|
-
);
|
|
193
|
-
expect(guideList).toEqual([
|
|
194
|
-
{
|
|
195
|
-
description: '(no description)',
|
|
196
|
-
exampleCount: 0,
|
|
197
|
-
id: 'goodbye',
|
|
198
|
-
kind: 'trail',
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
description: '(no description)',
|
|
202
|
-
exampleCount: 1,
|
|
203
|
-
id: 'hello',
|
|
204
|
-
kind: 'trail',
|
|
205
|
-
},
|
|
206
|
-
]);
|
|
207
|
-
|
|
208
|
-
const guideDetail = expectOk(
|
|
209
|
-
await guideTrail.blaze({ module: './src/app.ts', trailId: 'hello' }, {
|
|
210
|
-
cwd: dir,
|
|
211
|
-
} as never)
|
|
212
|
-
);
|
|
213
|
-
expect(guideDetail).toMatchObject({
|
|
214
|
-
description: null,
|
|
215
|
-
examples: [
|
|
216
|
-
{
|
|
217
|
-
input: {},
|
|
218
|
-
name: 'Default greeting',
|
|
219
|
-
},
|
|
220
|
-
],
|
|
221
|
-
id: 'hello',
|
|
222
|
-
kind: 'trail',
|
|
223
|
-
});
|
|
224
|
-
} finally {
|
|
225
|
-
rmSync(dir, { force: true, recursive: true });
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
test('pinning, history, unpinning, and dev maintenance work against shared trails.db', async () => {
|
|
230
|
-
const dir = repoTempDir();
|
|
231
|
-
|
|
232
|
-
try {
|
|
233
|
-
writeAppFixture(dir);
|
|
234
|
-
|
|
235
|
-
const firstPin = expectOk(
|
|
236
|
-
await topoPinTrail.blaze({ ...moduleInput, name: 'before-auth' }, {
|
|
237
|
-
cwd: dir,
|
|
238
|
-
} as never)
|
|
239
|
-
);
|
|
240
|
-
expect(firstPin.snapshot.pinnedAs).toBe('before-auth');
|
|
241
|
-
|
|
242
|
-
const firstExport = expectOk(
|
|
243
|
-
await topoExportTrail.blaze(moduleInput, { cwd: dir } as never)
|
|
244
|
-
);
|
|
245
|
-
const secondExport = expectOk(
|
|
246
|
-
await topoExportTrail.blaze(moduleInput, { cwd: dir } as never)
|
|
247
|
-
);
|
|
248
|
-
expect(firstExport.hash).toBe(secondExport.hash);
|
|
249
|
-
expect(
|
|
250
|
-
JSON.parse(readFileSync(join(dir, '.trails', 'trails.lock'), 'utf8'))
|
|
251
|
-
).toMatchObject({
|
|
252
|
-
hash: secondExport.hash,
|
|
253
|
-
version: 1,
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
const projectionDb = openReadTrailsDb({ rootDir: dir });
|
|
257
|
-
try {
|
|
258
|
-
const pinnedRows = projectionDb
|
|
259
|
-
.query<{ count: number }, [string]>(
|
|
260
|
-
'SELECT COUNT(*) as count FROM topo_trails WHERE snapshot_id = ?'
|
|
261
|
-
)
|
|
262
|
-
.get(firstPin.snapshot.id);
|
|
263
|
-
const exportedRows = projectionDb
|
|
264
|
-
.query<{ count: number }, [string]>(
|
|
265
|
-
'SELECT COUNT(*) as count FROM topo_trails WHERE snapshot_id = ?'
|
|
266
|
-
)
|
|
267
|
-
.get(firstExport.snapshot.id);
|
|
268
|
-
const projectedSaves = projectionDb
|
|
269
|
-
.query<{ count: number }, []>(
|
|
270
|
-
'SELECT COUNT(DISTINCT snapshot_id) as count FROM topo_trails'
|
|
271
|
-
)
|
|
272
|
-
.get();
|
|
273
|
-
const cachedSchemas = projectionDb
|
|
274
|
-
.query<{ count: number }, []>(
|
|
275
|
-
'SELECT COUNT(*) as count FROM topo_schemas'
|
|
276
|
-
)
|
|
277
|
-
.get();
|
|
278
|
-
|
|
279
|
-
expect(pinnedRows?.count).toBe(2);
|
|
280
|
-
expect(exportedRows?.count).toBe(2);
|
|
281
|
-
expect(projectedSaves?.count).toBe(3);
|
|
282
|
-
expect(cachedSchemas?.count).toBeGreaterThanOrEqual(9);
|
|
283
|
-
} finally {
|
|
284
|
-
projectionDb.close();
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const store = createDevStore({ rootDir: dir });
|
|
288
|
-
try {
|
|
289
|
-
store.write({
|
|
290
|
-
attrs: {},
|
|
291
|
-
endedAt: Date.now() - 1000,
|
|
292
|
-
id: 'track-1',
|
|
293
|
-
kind: 'trail',
|
|
294
|
-
name: 'hello',
|
|
295
|
-
rootId: 'track-1',
|
|
296
|
-
startedAt: Date.now() - 10_000,
|
|
297
|
-
status: 'ok',
|
|
298
|
-
traceId: 'trace-1',
|
|
299
|
-
trailId: 'hello',
|
|
300
|
-
trailhead: 'cli',
|
|
301
|
-
});
|
|
302
|
-
store.write({
|
|
303
|
-
attrs: {},
|
|
304
|
-
endedAt: Date.now() - 500,
|
|
305
|
-
id: 'track-2',
|
|
306
|
-
kind: 'trail',
|
|
307
|
-
name: 'goodbye',
|
|
308
|
-
rootId: 'track-2',
|
|
309
|
-
startedAt: Date.now() - 20_000,
|
|
310
|
-
status: 'err',
|
|
311
|
-
traceId: 'trace-2',
|
|
312
|
-
trailId: 'goodbye',
|
|
313
|
-
trailhead: 'cli',
|
|
314
|
-
});
|
|
315
|
-
} finally {
|
|
316
|
-
store.close();
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const history = expectOk(
|
|
320
|
-
await topoHistoryTrail.blaze({}, { cwd: dir } as never)
|
|
321
|
-
);
|
|
322
|
-
expect(history.pinnedCount).toBe(1);
|
|
323
|
-
expect(history.snapshotCount).toBeGreaterThanOrEqual(3);
|
|
324
|
-
expect(
|
|
325
|
-
history.snapshots.some(
|
|
326
|
-
(snapshot) => snapshot.id === firstPin.snapshot.id
|
|
327
|
-
)
|
|
328
|
-
).toBe(true);
|
|
329
|
-
expect(
|
|
330
|
-
history.snapshots.some(
|
|
331
|
-
(snapshot) => snapshot.id === secondExport.snapshot.id
|
|
332
|
-
)
|
|
333
|
-
).toBe(true);
|
|
334
|
-
|
|
335
|
-
const stats = expectOk(
|
|
336
|
-
await devStatsTrail.blaze({}, { cwd: dir } as never)
|
|
337
|
-
);
|
|
338
|
-
expect(stats.topo.pinnedCount).toBe(1);
|
|
339
|
-
expect(stats.tracing.recordCount).toBe(2);
|
|
340
|
-
|
|
341
|
-
const cleanPreview = expectOk(
|
|
342
|
-
await devCleanTrail.blaze(
|
|
343
|
-
{ dryRun: true, snapshots: 0, traceAgeMs: 0 },
|
|
344
|
-
{
|
|
345
|
-
cwd: dir,
|
|
346
|
-
} as never
|
|
347
|
-
)
|
|
348
|
-
);
|
|
349
|
-
expect(cleanPreview.dryRun).toBe(true);
|
|
350
|
-
expect(cleanPreview.removed.topoSnapshots).toBeGreaterThanOrEqual(2);
|
|
351
|
-
expect(cleanPreview.removed.traceRecords).toBe(2);
|
|
352
|
-
|
|
353
|
-
const cleanResult = expectOk(
|
|
354
|
-
await devCleanTrail.blaze(
|
|
355
|
-
{ dryRun: false, snapshots: 0, traceAgeMs: 0, yes: true },
|
|
356
|
-
{ cwd: dir } as never
|
|
357
|
-
)
|
|
358
|
-
);
|
|
359
|
-
expect(cleanResult.removed.traceRecords).toBe(2);
|
|
360
|
-
expect(cleanResult.remaining.pinnedCount).toBe(1);
|
|
361
|
-
|
|
362
|
-
const unpinPreview = expectOk(
|
|
363
|
-
await topoUnpinTrail.blaze({ dryRun: true, name: 'before-auth' }, {
|
|
364
|
-
cwd: dir,
|
|
365
|
-
} as never)
|
|
366
|
-
);
|
|
367
|
-
expect(unpinPreview.dryRun).toBe(true);
|
|
368
|
-
expect(unpinPreview.snapshot?.pinnedAs).toBe('before-auth');
|
|
369
|
-
|
|
370
|
-
const unpinResult = expectOk(
|
|
371
|
-
await topoUnpinTrail.blaze(
|
|
372
|
-
{ dryRun: false, name: 'before-auth', yes: true },
|
|
373
|
-
{ cwd: dir } as never
|
|
374
|
-
)
|
|
375
|
-
);
|
|
376
|
-
expect(unpinResult.removed).toBe(true);
|
|
377
|
-
|
|
378
|
-
const resetPreview = expectOk(
|
|
379
|
-
await devResetTrail.blaze({ dryRun: true }, { cwd: dir } as never)
|
|
380
|
-
);
|
|
381
|
-
expect(resetPreview.dryRun).toBe(true);
|
|
382
|
-
expect(resetPreview.removedFiles).toContain('.trails/trails.db');
|
|
383
|
-
|
|
384
|
-
const resetResult = expectOk(
|
|
385
|
-
await devResetTrail.blaze({ dryRun: false, yes: true }, {
|
|
386
|
-
cwd: dir,
|
|
387
|
-
} as never)
|
|
388
|
-
);
|
|
389
|
-
expect(resetResult.removedFiles).toContain('.trails/trails.db');
|
|
390
|
-
expect(existsSync(join(dir, '.trails', 'trails.db'))).toBe(false);
|
|
391
|
-
expect(
|
|
392
|
-
readFileSync(join(dir, '.trails', 'trails.lock'), 'utf8').length
|
|
393
|
-
).toBeGreaterThan(0);
|
|
394
|
-
} finally {
|
|
395
|
-
rmSync(dir, { force: true, recursive: true });
|
|
396
|
-
}
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
test('dev clean stays side-effect free when no local state exists', async () => {
|
|
400
|
-
const dir = repoTempDir();
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
mkdirSync(dir, { recursive: true });
|
|
404
|
-
|
|
405
|
-
const preview = expectOk(
|
|
406
|
-
await devCleanTrail.blaze({ dryRun: true }, { cwd: dir } as never)
|
|
407
|
-
);
|
|
408
|
-
expect(preview.dryRun).toBe(true);
|
|
409
|
-
expect(preview.removed.topoSnapshots).toBe(0);
|
|
410
|
-
expect(preview.removed.traceRecords).toBe(0);
|
|
411
|
-
expect(existsSync(join(dir, '.trails', 'trails.db'))).toBe(false);
|
|
412
|
-
|
|
413
|
-
const applied = expectOk(
|
|
414
|
-
await devCleanTrail.blaze({ dryRun: false, yes: true }, {
|
|
415
|
-
cwd: dir,
|
|
416
|
-
} as never)
|
|
417
|
-
);
|
|
418
|
-
expect(applied.dryRun).toBe(false);
|
|
419
|
-
expect(applied.removed.topoSnapshots).toBe(0);
|
|
420
|
-
expect(applied.removed.traceRecords).toBe(0);
|
|
421
|
-
expect(existsSync(join(dir, '.trails', 'trails.db'))).toBe(false);
|
|
422
|
-
} finally {
|
|
423
|
-
rmSync(dir, { force: true, recursive: true });
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
|
|
6
|
-
import { formatWardenReport, runWarden } from '@ontrails/warden';
|
|
7
|
-
|
|
8
|
-
const makeTempDir = (): string => {
|
|
9
|
-
const dir = join(
|
|
10
|
-
tmpdir(),
|
|
11
|
-
`trails-warden-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
12
|
-
);
|
|
13
|
-
mkdirSync(dir, { recursive: true });
|
|
14
|
-
return dir;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
describe('trails warden', () => {
|
|
18
|
-
test('runs lint + drift checks and produces a report', async () => {
|
|
19
|
-
const dir = makeTempDir();
|
|
20
|
-
try {
|
|
21
|
-
writeFileSync(
|
|
22
|
-
join(dir, 'good.ts'),
|
|
23
|
-
`trail("hello", {
|
|
24
|
-
blaze: async (input, ctx) => {
|
|
25
|
-
return Result.ok({ message: "hi" });
|
|
26
|
-
}
|
|
27
|
-
})`
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
const report = await runWarden({ rootDir: dir });
|
|
31
|
-
expect(report.diagnostics).toBeDefined();
|
|
32
|
-
expect(typeof report.errorCount).toBe('number');
|
|
33
|
-
expect(typeof report.warnCount).toBe('number');
|
|
34
|
-
expect(typeof report.passed).toBe('boolean');
|
|
35
|
-
} finally {
|
|
36
|
-
rmSync(dir, { force: true, recursive: true });
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('lintOnly skips drift detection', async () => {
|
|
41
|
-
const dir = makeTempDir();
|
|
42
|
-
try {
|
|
43
|
-
writeFileSync(join(dir, 'empty.ts'), 'export {}');
|
|
44
|
-
const report = await runWarden({ lintOnly: true, rootDir: dir });
|
|
45
|
-
expect(report.drift).toBeNull();
|
|
46
|
-
} finally {
|
|
47
|
-
rmSync(dir, { force: true, recursive: true });
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('driftOnly skips lint rules', async () => {
|
|
52
|
-
const dir = makeTempDir();
|
|
53
|
-
try {
|
|
54
|
-
writeFileSync(
|
|
55
|
-
join(dir, 'bad.ts'),
|
|
56
|
-
`trail("x", {
|
|
57
|
-
blaze: async () => { throw new Error("boom"); }
|
|
58
|
-
})`
|
|
59
|
-
);
|
|
60
|
-
const report = await runWarden({ driftOnly: true, rootDir: dir });
|
|
61
|
-
expect(report.diagnostics.length).toBe(0);
|
|
62
|
-
expect(report.drift).not.toBeNull();
|
|
63
|
-
} finally {
|
|
64
|
-
rmSync(dir, { force: true, recursive: true });
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('formatWardenReport produces human-readable output', async () => {
|
|
69
|
-
const report = await runWarden({ rootDir: '/dev/null' });
|
|
70
|
-
const output = formatWardenReport(report);
|
|
71
|
-
expect(output).toContain('Warden Report');
|
|
72
|
-
expect(typeof output).toBe('string');
|
|
73
|
-
});
|
|
74
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { trail } from '@ontrails/core';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
import { loadApp } from './load-app.js';
|
|
5
|
-
import { exportCurrentTopo } from './topo-store-support.js';
|
|
6
|
-
import {
|
|
7
|
-
createIsolatedExampleInput,
|
|
8
|
-
topoSnapshotOutput,
|
|
9
|
-
} from './topo-support.js';
|
|
10
|
-
|
|
11
|
-
export const topoExportTrail = trail('topo.export', {
|
|
12
|
-
blaze: async (input, ctx) => {
|
|
13
|
-
const rootDir = input.rootDir ?? ctx.cwd ?? process.cwd();
|
|
14
|
-
const app = await loadApp(input.module, rootDir);
|
|
15
|
-
return exportCurrentTopo(app, { rootDir });
|
|
16
|
-
},
|
|
17
|
-
description: 'Export the current topo to .trails artifacts',
|
|
18
|
-
examples: [
|
|
19
|
-
{
|
|
20
|
-
input: createIsolatedExampleInput('topo-export'),
|
|
21
|
-
name: 'Write the current topo export',
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
input: z.object({
|
|
25
|
-
module: z.string().optional().describe('Path to the app module'),
|
|
26
|
-
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
27
|
-
}),
|
|
28
|
-
intent: 'write',
|
|
29
|
-
output: z.object({
|
|
30
|
-
hash: z.string(),
|
|
31
|
-
lockPath: z.string(),
|
|
32
|
-
mapPath: z.string(),
|
|
33
|
-
snapshot: topoSnapshotOutput,
|
|
34
|
-
}),
|
|
35
|
-
});
|
package/src/trails/topo-show.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { NotFoundError, Result, trail } from '@ontrails/core';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
import { loadApp } from './load-app.js';
|
|
5
|
-
import { buildCurrentTopoDetail } from './topo-read-support.js';
|
|
6
|
-
|
|
7
|
-
const trailDetailOutput = z.object({
|
|
8
|
-
crosses: z.array(z.string()),
|
|
9
|
-
description: z.unknown().nullable(),
|
|
10
|
-
detours: z.unknown().nullable(),
|
|
11
|
-
examples: z.array(z.unknown()),
|
|
12
|
-
id: z.string(),
|
|
13
|
-
intent: z.enum(['read', 'write', 'destroy']),
|
|
14
|
-
kind: z.string(),
|
|
15
|
-
resources: z.array(z.string()),
|
|
16
|
-
safety: z.string(),
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const resourceDetailOutput = z.object({
|
|
20
|
-
description: z.string().nullable(),
|
|
21
|
-
health: z.enum(['available', 'none']),
|
|
22
|
-
id: z.string(),
|
|
23
|
-
kind: z.literal('resource'),
|
|
24
|
-
lifetime: z.literal('singleton'),
|
|
25
|
-
usedBy: z.array(z.string()),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export const topoShowTrail = trail('topo.show', {
|
|
29
|
-
blaze: async (input, ctx) => {
|
|
30
|
-
const rootDir = input.rootDir ?? ctx.cwd ?? process.cwd();
|
|
31
|
-
const app = await loadApp(input.module, rootDir);
|
|
32
|
-
const detail = buildCurrentTopoDetail(app, input.id, { rootDir });
|
|
33
|
-
if (detail !== undefined) {
|
|
34
|
-
return Result.ok(detail);
|
|
35
|
-
}
|
|
36
|
-
return Result.err(
|
|
37
|
-
new NotFoundError(`Trail or resource not found: ${input.id}`)
|
|
38
|
-
);
|
|
39
|
-
},
|
|
40
|
-
description: 'Show detail for a current trail or resource',
|
|
41
|
-
examples: [
|
|
42
|
-
{
|
|
43
|
-
input: { id: 'topo' },
|
|
44
|
-
name: 'Show current trail detail',
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
input: z.object({
|
|
48
|
-
id: z.string().describe('Trail or resource ID to inspect'),
|
|
49
|
-
module: z.string().optional().describe('Path to the app module'),
|
|
50
|
-
rootDir: z.string().optional().describe('Workspace root directory'),
|
|
51
|
-
}),
|
|
52
|
-
intent: 'read',
|
|
53
|
-
output: z.union([trailDetailOutput, resourceDetailOutput]),
|
|
54
|
-
});
|
package/tsconfig.json
DELETED